kimi-code/packages/agent-core/test/loop
Kai 4dd926b0ac
fix(agent-core): recover sessions bricked by orphan tool results (#1308)
* fix(agent-core): recover sessions bricked by orphan tool results

A stray `tool` message with no preceding assistant `tool_calls` permanently
bricked a session on OpenAI-compatible providers: every turn re-sent the same
malformed history and got a 400, and switching model/provider did not help.

Two independent gaps caused this:

- kosong did not recognize the OpenAI / DeepSeek / vLLM / Qwen phrasings of the
  tool-exchange structural 400 (`role 'tool' must be a response to a preceding
  message with 'tool_calls'` and the mirror `assistant message with 'tool_calls'
  must be followed by tool messages`), so the post-400 strict-resend fallback
  that drops the orphan never fired.

- The legacy-restore compaction path kept a verbatim tail
  `history.slice(compactedCount)`; when the cut landed inside a tool exchange the
  tail began with an orphan tool result whose assistant was summarized away. The
  normal projection does not repair a leading orphan, so the malformed history
  was baked in and re-sent every turn.

Recognize the additional phrasings so the strict resend un-bricks any session,
and trim leading tool results from the legacy-restore tail so the orphan is
never persisted in the first place.

* fix(agent-core): drop orphan tool results at the projection boundary

Rework the legacy-restore half of the fix based on review feedback: mutating
`_history` at restore time desyncs every consumer that models the history from
the wire records — the transcript reducer's fold length would overcount and
make MessageService skip unflushed live-tail messages.

Keep the restored history faithful to the wire records instead, and drop a
`tool` result whose call is nowhere in the history at the projection boundary,
on every request-building projection: the normal wire (`messages`), the
post-400 strict resend (`strictMessages`), and the compaction summarizer. An
orphan is wire-invalid on strict providers and useless to the model either
way, so it never reaches a provider — no longer relying on recognizing the
provider's 400 phrasing to recover. Fragment projections (e.g. token-estimating
a history slice) leave results untouched, since a matching call may
legitimately sit outside the slice.
2026-07-02 19:29:05 +08:00
..
fixtures feat: pursue a goal autonomously (#270) 2026-06-02 22:52:23 +08:00
abort.e2e.test.ts fix(agent-core): surface user interruptions to the model instead of a neutral abort (#236) 2026-05-30 09:53:21 +08:00
api-shape.e2e.test.ts fix: require AgentSwarm to run alone (#643) 2026-06-11 13:58:35 +08:00
error-paths.e2e.test.ts refactor(agent-core): slim llm request log line (#190) 2026-05-29 13:36:34 +08:00
events.e2e.test.ts feat: rework permission decision policies (#26) 2026-05-27 20:07:24 +08:00
hooks.e2e.test.ts fix: require AgentSwarm to run alone (#643) 2026-06-11 13:58:35 +08:00
retry.test.ts fix: classify OAuth token refresh errors by cause (#838) 2026-06-17 18:34:42 +08:00
streaming.e2e.test.ts Kimi For Coding 2026-05-22 15:54:50 +08:00
tool-args-parse.test.ts refactor(agent-core): align malformed tool args with schema validation (#1209) 2026-06-29 23:24:03 +08:00
tool-call.e2e.test.ts refactor(agent-core): align malformed tool args with schema validation (#1209) 2026-06-29 23:24:03 +08:00
tool-exchange-fallback.e2e.test.ts fix(agent-core): recover sessions bricked by orphan tool results (#1308) 2026-07-02 19:29:05 +08:00
tool-scheduler.test.ts feat: rework permission decision policies (#26) 2026-05-27 20:07:24 +08:00
transcript.e2e.test.ts Kimi For Coding 2026-05-22 15:54:50 +08:00
turn-lifecycle.e2e.test.ts feat: remove default per-turn step limit of 1000 (#186) 2026-05-29 13:11:45 +08:00