TestPackDropsDanglingAssistantDespiteIDReuse: pairing is positional, not a global ID lookup. Local backends commonly reuse index-derived IDs ("call_0") across turns, so a later turn's answered call must not vouch for an earlier turn's aborted (dangling) assistant - that shape reaches the wire and 40
(t *testing.T)
| 331 | // turn's aborted (dangling) assistant - that shape reaches the wire and 400s |
| 332 | // every backend ("missing tool response") until /clear. |
| 333 | func TestPackDropsDanglingAssistantDespiteIDReuse(t *testing.T) { |
| 334 | history := []Message{ |
| 335 | {Role: RoleUser, Content: "task"}, |
| 336 | // Turn 1: aborted mid-tool (Ctrl+C); call_0 never answered. |
| 337 | {Role: RoleAssistant, ToolCalls: []ToolCall{{ID: "call_0", Name: "bash"}}}, |
| 338 | // Turn 2: the backend reuses call_0; this exchange is complete. |
| 339 | {Role: RoleUser, Content: "again"}, |
| 340 | {Role: RoleAssistant, ToolCalls: []ToolCall{{ID: "call_0", Name: "bash"}}}, |
| 341 | {Role: RoleTool, ToolCallID: "call_0", Content: "ok"}, |
| 342 | {Role: RoleAssistant, Content: "done"}, |
| 343 | } |
| 344 | r := Pack(history, 100000) |
| 345 | owners := 0 |
| 346 | for i, m := range r.Messages { |
| 347 | if m.Role != RoleAssistant || len(m.ToolCalls) == 0 { |
| 348 | continue |
| 349 | } |
| 350 | owners++ |
| 351 | if i+1 >= len(r.Messages) || r.Messages[i+1].Role != RoleTool { |
| 352 | t.Fatalf("turn-1 dangling assistant survived via the reused ID: %+v", r.Messages) |
| 353 | } |
| 354 | } |
| 355 | if owners != 1 { |
| 356 | t.Fatalf("exactly the answered assistant must survive, got %d owners: %+v", owners, r.Messages) |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | // TestPackRecoversNewestToolGroupPastTrailingNudge: the over-budget recovery |
| 361 | // for the newest tool exchange must fire even when a failure/runaway nudge was |