Skip to content

feat(agent-core-v2): recover output-limit truncation while preserving queued prompts - #4298

Draft
7Sageer wants to merge 10 commits into
mainfrom
feat/output-limit-recovery
Draft

7Sageer wants to merge 10 commits into
mainfrom
feat/output-limit-recovery

Conversation

@7Sageer

@7Sageer 7Sageer commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Requirement or Bug

模型在推理阶段耗尽输出 token 上限时,带提醒重试当前 step;同时修复 loop error handler 重试时排队 prompt 被静默丢弃的问题(纳入原 #4321)。

Bug Reproduction Steps

  1. 注册一个 loop error handler,对 step 错误调用 retry() 并返回 true(生产中包括 full-compaction 的 context overflow 恢复)。
  2. 发送 prompt A,让它的第一个 step 失败;在 A 运行期间再排队 prompt B。
  3. A 重试后正常完成;修复前 B 可能未发出 LLM 请求,turn.result 永不 resolve,客户端也收不到错误。

loop.test.ts 覆盖 handler 立即重试和延迟 20ms 重试两种情况;outputLimitRecovery.test.ts 也覆盖 output-limit recovery 后的排队 prompt。缺少 queue 修复时,后者可复现超时。

Root Cause

推理阶段的 empty_response + truncated 原先会在 turn machine 内用同一请求原样重试,输入没有变化,容易反复耗尽输出额度。

另一个问题是 machine 与 loop 的 turn 生命周期不同:machine turn 在失败 step 后已经结束,loop turn 仍在等待 error handler。此时 machine 会自行 gate 并启动排队 prompt;后续 retry 的 reset 会中止这个 prompt,但它已经出队,waiter 可能永远无法 settle。

本 PR 分别通过带 reminder 的恢复策略,以及在 loop turn 活跃期间 hold 住排队 prompt,处理这两个根因。

Code Changes

1. 推理阶段截断不再在 turn machine 内原样重试

 isRetryableError(error)
   empty_response
-    finishReason !== 'filtered'
+    finishReason !== 'filtered' && finishReason !== 'truncated'

空响应判定不变:只有 think、或完全为空且 finishReason = truncated 的回复仍是 empty_response,只是 step 直接失败并交给 loop error handler。RETRYABLE_STATUS_CODES 改为 ReadonlySet 是 lint 修复,行为不变。

2. Agent 级 output-limit recovery

与 full-compaction 恢复 context overflow 使用同一个 loop error handler seam,loop 与 hook 契约均无改动。

outputLimitRecoveryService
  registerLoopErrorHandler
    match   APIEmptyResponseError 且 finishReason = truncated
    handle  已连续提醒 3 次  -> 抛 agent.max_tokens_exceeded,turn 失败,记录 exhausted
            否则             -> context 追加 reminder,retry(),计数 +1,记录 nudged
  onWillBeginStep  turn 的第一个 step -> 计数清零
  onDidFinishStep  任一 step 正常结束 -> 计数清零
 src/agent/
+├── outputLimitRecovery/
+│   ├── outputLimitRecovery.ts          # service id + injection variant
+│   └── outputLimitRecoveryService.ts   # loop error handler
 src/human/llm/requester/retry.ts         # truncated 不在 machine 内原样重试
 src/app/telemetry/events.ts
+    llm_output_limit_recovery            # action: nudged | exhausted

reminder 以 system reminder 注入:

Output token limit hit while you were still reasoning, before this response produced any text or tool call. That reasoning was not kept and is not part of this conversation, so there is nothing to resume. Continue from the conversation above: reason much more briefly and produce your answer or next tool call well within the output limit.

被截断的 think-only 消息不保留:未签名推理不会由 provider 回传,失败 step 中未签名的 think 按既有逻辑不写入历史。reminder 明确说明推理未保留,避免模型假设可以直接续想;未采用 assistant prefill 续写。

3. loop turn 活跃期间阻止排队 prompt 抢跑

 AgentMachineContext
+  queueHeld: boolean
 AgentEvent
+  input.hold_queue / input.release_queue

 idle.ready / idle.waiting
-  queue 非空 → gating
-  notifications 或 queue 非空 → running
+  queue 非空且未 held → gating
+  notifications 非空,或 queue 非空且未 held → running
 AgentLoopService
   beginActiveTurn
     this.active = active
+    engine.holdQueue()
   endTurn
     ...dispatch turn.ended / telemetry
+    if (this.active === undefined) engine.releaseQueue()
     turn.result.resolve(result)

release 放在 turn.ended 派发之后,保持上一 turn 的结束事件先于下一 turn 开始;条件判断防止旧 turn 的异步收尾释放新 turn 的 hold。steer / notify 不受 hold 影响。

Behavior Changes and Affected Users

Behavior Before After Who relies on the old behavior Escape hatch
loop turn 中只有 think、无正文、无工具调用且 finishReason = truncated 的回复 判为 empty_response,同一请求原样重试,用完 maxAttemptsPerStep 后 turn 失败 step 失败,追加 reminder 后重试;同一 turn 连续最多 3 次 所有客户端(TUI、print 模式、desktop 与 web、VS Code、ACP)的主 agent、plan 与 subagent turn;每次截断最多多出 3 次请求 无开关,需要 maintainer 确认;可调大 max_completion_tokens 或降低 thinking effort 减少截断
完全为空且 finishReason = truncated 的回复 同上,原样重试直到失败 同上,按推理阶段截断处理 同上 同上
3 次提醒后仍被截断 turn 以 empty_response 错误失败 turn 以 agent.max_tokens_exceeded 失败,提示降低 thinking effort 或调高输出上限 依赖 empty_response 错误类型的客户端 同上
subagent 推理阶段被截断且提醒用尽 以 empty_response 失败 以 reason=max_tokens 失败 调用 Agent tool 的主 agent 同上
截断发生在 loopControl.maxStepsPerTurn 的最后一步 turn 失败 重试步计入 maxSteps,以 loop.max_steps_exceeded 失败 无(上限保持为硬上限) 不需要
截断时已有延续当前 turn 的 steer / notify 不适用 reminder 与该消息一起进入重试请求 无 不需要
loop error handler(包括 context overflow 与 output-limit recovery)重试时排在后面的 prompt 可能静默丢失,result 永不 resolve 当前 turn 重试并结束后,排队 prompt 照常运行 所有会排队后续 prompt 的客户端与 SDK 调用方 不需要
loop turn 活跃期间 machine 自行启动排队 prompt machine turn 结束后可立刻 gate 并启动队首 不再启动,等 loop turn 结束 无;loop 本就在 gate 里等待当前 turn 结束 不需要
排队 prompt 的 prompt gate(onBeforeSubmitPrompt、UserPromptSubmit hook)执行时机 可能早于上一 turn 的 turn.ended 上一 turn 的 turn.ended 派发之后执行,次数不变 配置了 UserPromptSubmit hook 的用户 不需要
steer(排队 prompt 合并进当前 turn) 不变 不变:steer 走 notifications,不受 hold 影响 — —
无活跃 loop turn 时的排队、FIFO 与事件顺序 不变 不变 — —
外部 hook 推理阶段截断最终以 empty_response 失败 失败的 step 不触发 Stop;提醒用尽时只在 turn 结束后触发 StopFailure;features/externalHooks/ 无改动 配置了 Stop / StopFailure hook 的用户 不需要
transcript 中被截断的推理 流式显示后 step 以 error 结束,重试 不变:未签名的 think 不写入历史 无 不需要
compaction、catalog.generate、catalog.ping 遇到推理阶段截断 抛 / 返回 empty_response 不变:空响应判定未改,它们不经过 turn machine 的重试策略 无 不需要
reminder 第 1 句:Output token limit hit while still reasoning 无 告诉模型上一条回复在推理阶段被截断,没有产出正文或工具调用 触发推理阶段截断的 loop turn 不触发则不注入
reminder 第 2 句:That reasoning was not kept…nothing to resume 无 说明被截断的推理不会保留,避免模型假设能接着推理 同上 同上
reminder 第 3 句:reason much more briefly…within the output limit 无 引导模型缩短推理,直接回答或调用工具 同上 同上
带可见正文的截断 completed + truncated: true 不变 无 不需要
未截断的 think-only、完全空响应、被过滤的空响应 empty_response,按原策略重试 / 失败 不变 无 不需要
遥测 无 output-limit recovery 事件 新增 llm_output_limit_recovery(nudged / exhausted、turn、step、连续次数;model 来自既有上下文),不包含响应内容 遥测消费者 沿用现有遥测开关

契约文件:config-manifest.toml、wire-manifest.d.ts、state-manifest.d.ts、kap-server API 快照、node-sdk 入口、CLI、features/externalHooks/ 均无改动;loop 的 AfterStepContext 与 hook 契约无改动。queueHeld 是 machine 的运行时状态,不写入 journal。

受影响模块与测试覆盖:

  • retry.ts:turn.test.ts 覆盖推理阶段截断不在 turn machine 内原样重试,并以 empty_response + truncated 失败。
  • outputLimitRecoveryService.ts:outputLimitRecovery.test.ts 共 8 个集成用例,覆盖 reminder 重试、正文截断不处理、连续 3 次后失败、maxSteps、计数清零、notify 合并、排队 prompt 和取消。
  • human/agent/machine.ts、agent/loop/machine/engine.ts、agent/loop/loopService.ts:loop.test.ts 覆盖立即 / 延迟重试时的排队 prompt,断言共 3 次请求、重试请求只含原 prompt、下一 prompt 单独运行、prompt gate 只执行一次、两个 turn 都 completed。
  • 既有 FIFO 用例钉住上一 turn 结束先于下一 turn 开始;正文截断、未截断空响应与 subagent 的旧行为由 loop.test.ts、empty-response.test.ts 和 tool.test.ts 中未改动的用例锁定。

验证:

  • agent-core-v2 tsc --noEmit、tsdown build、check-no-comments 通过;改动文件 oxlint 0 errors。
  • output-limit recovery、loop、turn 三组相关测试:95 passed。
  • agent-core-v2 全量:7324 passed / 1 skipped / 2 failed。
    • fullCompaction.test.ts > names truncated compaction responses when retries are exhausted 单独重跑通过。
    • tool.test.ts > returns the spawned agent id when a foreground subagent times out 单独重跑仍超时;两个原 PR 均记录了该用例在干净 main 上同样失败。

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: issue must have a maintainer's /approve).
  • I have added tests that prove my feature works.
  • The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Copilot AI balanced review requested due to automatic review settings October 20, 2026 13:34
@changeset-bot

changeset-bot Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3f57d79

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@7Hanrui

7Hanrui commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T11:40:06.002508Z 3f57d79 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 989dffc7c3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

toolExecutor: this.toolExecutor,
toolInfos: () => this.toolRegistry.list(),
maxAttemptsPerStep: this.config.get<LoopControl>(LOOP_CONTROL_SECTION)?.maxAttemptsPerStep,
allowTruncatedThinkOnly: this.flags.enabled(OUTPUT_LIMIT_RECOVERY_FLAG_ID),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read the recovery flag at turn time

When an existing session's engine was attached while this flag was disabled, later enabling [experimental] output_limit_recovery through the watched configuration does not fully enable the feature: FlagService refreshes its value, and both the requester and recovery hook read the new value dynamically, but this machine option remains frozen at false. A truncated think-only response is therefore still classified as an empty response by the turn machine before the recovery hook can continue it. Pass a live flag lookup into the turn machine or refresh its option when configuration changes.

Useful? React with 👍 / 👎.

Comment on lines +74 to +77
const canResume =
part?.type === 'think' && part.encrypted !== undefined && part.encrypted.length > 0;
if (canResume) {
this.loop.notify({ bypassMaxSteps: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid assistant-prefill recovery for Anthropic

When an anthropic response is truncated after producing one signed thinking block, this branch queues an empty notification, so the next request history ends with the preserved assistant message and is lowered as an assistant prefill. Claude 4.6 and later reject requests whose final message is an assistant prefill with a 400 error, according to the Anthropic migration guidance, and this repository explicitly supports those models. The advertised signed-thinking recovery therefore fails for current Claude models; use the user-role continuation reminder for Anthropic or otherwise gate direct continuation by provider capability.

Useful? React with 👍 / 👎.

Comment on lines +42 to +43
this.recover(ctx);
await next();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer recovery accounting until the nudge is consumed

When a later onDidFinishStep hook cancels or stops the turn, this handler has already incremented the recovery count and emitted a resumed or nudged telemetry event. The loop subsequently drops the queued turn-scoped nudge, as demonstrated by the added cancellation test, so no continuation request occurs even though telemetry reports that recovery happened. Record the action from the notification's consumption callback, or suppress it when downstream hooks prevent the continuation.

Useful? React with 👍 / 👎.

@7Sageer 7Sageer changed the title feat(agent-core-v2): add experimental output-limit recovery feat(agent-core-v2): recover responses truncated by the output token limit Oct 21, 2026
@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 446ea81f1c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const canResume =
part?.type === 'think' && part.encrypted !== undefined && part.encrypted.length > 0;
if (canResume) {
this.loop.notify({ bypassMaxSteps: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor the configured per-turn step limit during recovery

When max_steps_per_turn or KIMI_LOOP_MAX_STEPS_PER_TURN is set and a response truncates at that limit, both recovery branches enqueue a nudge with bypassMaxSteps: true (also at line 87), allowing up to three additional LLM steps. This silently turns the documented maximum (docs/en/configuration/config-files.md:329) into a soft limit for long outputs—the added test even demonstrates two calls with a limit of one—so these nudges should respect the remaining step budget rather than bypassing it.

AGENTS.md reference: AGENTS.md:L130-L132

Useful? React with 👍 / 👎.

if (canResume) {
this.loop.notify({ bypassMaxSteps: true });
} else {
this.loop.notify({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Coordinate recovery reminders with in-flight compaction

When an unsigned or visible truncation occurs while automatic compaction has crossed its blocking threshold, this reminder is materialized before onWillBeginStep waits for the in-flight summary request. historySafeToCompact rejects appended injection messages (fullCompactionService.ts:914-920), causing that compaction to throw an AbortError; failMachineGate then cancels the turn instead of issuing the promised recovery request. Delay materializing the reminder until compaction completes, or restart compaction cleanly after adding it.

AGENTS.md reference: AGENTS.md:L102-L105

Useful? React with 👍 / 👎.

Comment on lines +63 to +65
ctx.failTurn = new ChatProviderError(
'The model did not complete the response after three output-limit recovery attempts. Increase max_completion_tokens or lower the thinking effort.',
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Suppress Stop hooks when recovery is exhausted

When the fourth truncation sets failTurn and the user has a Stop hook configured, the hook chain continues and agentExternalHooksService.ts:238-247 still fires the blockable Stop event because it does not inspect failTurn; after settlement, the same turn also fires StopFailure. A denying Stop hook additionally appends a continuation message whose nudge is then dropped by the failure, leaving stale hook text in context. Make downstream hooks recognize the pending failure, or skip the normal Stop path once exhaustion is selected.

AGENTS.md reference: AGENTS.md:L122-L124

Useful? React with 👍 / 👎.

@7Sageer 7Sageer changed the title feat(agent-core-v2): recover responses truncated by the output token limit feat(agent-core-v2): continue responses truncated by the output token limit Oct 21, 2026
@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4ea1900dd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

this.loop.notify({
message: {
role: 'user',
content: [{ type: 'text', text: wrapSystemReminder(OUTPUT_LIMIT_RECOVERY_REMINDER) }],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve recovered subagent summary prefixes

When a subagent's final summary truncates and the continuation request succeeds, this reminder explicitly makes the second assistant message contain only the suffix. runAgentTurn still computes its handoff with latestAssistantText (src/session/subagent/runAgentTurn.ts:73,169-174), which returns only that second message, so the parent loses the entire prefix and may receive text beginning mid-sentence. Preserve or concatenate the recovered assistant segments when producing the subagent summary.

Useful? React with 👍 / 👎.

Comment on lines +87 to +91
registerScopedService(
LifecycleScope.Agent,
IAgentOutputLimitRecoveryService,
AgentOutputLimitRecoveryService,
ScopeActivation.OnScopeCreated,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate the new recovery feature before activation

At this target hash, a repo-wide search finds no flag definition or IFlagService check for this feature, so this unconditional OnScopeCreated registration enables it for every agent. Any visible truncation now incurs up to three extra model calls and users who relied on immediate completed + truncated semantics have no opt-out. Add a default-off owning-domain experimental flag and gate recovery on it. This is fresh evidence distinct from the earlier live-refresh comment, which assumed that such a flag existed.

AGENTS.md reference: AGENTS.md:L64-L67

Useful? React with 👍 / 👎.

return;
}
this.consecutiveTruncations += 1;
this.loop.notify({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Let pending steering replace the recovery reminder

When a steerIfActive prompt arrives while a truncated response is streaming, it has already been converted into a live loop nudge (loopService.ts:1385-1449) when this hook runs. This unconditional second notify is appended after the user's steer, so the next request history ends with the recovery instruction to continue the old response rather than the user's new direction; it also creates an unnecessary continuation because the steer itself keeps the turn alive. Skip or coalesce recovery when a user steer or another continuation nudge is already pending.

Useful? React with 👍 / 👎.

@7Sageer 7Sageer changed the title feat(agent-core-v2): continue responses truncated by the output token limit feat(agent-core-v2): continue turns cut off by the output token limit while reasoning Oct 21, 2026
@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: cb1dbcfba0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 644af0c9b7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return;
}
const snapshot = this.loop.snapshot();
if (snapshot.hasPendingRequests) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Distinguish queued turns from continuation requests

When another prompt is submitted during an active turn without steerIfActive—as the kap-server prompt route does in packages/kap-server/src/routes/prompts.ts:344-351—it remains in the engine queue, so hasPendingRequests is true even though that prompt cannot continue the current turn. A think-only truncation therefore skips recovery and completes without a visible answer; only the unrelated queued prompt runs next, whereas the previous implementation retried the truncated request. Limit this suppression to pending nudges or requests that will actually continue the current turn.

AGENTS.md reference: AGENTS.md:L110-L112

Useful? React with 👍 / 👎.

@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9053e94a04

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

this.track(ctx, 'exhausted');
return;
}
this.loop.notify({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Drop recovery reminders when the step cap rejects them

When max_steps_per_turn is already exhausted, this nudge is drained by gate() before the max-step check, so its injection is appended to context and nudged telemetry is emitted even though no continuation request runs. The consumed nudge is not removed by endTurn(), leaving a stale “continue” reminder in history for the user's next turn; avoid enqueueing it without remaining budget, or only materialize/account for it after the gate accepts the step.

AGENTS.md reference: AGENTS.md:L110-L112

Useful? React with 👍 / 👎.

(part) => part.type === 'text' && part.text.trim().length > 0,
);
if (hasThink && !hasText && !hasToolCalls) {
if (isThinkOnlyMessage(message) && finish.finishReason !== 'truncated') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep truncated think-only model pings as failures

When a reasoning model consumes the ping's 512-token output budget before emitting pong, this exception makes ModelRequesterImpl return a successful finish containing only thought. ModelCatalog.ping() then returns ok: true with empty text and finishReason: 'truncated', and the kimi-inspect model catalog renders a green pong badge even though the connectivity probe produced no answer; the loop recovery hook cannot help because ping calls the requester directly. Limit this exception to agent turns or make ping reject truncated/no-text results.

AGENTS.md reference: AGENTS.md:L110-L112

Useful? React with 👍 / 👎.

@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 012f07e96e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

"@moonshot-ai/kimi-code": minor
---

Retry responses cut off by the output token limit while the model is still reasoning with a reminder, up to three times; when the reminders run out the turn now fails with an output-limit error instead of an empty-response error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Name the direct-request contract changes

For klient/SDK callers of global.catalog.generate and catalog.ping, this change does not perform the retry described here: a truncated think-only response changes from an empty_response failure to a successful truncated finish, and ping can change from ok: false to ok: true with empty text. The commit's behavior table acknowledges both changes, but this release note omits the failure semantics those users lose; include them so callers know they must handle truncated/empty results.

AGENTS.md reference: AGENTS.md:L114-L116

Useful? React with 👍 / 👎.

…ontinuation

Continue visible-text truncations from an after-step hook with a turn-scoped notify, and end the turn as truncated once three consecutive continuations are used. Think-only truncations stay on the empty-response retry path, so the requester, turn machine, loop, and state manifest are unchanged.
…inders within the step budget

Use a reminder that states the truncated reasoning was not kept, fail the
turn with agent.max_tokens_exceeded once three reminders are used, count
reminder steps against maxStepsPerTurn, record recoveries only when the
next step picks up the reminder, defer only to nudges that continue the
current turn, and skip the Stop hook for a turn that is set to fail.
…oop snapshot in output-limit recovery

Drop LoopSnapshot.hasLiveNudges: with an empty prompt queue, a pending
request can only be a notify or steer that continues the current turn.
@7Sageer
7Sageer force-pushed the feat/output-limit-recovery branch from 012f07e to 36e8f8b Compare October 21, 2026 12:55
@7Sageer
7Sageer changed the base branch from main to fix/loop-retry-queued-prompt October 21, 2026 12:56
@7Hanrui

7Hanrui commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36e8f8b6ed

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return false;
case 'empty_response':
return error.finishReason !== 'filtered';
return error.finishReason !== 'filtered' && error.finishReason !== 'truncated';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exempt output-limit errors from infinite retry

When KIMI_CODE_INFINITE_RETRY is enabled, this new non-retryable classification is never observed by the loop: AgentLLMRequesterService catches the truncated APIEmptyResponseError first and retries the identical request indefinitely, exempting only context overflow. A reasoning model that deterministically consumes its output budget therefore never reaches the reminder handler and can issue unbounded requests; route this error through the loop recovery just as context overflow is routed to compaction.

AGENTS.md reference: AGENTS.md:L126-L128

Useful? React with 👍 / 👎.

this._register(
loop.registerLoopErrorHandler({
id: 'outputLimitRecovery',
match: (ctx) => isOutputLimitBeforeAnswer(ctx.error),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict recovery to failures from the model step

When blocking automatic compaction exhausts its attempts on a think-only truncated summary, fullCompactionService.block() rethrows that same APIEmptyResponseError from onWillBeginStep. This error-only predicate cannot distinguish the gate failure from the agent's response, so it appends irrelevant answer-continuation reminders and eventually reports agent.max_tokens_exceeded, masking the compaction failure. Include the failure stage/source in the match so only the actual turn-model step is recovered.

AGENTS.md reference: AGENTS.md:L102-L105

Useful? React with 👍 / 👎.

@7Sageer 7Sageer changed the title feat(agent-core-v2): continue turns cut off by the output token limit while reasoning feat(agent-core-v2): recover output-limit truncation while preserving queued prompts Oct 26, 2026
@7Sageer
7Sageer changed the base branch from fix/loop-retry-queued-prompt to main October 26, 2026 11:35
@7Hanrui

7Hanrui commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 3f57d79e29

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants