@tencent-ai/agent-harness 0.2.0-beta.2 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/CHANGELOG.md +26 -2
  2. package/cli/CHANGELOG.md +64 -0
  3. package/cli/dist-server/shared.cjs +379 -315
  4. package/cli/dist-server/types/node/bash-sandbox/bash-sandbox-protocol.d.ts +3 -0
  5. package/cli/dist-server/types/node/cli/cli-protocol.d.ts +3 -0
  6. package/cli/dist-server/types/node/cli/stream-json-protocol.d.ts +8 -1
  7. package/cli/dist-server/types/node/sensitive-content/sensitive-content-protocol.d.ts +7 -0
  8. package/cli/dist-server/types/node/server/boot.d.ts +7 -4
  9. package/{lib/components → cli/dist-server/types/node/server/formats}/acp/acp-error-model.d.ts +1 -1
  10. package/{lib/components → cli/dist-server/types/node/server/formats}/acp/acp-protocol.d.ts +2 -3
  11. package/cli/dist-server/types/node/server/formats/acp/index.d.ts +12 -0
  12. package/cli/dist-server/types/node/server/formats/acp/projection.d.ts +10 -0
  13. package/cli/dist-server/types/node/server/index.d.ts +1 -1
  14. package/cli/dist-server/types/node/server/sessions.d.ts +3 -2
  15. package/cli/dist-server/types/node/server/types.d.ts +59 -7
  16. package/cli/dist-server/types/node/tools/tool-protocol.d.ts +16 -2
  17. package/cli/dist-server/types/vendor/cbc-core/node/agent/agent-protocol.d.ts +31 -2
  18. package/cli/dist-server/types/vendor/cbc-core/node/agent/agent-purpose-protocol.d.ts +8 -4
  19. package/cli/dist-server/types/vendor/cbc-core/node/agent/agent-purpose-scope.d.ts +46 -0
  20. package/cli/dist-server/types/vendor/cbc-core/node/agent/agent-service.d.ts +26 -3
  21. package/cli/dist-server/types/vendor/cbc-core/node/agent/index.d.ts +3 -0
  22. package/cli/dist-server/types/vendor/cbc-core/node/agent/model-call-failure.d.ts +9 -0
  23. package/cli/dist-server/types/vendor/cbc-core/node/agent/model-step-sequence.d.ts +11 -0
  24. package/cli/dist-server/types/vendor/cbc-core/node/agent/runner-factory.d.ts +25 -9
  25. package/cli/dist-server/types/vendor/cbc-core/node/agent/tool-call-loop-guard-protocol.d.ts +1 -1
  26. package/cli/dist-server/types/vendor/cbc-core/node/agent/tool-call-loop-pattern.d.ts +9 -8
  27. package/cli/dist-server/types/vendor/cbc-core/node/context/compaction-lineage.d.ts +39 -0
  28. package/cli/dist-server/types/vendor/cbc-core/node/context/context-compact-summarizer.d.ts +2 -0
  29. package/cli/dist-server/types/vendor/cbc-core/node/context/context-protocol.d.ts +17 -0
  30. package/cli/dist-server/types/vendor/cbc-core/node/context/index.d.ts +1 -0
  31. package/cli/dist-server/types/vendor/cbc-core/node/context/pre-message-compact-strategy.d.ts +1 -2
  32. package/cli/dist-server/types/vendor/cbc-core/node/session/index.d.ts +1 -0
  33. package/cli/dist-server/types/vendor/cbc-core/node/session/interruption-service.d.ts +8 -0
  34. package/cli/dist-server/types/vendor/cbc-core/node/session/model-request-retry-phase.d.ts +16 -0
  35. package/cli/dist-server/types/vendor/cbc-core/node/session/model-response-interceptors.d.ts +15 -0
  36. package/cli/dist-server/types/vendor/cbc-core/node/session/session-manager-host-protocol.d.ts +3 -2
  37. package/cli/dist-server/types/vendor/cbc-core/node/session/session-manager.d.ts +7 -3
  38. package/cli/dist-server/types/vendor/cbc-core/node/session/session-protocol.d.ts +47 -5
  39. package/cli/dist-server/types/vendor/cbc-core/node/session/session-run-state-machine-protocol.d.ts +14 -1
  40. package/cli/dist-server/types/vendor/cbc-core/node/session/stream-timeout-retry-budget.d.ts +8 -1
  41. package/cli/dist-server/types/vendor/cbc-core/node/trace/turn-context.d.ts +28 -1
  42. package/cli/dist-server/types/vendor/cbc-core/node/utils/command-message-utils.d.ts +20 -0
  43. package/cli/dist-server/types/vendor/cbc-core/node/utils/history-utils.d.ts +4 -4
  44. package/cli/dist-server/types/vendor/cbc-core/node/utils/input-item-utils.d.ts +8 -0
  45. package/cli/dist-server/types/vendor/cbc-core/node/utils/token-utils.d.ts +3 -1
  46. package/cli/dist-server/types/vendor/cbc-shared/node/agent/openai-agents-types.d.ts +1 -0
  47. package/cli/dist-server/types/vendor/cbc-shared/node/checkpoint/checkpoint-types.d.ts +6 -0
  48. package/cli/dist-server/types/vendor/cbc-shared/node/model/model-errors.d.ts +30 -2
  49. package/cli/dist-server/types/workspace/agent-client-protocol/lib/common/index.d.ts +1 -0
  50. package/cli/dist-server/types/workspace/agent-client-protocol/lib/common/sandbox-rule-context.d.ts +38 -0
  51. package/cli/dist-server/types/workspace/authentication/lib/common/account-scoped-external-link-authentication-provider.d.ts +3 -3
  52. package/cli/dist-server/types/workspace/authentication/lib/common/authentication-manager.d.ts +6 -0
  53. package/cli/dist-server/types/workspace/authentication/lib/common/authentication-observation.d.ts +36 -0
  54. package/cli/dist-server/types/workspace/authentication/lib/common/authentication-protocol.d.ts +21 -0
  55. package/cli/dist-server/types/workspace/authentication/lib/common/external-link-authentication-provider.d.ts +20 -4
  56. package/cli/dist-server/types/workspace/authentication/lib/common/index.d.ts +1 -0
  57. package/cli/dist-server/types/workspace/cbc-checkpoint/lib/node/checkpoint/checkpoint-protocol.d.ts +2 -0
  58. package/cli/dist-server/types/workspace/cbc-checkpoint/lib/node/checkpoint/checkpoint-service.d.ts +1 -0
  59. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/agent-protocol.d.ts +31 -2
  60. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/agent-purpose-protocol.d.ts +8 -4
  61. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/agent-purpose-scope.d.ts +46 -0
  62. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/agent-service.d.ts +26 -3
  63. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/index.d.ts +3 -0
  64. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/model-call-failure.d.ts +9 -0
  65. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/model-step-sequence.d.ts +11 -0
  66. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/runner-factory.d.ts +25 -9
  67. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/tool-call-loop-guard-protocol.d.ts +1 -1
  68. package/cli/dist-server/types/workspace/cbc-core/lib/node/agent/tool-call-loop-pattern.d.ts +9 -8
  69. package/cli/dist-server/types/workspace/cbc-core/lib/node/context/compaction-lineage.d.ts +39 -0
  70. package/cli/dist-server/types/workspace/cbc-core/lib/node/context/context-compact-summarizer.d.ts +2 -0
  71. package/cli/dist-server/types/workspace/cbc-core/lib/node/context/context-protocol.d.ts +17 -0
  72. package/cli/dist-server/types/workspace/cbc-core/lib/node/context/index.d.ts +1 -0
  73. package/cli/dist-server/types/workspace/cbc-core/lib/node/context/pre-message-compact-strategy.d.ts +1 -2
  74. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/index.d.ts +1 -0
  75. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/interruption-service.d.ts +8 -0
  76. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/model-request-retry-phase.d.ts +16 -0
  77. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/model-response-interceptors.d.ts +15 -0
  78. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/session-manager-host-protocol.d.ts +3 -2
  79. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/session-manager.d.ts +7 -3
  80. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/session-protocol.d.ts +47 -5
  81. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/session-run-state-machine-protocol.d.ts +14 -1
  82. package/cli/dist-server/types/workspace/cbc-core/lib/node/session/stream-timeout-retry-budget.d.ts +8 -1
  83. package/cli/dist-server/types/workspace/cbc-core/lib/node/trace/turn-context.d.ts +28 -1
  84. package/cli/dist-server/types/workspace/cbc-core/lib/node/utils/command-message-utils.d.ts +20 -0
  85. package/cli/dist-server/types/workspace/cbc-core/lib/node/utils/history-utils.d.ts +4 -4
  86. package/cli/dist-server/types/workspace/cbc-core/lib/node/utils/input-item-utils.d.ts +8 -0
  87. package/cli/dist-server/types/workspace/cbc-core/lib/node/utils/token-utils.d.ts +3 -1
  88. package/cli/dist-server/types/workspace/cbc-shared/lib/node/agent/openai-agents-types.d.ts +1 -0
  89. package/cli/dist-server/types/workspace/cbc-shared/lib/node/checkpoint/checkpoint-types.d.ts +6 -0
  90. package/cli/dist-server/types/workspace/cbc-shared/lib/node/model/model-errors.d.ts +30 -2
  91. package/cli/dist-server/types/workspace/cbc-worktree/lib/node/worktree/worktree-service.d.ts +5 -0
  92. package/cli/dist-server/types/workspace/core/lib/common/protocol.d.ts +8 -1
  93. package/cli/dist-server/types/workspace/product/lib/common/index.d.ts +1 -0
  94. package/cli/dist-server/types/workspace/product/lib/common/tencent-docs-endpoint.d.ts +37 -0
  95. package/cli/dist-server/types/workspace/product/lib/common/types/config.d.ts +12 -0
  96. package/cli/dist-server/types/workspace/product/lib/common/types/features.d.ts +1 -9
  97. package/cli/dist-server/types/workspace/product/lib/common/types/product.d.ts +15 -0
  98. package/cli/dist-server/types/workspace/sandbox-cli/lib/common/center-protocol.generated.d.ts +5 -0
  99. package/cli/dist-server/types/workspace/sandbox-cli/lib/common/file-policy.d.ts +4 -1
  100. package/cli/dist-server/types/workspace/sandbox-cli/lib/common/http-policy.d.ts +20 -0
  101. package/cli/dist-server/types/workspace/sandbox-cli/lib/common/rule-config.d.ts +10 -0
  102. package/cli/dist-server/types/workspace/sandbox-cli/lib/node/sandbox-center-client.d.ts +3 -0
  103. package/cli/dist-server/types/workspace/telemetry/lib/common/event/types/craft.d.ts +43 -3
  104. package/cli/package.json +1 -1
  105. package/cli/product.cloudhosted.json +4 -4
  106. package/cli/product.internal.json +11 -7
  107. package/cli/product.ioa.json +32 -24
  108. package/cli/product.json +45 -17
  109. package/cli/product.selfhosted.json +2 -2
  110. package/cli/vendor/shim/brokered-bin/7z +675 -0
  111. package/cli/vendor/shim/brokered-bin/7za +675 -0
  112. package/cli/vendor/shim/brokered-bin/7zz +675 -0
  113. package/cli/vendor/shim/brokered-bin/rar +675 -0
  114. package/cli/vendor/shim/brokered-bin/tar +675 -0
  115. package/cli/vendor/shim/brokered-bin/zip +187 -0
  116. package/docs/in-process.md +26 -2
  117. package/docs/protocol.md +7 -1
  118. package/docs/server.md +2 -0
  119. package/lib/agent/v2.js +1 -1
  120. package/lib/application/services.js +1 -1
  121. package/lib/client/client.js +1 -1
  122. package/lib/client/mcp.d.ts +1 -1
  123. package/lib/client/mcp.js +1 -1
  124. package/lib/components/acp-mcp-transport.d.mts +84 -1
  125. package/lib/components/question-elicitation.d.mts +20 -1
  126. package/lib/components/{acp/acp-prompt-input.d.ts → session/prompt-input.d.ts} +1 -1
  127. package/lib/components/session-replay.d.mts +6 -7
  128. package/lib/components/session-replay.mjs +1 -1
  129. package/lib/effect-runtime/mcp-lifecycle.mjs +1 -1
  130. package/lib/harness/create-harness.js +1 -1
  131. package/lib/integration/agent-integration.d.ts +5 -0
  132. package/lib/runtime/private-runtime-role.js +1 -1
  133. package/lib/session/agent-backend.d.ts +3 -0
  134. package/lib/session/agent-backend.js +1 -1
  135. package/lib/session/config-defaults.d.ts +4 -0
  136. package/lib/session/config-defaults.js +1 -0
  137. package/lib/session/config-store.d.ts +1 -1
  138. package/lib/session/config-store.js +1 -1
  139. package/lib/session/replay.d.ts +2 -2
  140. package/lib/session/replay.js +1 -1
  141. package/lib/session/server-config.d.ts +11 -2
  142. package/lib/session/server-config.js +1 -1
  143. package/lib/session/server-events.d.ts +10 -8
  144. package/lib/session/server-events.js +1 -1
  145. package/lib/session/server-mcp.js +1 -1
  146. package/package.json +20 -19
  147. package/lib/components/acp/acp-client-channel.d.ts +0 -36
  148. package/lib/components/acp/acp-mcp-transport.d.ts +0 -84
  149. package/lib/components/acp/acp-question-elicitation.d.ts +0 -20
