Repository navigation
Conversation
🦋 Changeset detectedLatest commit: 3f57d79 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
There was a problem hiding this comment.
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.
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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), |
There was a problem hiding this comment.
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 👍 / 👎.
| const canResume = | ||
| part?.type === 'think' && part.encrypted !== undefined && part.encrypted.length > 0; | ||
| if (canResume) { | ||
| this.loop.notify({ bypassMaxSteps: true }); |
There was a problem hiding this comment.
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 👍 / 👎.
| this.recover(ctx); | ||
| await next(); |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 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 }); |
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
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 👍 / 👎.
| 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.', | ||
| ); |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 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) }], |
There was a problem hiding this comment.
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 👍 / 👎.
| registerScopedService( | ||
| LifecycleScope.Agent, | ||
| IAgentOutputLimitRecoveryService, | ||
| AgentOutputLimitRecoveryService, | ||
| ScopeActivation.OnScopeCreated, |
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
|
@codex review |
There was a problem hiding this comment.
💡 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; |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 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({ |
There was a problem hiding this comment.
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') { |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 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. |
There was a problem hiding this comment.
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.
… off while reasoning
…d the step message in output-limit recovery
…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.
…ion through the loop error handler
012f07e to
36e8f8b
Compare
|
@codex review |
There was a problem hiding this comment.
💡 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'; |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
Requirement or Bug
模型在推理阶段耗尽输出 token 上限时,带提醒重试当前 step;同时修复 loop error handler 重试时排队 prompt 被静默丢弃的问题(纳入原 #4321)。
Bug Reproduction Steps
retry()并返回true(生产中包括 full-compaction 的 context overflow 恢复)。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 内原样重试
空响应判定不变:只有 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 契约均无改动。
reminder 以 system reminder 注入:
被截断的 think-only 消息不保留:未签名推理不会由 provider 回传,失败 step 中未签名的 think 按既有逻辑不写入历史。reminder 明确说明推理未保留,避免模型假设可以直接续想;未采用 assistant prefill 续写。
3. loop turn 活跃期间阻止排队 prompt 抢跑
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
finishReason = truncated的回复empty_response,同一请求原样重试,用完maxAttemptsPerStep后 turn 失败max_completion_tokens或降低 thinking effort 减少截断finishReason = truncated的回复empty_response错误失败agent.max_tokens_exceeded失败,提示降低 thinking effort 或调高输出上限empty_response错误类型的客户端empty_response失败reason=max_tokens失败loopControl.maxStepsPerTurn的最后一步loop.max_steps_exceeded失败result永不 resolvegate里等待当前 turn 结束onBeforeSubmitPrompt、UserPromptSubmit hook)执行时机turn.endedturn.ended派发之后执行,次数不变empty_response失败Stop;提醒用尽时只在 turn 结束后触发StopFailure;features/externalHooks/无改动catalog.generate、catalog.ping遇到推理阶段截断empty_responsecompleted + truncated: trueempty_response,按原策略重试 / 失败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。loop.test.ts、empty-response.test.ts和tool.test.ts中未改动的用例锁定。验证:
tsc --noEmit、tsdownbuild、check-no-comments通过;改动文件 oxlint 0 errors。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
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.