@@ -90,10 +90,10 @@ export declare class RunnerFactoryImpl implements RunnerFactory {
90
90
  /**
91
91
  * Mode B:把「立即发送」队列项注入正在运行的 turn(#73022)。
92
92
  *
93
- * 调用点在 `callModelInputFilter`(见 `create()`),即**每次模型请求之前**,
94
- * 且此时本轮工具已全部执行完、tool result 已回填。选这个边界是因为既不用
95
- * 打断在跑的工具,也不会破坏 `tool_call` 与 `tool_result` 的配对——Codex 与
96
- * Claude Code 同样选在这个位置。
93
+ * 调用点在 `callModelInputFilter`(见 `create()`),即**每次模型请求之前**。
94
+ * rich message / notification / drain prompt 仍在这里注入请求副本;真实 steer
95
+ * 由 `continuationInputProvider` 在 `prepareTurn` 前写入 SDK RunState,避免后续
96
+ * 工具 step 从权威状态重建请求时丢失。
97
97
  *
98
98
  * 注入内容已经进入本次模型请求,无需再为“曾经注入过”强制续圈。只有模型响应期间
99
99
  * 新到达、尚未被取出的消息,才由 `__codebuddySteerPending` 在收尾时实时判定是否
@@ -111,6 +111,22 @@ export declare class RunnerFactoryImpl implements RunnerFactory {
111
111
  * `create()` 注释)。payload 与授权同为调用局部值,天然不跨 run 共享。
112
112
  */
113
113
  private injectSendNowItems;
114
+ /**
115
+ * Drain steer only at the SDK continuation boundary, before it prepares the next model step.
116
+ *
117
+ * The returned model items are installed by the SDK into RunState.generatedItems before
118
+ * `prepareTurn`. This makes steer authoritative input for every later tool/model step instead
119
+ * of an ephemeral `callModelInputFilter` addition. The visible history stays the bare user
120
+ * message; it is admitted exactly once, by the stream consumer, in RunState order.
121
+ */
122
+ private takeSteerContinuationInput;
123
+ /**
124
+ * Visible-history + ACP admission of one drained steer group.
125
+ *
126
+ * Ordering is the caller's responsibility (continuation-history-queue.ts); this only performs
127
+ * the writes and must never throw back into the run.
128
+ */
129
+ private admitSteerToVisibleHistory;
114
130
  /**
115
131
  * #100404:落定来源一(rich queue sendNow)的消费。
116
132
  *
@@ -139,11 +155,11 @@ export declare class RunnerFactoryImpl implements RunnerFactory {
139
155
  * (`session/session-manager.ts` 的 `MODEL_STREAM_STARTED`),涉及跨模块反向布线与
140
156
  * 事件类型白名单依赖,另开 issue 跟进。
141
157
  *
142
- * **落史顺序反转(显式接受)**:`modelData.input` 的 push 顺序是来源一 → 二 → 三 → 四,
143
- * 但来源一的 `addHistory` 推迟到本方法内执行,来源二/三/四仍在 `injectSendNowItems` 里
144
- * 就地 addHistory。同一 model step 同时命中多个来源时,history 里队员消息会排在
145
- * steer / 通知之后,与模型当时看到的输入顺序相反。已确认下游按 role/type 消费、不依赖
146
- * 交错位置,故接受该反转,不做「四来源 historyItems 统一收敛」的重构。
158
+ * **落史顺序反转(显式接受)**:`modelData.input` 的 filter push 顺序是来源一 → 三 → 四,
159
+ * 但来源一的 `addHistory` 推迟到本方法内执行,来源三/四仍在 `injectSendNowItems` 里
160
+ * 就地 addHistory。同一 model step 同时命中多个来源时,history 里队员消息会排在通知
161
+ * 之后,与模型当时看到的输入顺序相反。steer 已在更早的 SDK continuation 边界同时进入
162
+ * RunState 与可见历史,不参与这段反转。下游按 role/type 消费、不依赖交错位置,故接受。
147
163
  *
148
164
  * at-most-once 由**作用域**保证,不再依赖「commit 前清暂存」这类补偿动作:`pending` 是
149
165
  * 单次 `callModelInputFilter` 调用的局部值,每次调用最多把它 commit 一次,也不可能被
@@ -27,7 +27,7 @@ export declare const TOOL_CALL_LOOP_STOPPED_MESSAGE = "Request aborted: user sto
27
27
  * - `ToolCallLoopDetector` 在**模型请求前**拦截,静默向模型注入停止消息,用户无感;
28
28
  * 它覆盖的是旧的三条规则(黑名单同参 4 次 / Read 同文件 40 次 / 非黑名单同参 40 次)。
29
29
  * - `ToolCallLoopGuard` 在**工具执行前**拦截,弹窗让用户自己决定继续还是停止;
30
- * 它覆盖新增的两条规则(同工具连续 40 次,Bash 单独放到 80 次 / 周期序列重复 10 轮)。
30
+ * 它覆盖新增的两条规则(同工具连续 40 次,Bash 与 Write 单独放到 80 次 / 周期序列重复 10 轮)。
31
31
  *
32
32
  * 两者刻意并存:旧规则的静默语义已被线上验证,不因新能力回归。
33
33
  */
@@ -5,11 +5,11 @@
5
5
  */
6
6
  import type { ToolCallLoopTriggerType } from '../../../../telemetry/lib/common/event/types/craft.js';
7
7
  export interface ToolCallLoopThresholds {
8
- /** 同一工具连续调用达到该次数即判定为循环。Bash 走单独阈值。 */
8
+ /** 同一工具连续调用达到该次数即判定为循环。Bash / Write 走单独阈值。 */
9
9
  consecutiveThreshold: number;
10
10
  /**
11
- * Bash 连续调用的单独阈值。长任务里模型会连续跑很多条不同命令,
12
- * 只看工具名时 40 次容易把正常推进误判成循环。
11
+ * Bash 与 Write 连续调用的单独阈值。长任务里模型会连续跑很多条命令、
12
+ * 或连续落很多个文件,只看工具名时 40 次容易把正常推进误判成循环。
13
13
  */
14
14
  bashConsecutiveThreshold: number;
15
15
  /** AAB 块需要连续重复的轮数。 */
@@ -22,8 +22,9 @@ export interface ToolCallLoopHit {
22
22
  /** 重复序列的周期长度;AAB 恒为 3,连续同工具恒为 1。 */
23
23
  patternLength: number;
24
24
  }
25
- /** 与 `ToolNames.BASH` 对齐。模式识别层不依赖 agent-protocol,避免把工具名枚举拉进来。 */
25
+ /** 与 `ToolNames.BASH` / `ToolNames.WRITE` 对齐。模式识别层不依赖 agent-protocol,避免把工具名枚举拉进来。 */
26
26
  export declare const BASH_LOOP_TOOL_NAME = "Bash";
27
+ export declare const WRITE_LOOP_TOOL_NAME = "Write";
27
28
  export declare const DEFAULT_TOOL_CALL_LOOP_THRESHOLDS: ToolCallLoopThresholds;
28
29
  /** AAB 块的长度。多工具循环只识别这一种形状,因此是常量而非可配阈值。 */
29
30
  export declare const AAB_PERIOD = 3;
@@ -43,7 +44,7 @@ export declare class ToolCallLoopTracker {
43
44
  * 记录一次即将执行的工具调用,返回本次是否命中循环。
44
45
  *
45
46
  * 命中优先级:先判连续同工具,再判 AAB 重复。两条规则的阈值默认不重叠
46
- * (普通工具 40 次、Bash 80 次 vs AAB 重复 10 轮),实践中不会互相抢占。
47
+ * (普通工具 40 次、Bash / Write 80 次 vs AAB 重复 10 轮),实践中不会互相抢占。
47
48
  */
48
49
  record(toolName: string): ToolCallLoopHit | undefined;
49
50
  /**
@@ -59,7 +60,7 @@ export declare class ToolCallLoopTracker {
59
60
  * - `ABC`(三者互不相同)通常是正常的多步工作流(读文件 → 改文件 → 跑测试,
60
61
  * 然后换下一个文件),按周期重复判它循环误伤面太大;
61
62
  * - `ABAB` 这类两工具交替同样不判,避免把翻页 / 分批处理误当循环;
62
- * - `AAA…` 属于连续同工具,由 consecutiveThreshold / bashConsecutiveThreshold 那条规则负责。
63
+ * - `AAA…` 属于连续同工具,由 consecutiveThreshold / bashConsecutiveThreshold(Bash、Write)那条规则负责。
63
64
  *
64
65
  * `X ≠ Y` 顺带保证了 AAB 一定是本原周期(唯一可能的更短整除周期是 1,
65
66
  * 那要求三个元素全等),所以不需要额外的本原性校验。
@@ -69,8 +70,8 @@ export declare function matchesRepeatedAabSequence(history: readonly string[], r
69
70
  * 读取阈值配置。环境变量只用于 QA 快速复现,生产不设置即走默认值。
70
71
  * AAB 的周期是形状定义的一部分,不开放配置。
71
72
  *
72
- * `CBC_LOOP_GUARD_CONSECUTIVE_THRESHOLD` 只覆盖非 Bash 工具。Bash 用
73
- * `CBC_LOOP_GUARD_BASH_CONSECUTIVE_THRESHOLD`,避免 QA 把通用阈值压低时
73
+ * `CBC_LOOP_GUARD_CONSECUTIVE_THRESHOLD` 只覆盖非 Bash / Write 工具。Bash 与 Write
74
+ * 共用 `CBC_LOOP_GUARD_BASH_CONSECUTIVE_THRESHOLD`,避免 QA 把通用阈值压低时
74
75
  * 把长任务放宽一并关掉。
75
76
  */
76
77
  export declare function resolveToolCallLoopThresholds(env?: NodeJS.ProcessEnv): ToolCallLoopThresholds;
@@ -0,0 +1,39 @@
1
+ import type { CompactionKind, CompactionTrigger } from '../../../../telemetry/lib/common/index.js';
2
+ import type { Session } from '../session/session-protocol.js';
3
+ export type { CompactionKind, CompactionTrigger };
4
+ export declare function compactionKindOf(trigger: CompactionTrigger): CompactionKind;
5
+ /** Compactions of a Session's context. Never resets, like `modelStepSeq`. */
6
+ export interface CompactionLineage {
7
+ /** Completed compactions. */
8
+ count: number;
9
+ /** Compaction the current context starts from. */
10
+ lastId?: string;
11
+ /** Trigger of the completed compaction the next agent-loop model call reports. */
12
+ pending?: CompactionTrigger;
13
+ }
14
+ type LineageSession = Pick<Session, 'id' | 'history' | 'compactionLineage' | 'meta'>;
15
+ /** Compaction lineage of `session`, restored from its history after resume. */
16
+ export declare function getCompactionLineage(session: LineageSession): CompactionLineage;
17
+ /**
18
+ * Numbers a compaction boundary item before it enters history and stamps the running
19
+ * compaction onto it; the next agent-loop model call reports it.
20
+ */
21
+ export declare function recordCompactionBoundary(session: LineageSession, providerData: Record<string, unknown>): void;
22
+ /** Compaction lineage of one model call (gateway headers, `chat_message_send`). */
23
+ export interface ModelCallCompaction {
24
+ /** Completed compactions of the Session when the call is sent. */
25
+ readonly count: number;
26
+ /** Compaction the call's context starts from. */
27
+ readonly id?: string;
28
+ /** Compaction calls: class of the running compaction (`X-Compaction`). */
29
+ readonly kind?: CompactionKind;
30
+ /** First agent-loop call after a compaction: its class (`X-Context-Compacted`) and trigger. */
31
+ readonly contextCompacted?: CompactionKind;
32
+ readonly trigger?: CompactionTrigger;
33
+ }
34
+ /**
35
+ * Compaction lineage for the model call in the current async chain. Only the first
36
+ * agent-loop call after a compaction reports it, like Claude Code's
37
+ * `consumePendingContextCompacted`.
38
+ */
39
+ export declare function resolveModelCallCompaction(session: LineageSession & Pick<Session, 'agentPurpose' | 'conversationRequestId'>): ModelCallCompaction;
@@ -16,6 +16,7 @@ import { CompactStrategy, type ContextCompactResult, ContextCompactSummarizeOpti
16
16
  * 不包含任何具体的压缩逻辑实现。
17
17
  */
18
18
  export declare class ContextCompactSummarizerImpl implements ContextCompactSummarizer {
19
+ private activeCompactions?;
19
20
  protected readonly productManager: ProductManager;
20
21
  protected readonly agentManager: AgentManager;
21
22
  protected readonly logger: Logger;
@@ -25,6 +26,7 @@ export declare class ContextCompactSummarizerImpl implements ContextCompactSumma
25
26
  protected readonly strategyProvider: Provider<CompactStrategy>;
26
27
  compactAndSummarize(session: Session, options?: ContextCompactSummarizeOptions): Promise<boolean>;
27
28
  compactWithResult(session: Session, options?: ContextCompactSummarizeOptions): Promise<ContextCompactResult>;
29
+ private compactWithResultOwned;
28
30
  /**
29
31
  * 按优先级匹配(PreMessage → MaxToken),跳过 Blocking(仅作最终兜底),执行第一个命中的策略;
30
32
  * 都不命中则安全网回落到 Blocking。逻辑与压缩 span 化之前完全一致,只是把散落的 `return` 收敛成
@@ -1,7 +1,10 @@
1
+ import type { AgentInputItem } from '../../../../cbc-shared/lib/node/index.js';
2
+ import type { CompactionTrigger } from '../../../../telemetry/lib/common/index.js';
1
3
  import { Session } from '../session/session-protocol.js';
2
4
  export declare const PromptContextProvider: unique symbol;
3
5
  export declare const PromptContextInitializer: unique symbol;
4
6
  export declare const ContextCompactSummarizer: unique symbol;
7
+ export declare const ManualCompactRequestHandler: unique symbol;
5
8
  export declare const PromptRenderer: unique symbol;
6
9
  export declare const COMPACT_PROMPT = "compact-prompt";
7
10
  export declare const DEFAULT_TOKEN_THRESHOLD = 0.7;
@@ -190,6 +193,8 @@ export interface ContextCompactSummarizeOptions {
190
193
  type?: CompactType;
191
194
  /** Stable identity shared by live updates and the durable compact boundary. */
192
195
  compactionId?: string;
196
+ /** Why this compaction runs; defaults from `type` (manual / pre_message_threshold / emergency_threshold). */
197
+ trigger?: CompactionTrigger;
193
198
  }
194
199
  export type ContextCompactSkipReason = 'already_compacting' | 'pending_interruption' | 'auto_disabled' | 'empty_history' | 'already_compacted' | 'non_default_agent' | 'below_threshold';
195
200
  export type ContextCompactFailureReason = 'strategy_failed' | 'strategy_unavailable';
@@ -214,6 +219,8 @@ export declare function createCompactionId(): string;
214
219
  export declare const SESSION_META_COMPACT_TYPE: 'compactType';
215
220
  /** Current stable compaction identity, persisted onto the boundary item. */
216
221
  export declare const SESSION_META_COMPACTION_ID: 'compactionId';
222
+ /** Trigger of the compaction the summarizer is running; unset for a typed `/compact`. */
223
+ export declare const SESSION_META_COMPACTION_TRIGGER: 'compactionTrigger';
217
224
  /**
218
225
  * session.meta 上标记当前 compact 成功后会继续执行一次真实 agent run。
219
226
  *
@@ -321,6 +328,16 @@ export interface ContextCompactSummarizer {
321
328
  /** Compatibility wrapper for existing automatic-compaction callers. */
322
329
  compactAndSummarize(session: Session, options?: ContextCompactSummarizeOptions): Promise<boolean>;
323
330
  }
331
+ /**
332
+ * Host-owned boundary for external `/compact` requests.
333
+ *
334
+ * AgentService invokes this before creating a normal run lifecycle when an
335
+ * interactive host opts in. Implementations must return false for internal compact
336
+ * inputs so strategy-owned internal runs cannot recursively re-enter this boundary.
337
+ */
338
+ export interface ManualCompactRequestHandler {
339
+ tryHandle(input: string | AgentInputItem[], session: Session): Promise<boolean>;
340
+ }
324
341
  export interface PromptRenderer {
325
342
  render(template: string, context?: PromptContext): Promise<string>;
326
343
  }
@@ -1,4 +1,5 @@
1
1
  export * from './context-protocol.js';
2
+ export * from './compaction-lineage.js';
2
3
  export * from './context-host-protocol.js';
3
4
  export * from './prompt-context-provider.js';
4
5
  export * from './prompt-renderer.js';
@@ -4,8 +4,7 @@ import { CompactStrategyContext, ContextCompactSummarizeOptions } from './contex
4
4
  /**
5
5
  * PreMessage 压缩策略:在发送用户消息前触发。
6
6
  *
7
- * 触发条件:PRE_MESSAGE_AUTO 或 WorkBuddy 的 USER_COMMAND。
8
- * CBC 手动 /compact 不走该策略,继续由 compact agent 路径处理。
7
+ * 触发条件:PRE_MESSAGE_AUTO 或 USER_COMMAND(WorkBuddy / TUI 手动压缩)。
9
8
  *
10
9
  * 流程:
11
10
  * 1. Engineering compact (no LLM call)
@@ -36,6 +36,7 @@ export * from './stream-timeout-retry-budget.js';
36
36
  export { consumeRecoveryRequestPending } from './recovery-budget.js';
37
37
  export * from './session-run-state-machine-protocol.js';
38
38
  export * from './session-run-state-machine.js';
39
+ export * from './model-request-retry-phase.js';
39
40
  export * from './session-run-state-projection-service.js';
40
41
  export * from './local-jsonl-session-store.js';
41
42
  export * from './transcript-branch.js';
@@ -93,6 +93,13 @@ export declare class InterruptionServiceImpl implements InterruptionService {
93
93
  protected readonly idleQueueDrainService?: IIdleQueueDrainService;
94
94
  protected readonly traceCollector?: SessionInterruptionTraceHost;
95
95
  protected get startupProfiler(): SessionInterruptionProfilerHost;
96
+ /**
97
+ * 标记「用户取消本轮」的拒绝项(`reject(item, { cancelTurn: true })`)。
98
+ * 语义是取消当前 turn,而不是「拒绝并让模型带反馈续跑」。
99
+ * 记在 WeakSet 而不是 `rawItem.providerData`(那会落进历史),也不改
100
+ * deferred 的 reject 形状(下游按 error 类型分支的逻辑保持不变)。
101
+ */
102
+ private readonly cancelTurnItems;
96
103
  /** Claim before calling out: sandbox callbacks can throw or synchronously re-enter. */
97
104
  private takeSandboxResolve;
98
105
  approve(item: RunToolApprovalItem, options?: {
@@ -101,6 +108,7 @@ export declare class InterruptionServiceImpl implements InterruptionService {
101
108
  }): void;
102
109
  reject(item: RunToolApprovalItem, options?: {
103
110
  decision?: 'deny' | 'rejectAndExitPlan';
111
+ cancelTurn?: boolean;
104
112
  }): void;
105
113
  /**
106
114
  * 取消指定 session 上所有未决的 interruption Deferred。
@@ -0,0 +1,16 @@
1
+ import type { AgentRetryReason, Session } from './session-protocol.js';
2
+ import { ModelRequestRetryPhase, SessionRunStateMachine } from './session-run-state-machine-protocol.js';
3
+ export declare class ModelRequestRetryPhaseImpl implements ModelRequestRetryPhase {
4
+ protected readonly stateMachine: SessionRunStateMachine;
5
+ during<T>(session: Session | undefined, reason: AgentRetryReason, wait: () => Promise<T>): Promise<T>;
6
+ /**
7
+ * 辅助型调用复用主 session 却不回送 AGENT_ENDED(#52320),不得驱动其状态;
8
+ * 状态已离开 MODEL_REQUESTING 说明另有路径在推进,此时不抢写。
9
+ */
10
+ private enter;
11
+ /**
12
+ * 必须回到 MODEL_REQUESTING:RETRYING 不接收 TOOL_STARTED / MODEL_RESPONSE_DONE 等后续事件,
13
+ * 停留会让错误处理路径的转换被静默忽略。已被取消等事件推走时不再改写。
14
+ */
15
+ private leave;
16
+ }
@@ -215,6 +215,21 @@ export declare class MediaContentErrorInterceptor implements ModelResponseInterc
215
215
  private static readonly MAX_RETRY_ATTEMPTS;
216
216
  private static readonly MAX_MIME_MISMATCH_RETRY_ATTEMPTS;
217
217
  private static readonly GIF_UNSUPPORTED_ERROR_CODES;
218
+ /**
219
+ * Gateway business codes that mean "the model refused the images in this
220
+ * request" (`11135 InvalidInputImage`).
221
+ *
222
+ * The code is the only stable signal. The gateway folds several unrelated
223
+ * upstream causes into one hard-coded English sentence and overwrites the
224
+ * upstream text, so `ERROR_TYPE_PATTERNS` matching that sentence breaks the
225
+ * moment the wording is changed or localized (`docs/error-codes.md` already
226
+ * ships a Chinese user-facing string for this code). The code is carried on
227
+ * the error object all the way here — `error-analyzer` puts the parsed body
228
+ * code into the response, the SDK surfaces it as `APIError.error.code`, and
229
+ * `ErrorUtils.extractBizCode` recovers it — so key off it and keep the
230
+ * phrasing match only as a fallback for gateways that send no code.
231
+ */
232
+ private static readonly IMAGE_REJECTED_ERROR_CODES;
218
233
  private static readonly GIF_UNSUPPORTED_PLACEHOLDER;
219
234
  private static readonly ERROR_TYPE_PATTERNS;
220
235
  intercept(ctx: ModelResponseInterceptorContext): Promise<boolean | void>;
@@ -1,5 +1,5 @@
1
1
  import type { AgentInputItem } from '../../../../cbc-shared/lib/node/index.js';
2
- import type { ModelResponseMetadata, Session } from './session-protocol.js';
2
+ import type { ModelCallGeneration, ModelResponseMetadata, Session } from './session-protocol.js';
3
3
  export declare const SessionCheckpointHost: unique symbol;
4
4
  export interface SessionCheckpointHost {
5
5
  createCheckpoint(session: Session): Promise<void>;
@@ -57,7 +57,8 @@ export declare const SessionResponseTelemetryHost: unique symbol;
57
57
  export interface SessionResponseTelemetryHost {
58
58
  reportChatRequestResponseTelemetry(session: Session, result: NonNullable<Session['resultSubject']['value']>, isSuccessful: boolean, error: unknown, metadata: ModelResponseMetadata): Promise<void>;
59
59
  reportMessageResponseTelemetryStream(session: Session, usage: SessionResponseUsage | undefined, metadata: ModelResponseMetadata, error: unknown): Promise<void>;
60
- reportChatMessageStatus(session: Session, errorCode: string, error?: unknown): Promise<void>;
60
+ /** Status of one model call (`generation`); run failures no model call owns are not reported. */
61
+ reportChatMessageStatus(session: Session, errorCode: string, generation: ModelCallGeneration, error?: unknown): Promise<void>;
61
62
  }
62
63
  export type SessionResponseTelemetrySnapshot = Pick<NonNullable<Session['telemetry']>, 'modelId' | 'modelName' | 'currentTurn' | 'traceId'>;
63
64
  export declare const SessionResponseObserver: unique symbol;
@@ -4,7 +4,7 @@ import { Logger } from '../../../../telemetry/lib/common/index.js';
4
4
  import { BehaviorSubject } from 'rxjs';
5
5
  import { SessionRequestContextHost, SessionSettingsHost as SettingsManager, SessionUsageHost as UserStateManager } from './session-host-protocol.js';
6
6
  import { SessionBackgroundTaskHost as ToolManager, SessionCheckpointHost as CheckpointService, SessionProgressHost as ProgressSyncService, SessionResponseObserver as GalileoSessionBridge, SessionResponseTelemetryHost as TelemetryService, SessionSchedulerHost as CronSchedulerService, SessionSensitiveProjectionHost, SessionTaskLifecycleHost as AgentTaskManager } from './session-manager-host-protocol.js';
7
- import { AgentInputItemProcessor, InterruptionService, ModelResponseInterceptor, ModelResponseMetadata, Session, SessionFactory, SessionGetOptions, SessionHookManager, SessionManager, SessionStore } from './session-protocol.js';
7
+ import { AgentInputItemProcessor, InterruptionService, ModelResponseInterceptor, ModelResponseMetadata, OverrideModelSource, Session, SessionFactory, SessionGetOptions, SessionHookManager, SessionManager, SessionStore } from './session-protocol.js';
8
8
  import { SessionRunStateMachine } from './session-run-state-machine-protocol.js';
9
9
  export declare class SessionManagerImpl implements SessionManager, ApplicationLifecycle<Application> {
10
10
  protected readonly sessionFactory: SessionFactory;
@@ -44,6 +44,8 @@ export declare class SessionManagerImpl implements SessionManager, ApplicationLi
44
44
  private readonly liveSessionsById;
45
45
  private overridePermissionModes;
46
46
  private overrideModels;
47
+ /** 与 overrideModels 同生命周期的意图来源(user / declared / materialized)。 */
48
+ private overrideModelSources;
47
49
  /**
48
50
  * Issue #12554:会话级 thinking override(按 sessionId 隔离)。
49
51
  * 由 ACP setSessionConfigOption('thought_level') 或 prompt _meta 写入。
@@ -190,9 +192,11 @@ export declare class SessionManagerImpl implements SessionManager, ApplicationLi
190
192
  rename(session: Session, name: string): Promise<void>;
191
193
  setOverridePermissionMode(sessionId: string, mode: PermissionMode): void;
192
194
  getOverridePermissionMode(sessionId: string): PermissionMode | undefined;
193
- setOverrideModel(sessionId: string, model: string): void;
195
+ setOverrideModel(sessionId: string, model: string, source?: OverrideModelSource): void;
194
196
  getOverrideModel(sessionId: string): string | undefined;
195
- setRuntimeModel(session: Session | undefined, modelId: string): void;
197
+ getOverrideModelSource(sessionId: string): OverrideModelSource | undefined;
198
+ clearOverrideModel(sessionId: string): void;
199
+ setRuntimeModel(session: Session | undefined, modelId: string, source?: OverrideModelSource): void;
196
200
  setRuntimeOutputStyle(session: Session | undefined, style: string): void;
197
201
  /**
198
202
  * Issue #83229:把 settings.json 中的 model / outputStyle 读一次"钉"进 session.options,
@@ -2,6 +2,7 @@ import { Deferred } from '@celljs/core';
2
2
  import { type AgentInputItem, ApprovalOptions, type Checkpoint, FinalStopReason, NotificationType, PermissionMode, ReasoningEffortLevel, type ResponseStreamEvent, type RunItem, type RunResult, type RunState, type RunToolApprovalItem, type SdkCapabilities, SessionEndReason, SessionStartSource, type StreamedRunResult, type TerminalEvent, type ToolDeferOverlaySnapshot, ToolPermissionContext, type Usage } from '../../../../cbc-shared/lib/node/index.js';
3
3
  import { Command } from '../../../../product/lib/common/index.js';
4
4
  import { BehaviorSubject, Subject } from 'rxjs';
5
+ import type { CompactionLineage } from '../context/compaction-lineage.js';
5
6
  import { BackgroundTask } from './background-task-protocol.js';
6
7
  import type { SessionRunStateSnapshot } from './session-run-state-machine-protocol.js';
7
8
  import type { Lane, LanePointer, SessionKey, SessionStoreEntry } from './session-store-contract.js';
@@ -86,6 +87,7 @@ export type AgentState = 'agent_pending' | 'agent_start' | 'agent_tool_start' |
86
87
  * 则 emit,触发 UI 显示"正在同步文件"提示)
87
88
  */
88
89
  export type AgentPhase = 'preparing' | 'model_requesting' | 'model_streaming' | 'model_done' | 'tool_executing' | 'waiting_for_permission' | 'waiting_for_user' | 'retrying' | 'finalization_file_projection_sync' | 'finalization_file_projection_sync_notify' | 'idle';
90
+ export type AgentRetryReason = 'network';
89
91
  export interface AgentPhaseInfo {
90
92
  phase: AgentPhase;
91
93
  toolName?: string;
@@ -102,6 +104,13 @@ export interface AgentPhaseInfo {
102
104
  current: number;
103
105
  total: number;
104
106
  };
107
+ /**
108
+ * 仅 retrying phase 使用,经 ACP `_meta['codebuddy.ai/agentPhase']` 透传给上层。
109
+ * 缺省表示原因未分类(如流超时),不代表网络正常。
110
+ */
111
+ retry?: {
112
+ reason: AgentRetryReason;
113
+ };
105
114
  }
106
115
  export declare const AgentPhaseUtils: {
107
116
  /** Create an AgentPhaseInfo with the current timestamp. */
@@ -353,6 +362,14 @@ export interface Session {
353
362
  * field and pass it through as `options.maxTurns` explicitly.
354
363
  */
355
364
  lastRunMaxTurns?: number;
365
+ /**
366
+ * Latest agent-loop model call number (`providerData.stepSeq`). Unlike the
367
+ * SDK turn counter it survives runs, compaction and resume; see
368
+ * `allocateModelStepSeq`.
369
+ */
370
+ modelStepSeq?: number;
371
+ /** Compactions of this Session's context; see `getCompactionLineage`. */
372
+ compactionLineage?: CompactionLineage;
356
373
  queue: AgentInputItem[];
357
374
  queueSubject: BehaviorSubject<AgentInputItem[]>;
358
375
  /**
@@ -860,7 +877,7 @@ export interface SubagentInvocationRecord {
860
877
  lifecycle: SubagentLifecycleSnapshot;
861
878
  }
862
879
  export type SubagentLifecycleDescriptor = Omit<SubagentLifecycleSnapshot, 'status' | 'updatedAt' | 'usage'>;
863
- export type CombineEvent = (AgentInputItemEvent | ModelRawResponseEvent | ResultEndEvent | InterruptResultEvent | NoticeEvent | SubagentStatusEvent | CompactionLifecycleEvent | TerminalEvent) & CombineEventMeta;
880
+ export type CombineEvent = (AgentInputItemEvent | AgentSdkStreamEvent | ModelRawResponseEvent | ResultEndEvent | InterruptResultEvent | NoticeEvent | SubagentStatusEvent | CompactionLifecycleEvent | TerminalEvent) & CombineEventMeta;
864
881
  export interface CompactionLifecycleEvent {
865
882
  type: 'compaction';
866
883
  data: {
@@ -875,6 +892,11 @@ export interface SubagentStatusEvent {
875
892
  type: 'subagent_status';
876
893
  data: SubagentLifecycleSnapshot;
877
894
  }
895
+ /** Original SDK stream payload, observed by the existing single stream consumer. */
896
+ export interface AgentSdkStreamEvent {
897
+ type: 'sdk';
898
+ data: import('../../../../cbc-shared/lib/node/index.js').RunStreamEvent;
899
+ }
878
900
  export interface AgentInputItemEvent {
879
901
  type: 'input';
880
902
  data: AgentInputItem;
@@ -1063,6 +1085,13 @@ export interface SessionRewindMutationOptions {
1063
1085
  cwd: string;
1064
1086
  headMessageId: string | undefined;
1065
1087
  }
1088
+ /**
1089
+ * model override 的意图来源:决定该值是否等价于「用户显式选择」。
1090
+ * - 'user':/model 命令、ACP set_model、SDK 控制信号等用户动作;
1091
+ * - 'declared':主 Agent 声明模型(declaredModel)联动写入;
1092
+ * - 'materialized':Web UI 会话装配期钉住的兜底解析值(非用户意图)。
1093
+ */
1094
+ export type OverrideModelSource = 'user' | 'declared' | 'materialized';
1066
1095
  export interface SessionManager {
1067
1096
  getCurrent(): Session;
1068
1097
  setCurrent(session: Session): void;
@@ -1165,11 +1194,15 @@ export interface SessionManager {
1165
1194
  setOverridePermissionMode(sessionId: string, mode: PermissionMode): void;
1166
1195
  getOverridePermissionMode(sessionId: string): PermissionMode | undefined;
1167
1196
  /**
1168
- * 设置 session 的 model 覆盖值(来自 SDK control signal)
1169
- * 这些值会覆盖 CLI 参数和配置文件的设置
1197
+ * 设置 session 的 model 覆盖值。source 记录意图来源,供「是否用户显式选择」判定:
1198
+ * - 'user'(默认):/model 命令、ACP set_model、SDK 控制信号等用户动作;
1199
+ * - 'declared':主 Agent 声明模型(declaredModel)联动写入;
1200
+ * - 'materialized':Web UI 会话装配期钉住的兜底解析值(非用户意图)。
1170
1201
  */
1171
- setOverrideModel(sessionId: string, model: string): void;
1202
+ setOverrideModel(sessionId: string, model: string, source?: OverrideModelSource): void;
1172
1203
  getOverrideModel(sessionId: string): string | undefined;
1204
+ getOverrideModelSource(sessionId: string): OverrideModelSource | undefined;
1205
+ clearOverrideModel(sessionId: string): void;
1173
1206
  /**
1174
1207
  * 设置会话运行时模型:同步写 session.options.model + requestOptions.model + override map。
1175
1208
  *
@@ -1178,7 +1211,7 @@ export interface SessionManager {
1178
1211
  * 供 applyOverrideModel 每轮重建 requestOptions(Issue #82146)。/model 命令与模型面板
1179
1212
  * 共用此入口;ACP set_model(applyRuntimeSessionModel)仍为独立实现,待统一收敛。
1180
1213
  */
1181
- setRuntimeModel(session: Session | undefined, modelId: string): void;
1214
+ setRuntimeModel(session: Session | undefined, modelId: string, source?: OverrideModelSource): void;
1182
1215
  /**
1183
1216
  * 设置会话运行时 outputStyle:同步写 session.options.outputStyle。
1184
1217
  *
@@ -1502,6 +1535,7 @@ export interface InterruptionService {
1502
1535
  approve(item: RunToolApprovalItem, options?: ApprovalOptions): void;
1503
1536
  reject(item: RunToolApprovalItem, options?: {
1504
1537
  decision?: 'deny' | 'rejectAndExitPlan';
1538
+ cancelTurn?: boolean;
1505
1539
  }): void;
1506
1540
  waitFor(session: Session): Promise<void>;
1507
1541
  /** Resolve the main/leader session for a given session (returns self if already main). */
@@ -1712,6 +1746,12 @@ export interface TurnMetricsReporter {
1712
1746
  */
1713
1747
  unregister(sessionId: string): void;
1714
1748
  }
1749
+ /** Ids of one model call, as stamped by the model wrapper. */
1750
+ export interface ModelCallGeneration {
1751
+ messageId: string;
1752
+ conversationRequestId?: string;
1753
+ stepSeq?: number;
1754
+ }
1715
1755
  export interface ModelResponseMetadata {
1716
1756
  /** 响应中的模型 ID */
1717
1757
  model?: string;
@@ -1725,6 +1765,8 @@ export interface ModelResponseMetadata {
1725
1765
  * 都带 providerData.messageId === 此值,handleContentFilteredResponse 据此剔除并替换为安全提示。
1726
1766
  */
1727
1767
  contentFilterMessageId?: string;
1768
+ /** Correlation of the generation that just completed, as stamped by the model wrapper. */
1769
+ generation?: ModelCallGeneration;
1728
1770
  usage?: {
1729
1771
  prompt_tokens: number;
1730
1772
  completion_tokens: number;
@@ -1,5 +1,5 @@
1
1
  import type { Observable } from 'rxjs';
2
- import type { Session } from './session-protocol.js';
2
+ import type { AgentRetryReason, Session } from './session-protocol.js';
3
3
  export declare const SessionRunStateMachine: unique symbol;
4
4
  export declare const SessionRunStateProjectionService: unique symbol;
5
5
  export declare const SESSION_RUN_STATE_PROJECTION_SCHEMA: 'session-run-state.v1';
@@ -144,6 +144,7 @@ export type SessionRunEvent = {
144
144
  } | {
145
145
  type: SessionRunEventType.RETRYING;
146
146
  reason?: string;
147
+ retryReason?: AgentRetryReason;
147
148
  } | {
148
149
  type: SessionRunEventType.COMPACT_STARTED;
149
150
  reason?: string;
@@ -189,6 +190,18 @@ export interface SessionRunStateMachine {
189
190
  */
190
191
  getChange$(session: Session, skipFirst?: boolean): Observable<void>;
191
192
  }
193
+ export declare const ModelRequestRetryPhase: unique symbol;
194
+ /**
195
+ * 模型请求层(宿主 provider)在同一次请求内部退避重发时,借此把会话投影为 retrying。
196
+ * 状态写入与守卫收口在 core,宿主只包住退避等待,不直接驱动状态机。
197
+ */
198
+ export interface ModelRequestRetryPhase {
199
+ /**
200
+ * `wait` 期间会话显示 retrying 并携带 `reason`;结束(含抛错 / 取消)后恢复 model_requesting。
201
+ * 非本轮主请求(无 session、辅助型 purpose、状态不在 model_requesting)时只执行 `wait`。
202
+ */
203
+ during<T>(session: Session | undefined, reason: AgentRetryReason, wait: () => Promise<T>): Promise<T>;
204
+ }
192
205
  /**
193
206
  * Pure derivations from a `SessionRunStatus`. The state machine implementation
194
207
  * MUST keep its instance methods (`getLifecycle`, `isBusy`, etc.) consistent
@@ -9,11 +9,18 @@ export declare function hasExhaustedStreamTimeoutRetryBudget(session: Session, i
9
9
  /**
10
10
  * `finish_reason=error` 且没有可用输出的两种空流:reasoning-only 与 no-token。
11
11
  * placeholder(空 200 / 心跳帧)仍是会话层终态,由拦截器切一次备站。
12
+ *
13
+ * 额外的时长闸门(#132616):这两种 kind 之所以可重试,前提是「上游瞬时失败」——秒级
14
+ * 空响应重跑一次大概率就好了。但当这条流已经跑满上游的硬时限才收到 error 时,前提不成立:
15
+ * 同一段 prompt 重跑必然再次跑满同一个时限。实测 7 个会话全部重试、全部再次撞墙、0 次恢复,
16
+ * 而 `#130098` 把预算从 1 提到 3 之后,同样的输入会从 2×900s 变成 4×900s ≈ 60 分钟静默。
17
+ * 重试的期望收益不随 elapsed 增长,成本却线性增长,所以按 elapsed 收口。
12
18
  */
13
19
  export declare function isRetryableEmptyStreamError(error: unknown): boolean;
14
20
  /**
15
21
  * Empty 200 / placeholder-only:拦截器最多切一次备站,会话层终态(#115866)。
16
- * reasoning-only / no-token + finish_reason=error:上游瞬时失败,按 error 预算重试。
22
+ * reasoning-only / no-token + finish_reason=error:上游瞬时失败,按 error 预算重试;
23
+ * 已烧满上游硬时限的那些除外,见 {@link isRetryableEmptyStreamError} 的时长闸门(#132616)。
17
24
  * 流空闲超时 / 首 token 超时:按各自的超时预算重试。
18
25
  */
19
26
  export declare function shouldAutoRetryStreamTimeout(session: Session, error: unknown): boolean;
@@ -34,6 +34,33 @@ export interface TurnContext {
34
34
  /** Canonical id assigned when the external user input is admitted. */
35
35
  conversationRequestId?: string;
36
36
  }
37
+ export interface RequestOwner {
38
+ readonly session: Session;
39
+ readonly requestId: string;
40
+ readonly turn: TurnContext;
41
+ latestExecution?: object;
42
+ }
43
+ export type RequestTerminalOutcome = 'written' | 'failed' | 'unconfirmed';
44
+ export interface RequestTerminal {
45
+ readonly attempt: object;
46
+ outcome?: RequestTerminalOutcome;
47
+ readonly completed: Promise<RequestTerminalOutcome>;
48
+ }
49
+ export declare function getRequestOwner(ref: object): RequestOwner | undefined;
50
+ export declare function getRequestExecution(ref: object): object | undefined;
51
+ export declare function associateRequestOwner(ref: object, owner: RequestOwner, execution?: object): void;
52
+ /** Carry physical execution through admission without inventing a request owner. */
53
+ export declare function associateRequestExecution(ref: object, execution: object): void;
54
+ /** Reserve completion ownership at admission, retaining the prior result if startup fails. */
55
+ export declare function beginRequestExecution(owner: RequestOwner, execution: object): void;
56
+ /** A failed startup never owns a result; restore only the execution it actually replaced. */
57
+ export declare function abandonRequestExecution(execution: object): Session['resultSubject']['value'];
58
+ export declare function getRequestTerminal(owner: RequestOwner, destination: string): RequestTerminal | undefined;
59
+ export declare function claimRequestTerminal(owner: RequestOwner, destination: string, attempt: object): boolean;
60
+ export declare function settleRequestTerminal(owner: RequestOwner, destination: string, attempt: object, outcome: RequestTerminalOutcome): void;
61
+ /** The admission boundary, not trace binding, creates a new logical request. */
62
+ export declare function admitRequestOwner(session: Session, requestId: string): RequestOwner;
63
+ export declare function runInRequestOwner<T>(owner: RequestOwner, callback: () => T): T;
37
64
  /** Transient provider-data key; stripped before history/model persistence. */
38
65
  export declare const TURN_CARRIER_CANDIDATE_KEY = "codebuddy.ai/turnCarrierCandidate";
39
66
  export declare const BAGGAGE_KEY_SESSION_ID = "codebuddy.session_id";
@@ -83,7 +110,7 @@ export declare function bindCurrentTurnContext(session: Session, conversationReq
83
110
  /** Carrier for one exact admitted request; never returns the latest other turn. */
84
111
  export declare function turnCarrierForRequest(session: Session, conversationRequestId: string | undefined): W3CTraceContextMeta;
85
112
  /** Release an exact request binding. */
86
- export declare function releaseTurnContext(session: Session, conversationRequestId: string | undefined): void;
113
+ export declare function releaseTurnContext(session: Session, conversationRequestId: string | undefined, owner?: RequestOwner): void;
87
114
  /** The current turn as an injectable three-key carrier; `{}` outside a turn. */
88
115
  export declare function currentTurnCarrier(): W3CTraceContextMeta;
89
116
  /**
@@ -58,6 +58,26 @@ export declare abstract class CommandMessageUtils {
58
58
  * 解析XML格式的命令输出
59
59
  */
60
60
  static parseOutput(xmlText: string): ParseCommandMessage | undefined;
61
+ /**
62
+ * 是否为本地命令在会话历史里留下的记账项:caveat、命令回显(`<command-name>`)、
63
+ * 命令输出(`<local-command-stdout>`)、本地 bash 的输入输出、plan 输出。
64
+ *
65
+ * 这些 item 的 role 是 user、也不带 skipRun,因此按「真实用户消息」判定会命中;
66
+ * 但它们只是本地命令的痕迹,**不代表用户开启了新一轮对话**。调用方(如 TUI 的
67
+ * loading 计时器)需要区分「新一轮」和「本地命令留痕」时用这个判据。
68
+ *
69
+ * 识别口径复用 parseInput / parseOutput / parsePlanOutput,避免调用方各自
70
+ * 维护一份 XML 标签清单而漂移。
71
+ */
72
+ static isLocalCommandRecord(text: string): boolean;
73
+ /**
74
+ * {@link isLocalCommandRecord} 的 item 版本。
75
+ *
76
+ * 必须读 **原始** content:`MessageContentUtils.extractTextContent` 已经把命令 XML
77
+ * 拆开(`<command-name>/doctor</command-name>` → `/doctor`),拿它的结果再判就永远
78
+ * 判不出来。
79
+ */
80
+ static isLocalCommandRecordItem(item: UserMessageItem): boolean;
61
81
  /**
62
82
  * 解析计划输出
63
83
  */