@cjhyy/code-shell-core 0.6.0-rc.9 → 0.7.0

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 (193) hide show
  1. package/THIRD_PARTY_NOTICES.md +206 -0
  2. package/dist/automation/scheduler.d.ts +13 -7
  3. package/dist/automation/scheduler.js +116 -37
  4. package/dist/capability-control/service.d.ts +2 -0
  5. package/dist/capability-control/service.js +4 -2
  6. package/dist/cc-orchestrator/agent-adapter.d.ts +4 -0
  7. package/dist/cc-orchestrator/agent-adapter.js +11 -1
  8. package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
  9. package/dist/cc-orchestrator/codex-session-history.js +65 -5
  10. package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
  11. package/dist/cc-orchestrator/cwd-normalize.js +19 -0
  12. package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
  13. package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
  14. package/dist/cc-orchestrator/external-agent-changes.js +22 -5
  15. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
  16. package/dist/cc-orchestrator/external-agent-driver.js +265 -50
  17. package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
  18. package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
  19. package/dist/cc-orchestrator/session-history.d.ts +35 -0
  20. package/dist/cc-orchestrator/session-history.js +98 -15
  21. package/dist/cli/agent-server-stdio.js +9 -2
  22. package/dist/context/compaction.d.ts +8 -1
  23. package/dist/context/compaction.js +49 -4
  24. package/dist/context/manager.d.ts +16 -2
  25. package/dist/context/manager.js +103 -19
  26. package/dist/credentials/access.d.ts +57 -0
  27. package/dist/credentials/access.js +185 -0
  28. package/dist/credentials/index.d.ts +3 -1
  29. package/dist/credentials/index.js +2 -0
  30. package/dist/credentials/inject-credential-tool.d.ts +3 -1
  31. package/dist/credentials/inject-credential-tool.js +30 -11
  32. package/dist/credentials/oauth.d.ts +20 -0
  33. package/dist/credentials/oauth.js +114 -0
  34. package/dist/credentials/store.d.ts +1 -0
  35. package/dist/credentials/store.js +3 -1
  36. package/dist/credentials/types.d.ts +49 -3
  37. package/dist/credentials/use-credential-tool.d.ts +9 -1
  38. package/dist/credentials/use-credential-tool.js +58 -45
  39. package/dist/engine/engine.d.ts +23 -3
  40. package/dist/engine/engine.js +380 -255
  41. package/dist/engine/goal.d.ts +17 -0
  42. package/dist/engine/goal.js +16 -6
  43. package/dist/engine/image-policy.d.ts +6 -0
  44. package/dist/engine/image-policy.js +17 -6
  45. package/dist/engine/input-attachments.d.ts +13 -0
  46. package/dist/engine/input-attachments.js +398 -0
  47. package/dist/engine/model-facade.d.ts +5 -2
  48. package/dist/engine/model-facade.js +4 -4
  49. package/dist/engine/parse-task.d.ts +10 -0
  50. package/dist/engine/parse-task.js +5 -0
  51. package/dist/engine/run-image-input.d.ts +22 -0
  52. package/dist/engine/run-image-input.js +195 -0
  53. package/dist/engine/steer-queue.d.ts +3 -1
  54. package/dist/engine/steer-queue.js +10 -2
  55. package/dist/engine/streaming-tool-queue.d.ts +11 -7
  56. package/dist/engine/streaming-tool-queue.js +11 -7
  57. package/dist/engine/turn-loop.d.ts +37 -2
  58. package/dist/engine/turn-loop.js +228 -43
  59. package/dist/engine/types.d.ts +8 -0
  60. package/dist/git/worktree/crud.d.ts +69 -0
  61. package/dist/git/worktree/crud.js +206 -0
  62. package/dist/git/worktree/diff.d.ts +14 -0
  63. package/dist/git/worktree/diff.js +82 -0
  64. package/dist/git/worktree/git-exec.d.ts +7 -0
  65. package/dist/git/worktree/git-exec.js +51 -0
  66. package/dist/git/worktree/index.d.ts +5 -0
  67. package/dist/git/worktree/index.js +5 -0
  68. package/dist/git/worktree/query.d.ts +42 -0
  69. package/dist/git/worktree/query.js +121 -0
  70. package/dist/git/worktree/slug.d.ts +11 -0
  71. package/dist/git/worktree/slug.js +58 -0
  72. package/dist/git/worktree.d.ts +1 -84
  73. package/dist/git/worktree.js +5 -230
  74. package/dist/hooks/goal-stop-hook.d.ts +33 -1
  75. package/dist/hooks/goal-stop-hook.js +202 -34
  76. package/dist/index.d.ts +31 -28
  77. package/dist/index.js +27 -25
  78. package/dist/logging/logger.js +6 -6
  79. package/dist/logging/sanitize-messages.d.ts +10 -2
  80. package/dist/logging/sanitize-messages.js +21 -6
  81. package/dist/plugins/installer/checkUpdate.d.ts +4 -1
  82. package/dist/plugins/installer/checkUpdate.js +4 -2
  83. package/dist/plugins/installer/install.js +2 -0
  84. package/dist/plugins/installer/installFromSource.js +9 -1
  85. package/dist/plugins/installer/parseSource.d.ts +4 -1
  86. package/dist/plugins/installer/parseSource.js +28 -10
  87. package/dist/plugins/installer/sourcePath.d.ts +9 -0
  88. package/dist/plugins/installer/sourcePath.js +50 -0
  89. package/dist/plugins/installer/update.d.ts +4 -1
  90. package/dist/plugins/installer/update.js +5 -3
  91. package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
  92. package/dist/plugins/parseMarketplaceInput.js +4 -3
  93. package/dist/plugins/pluginInstaller.js +24 -22
  94. package/dist/preset/index.d.ts +1 -0
  95. package/dist/preset/index.js +30 -13
  96. package/dist/prompt/sections/base.md +1 -1
  97. package/dist/protocol/chat-session-manager.d.ts +2 -0
  98. package/dist/protocol/chat-session-manager.js +38 -2
  99. package/dist/protocol/chat-session.d.ts +3 -0
  100. package/dist/protocol/chat-session.js +1 -0
  101. package/dist/protocol/client.d.ts +9 -4
  102. package/dist/protocol/client.js +18 -1
  103. package/dist/protocol/server.d.ts +30 -0
  104. package/dist/protocol/server.js +247 -47
  105. package/dist/protocol/types.d.ts +49 -0
  106. package/dist/protocol/types.js +6 -0
  107. package/dist/run/FileRunStore.js +10 -1
  108. package/dist/run/Heartbeat.js +12 -0
  109. package/dist/run/RunApprovalBackend.d.ts +3 -0
  110. package/dist/run/RunApprovalBackend.js +41 -6
  111. package/dist/run/RunLock.js +2 -0
  112. package/dist/run/RunManager.d.ts +2 -0
  113. package/dist/run/RunManager.js +64 -24
  114. package/dist/run/ids.d.ts +2 -0
  115. package/dist/run/ids.js +23 -0
  116. package/dist/runtime/background-shell.d.ts +1 -0
  117. package/dist/runtime/background-shell.js +23 -13
  118. package/dist/runtime/spawn-common.js +10 -0
  119. package/dist/services/auto-dream.d.ts +15 -4
  120. package/dist/services/auto-dream.js +20 -20
  121. package/dist/services/dream-consolidation.d.ts +2 -3
  122. package/dist/services/dream-consolidation.js +65 -15
  123. package/dist/services/extract-memories.d.ts +14 -5
  124. package/dist/services/extract-memories.js +20 -3
  125. package/dist/services/global-dream-promotion.d.ts +23 -0
  126. package/dist/services/global-dream-promotion.js +112 -0
  127. package/dist/services/memory-orchestrator.js +347 -34
  128. package/dist/session/memory.d.ts +61 -18
  129. package/dist/session/memory.js +342 -79
  130. package/dist/session/session-manager.d.ts +35 -1
  131. package/dist/session/session-manager.js +224 -4
  132. package/dist/session/transcript.d.ts +1 -1
  133. package/dist/session/transcript.js +17 -5
  134. package/dist/settings/manager.d.ts +1 -0
  135. package/dist/settings/manager.js +87 -37
  136. package/dist/settings/schema-export.d.ts +2 -3
  137. package/dist/settings/schema-export.js +2 -3
  138. package/dist/settings/schema.d.ts +21 -0
  139. package/dist/settings/schema.js +12 -0
  140. package/dist/skills/scanner.d.ts +3 -2
  141. package/dist/skills/scanner.js +12 -9
  142. package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
  143. package/dist/tool-system/builtin/agent-notifications.js +19 -7
  144. package/dist/tool-system/builtin/background-jobs.d.ts +35 -6
  145. package/dist/tool-system/builtin/background-jobs.js +116 -7
  146. package/dist/tool-system/builtin/background-work.d.ts +17 -18
  147. package/dist/tool-system/builtin/background-work.js +51 -5
  148. package/dist/tool-system/builtin/config.d.ts +2 -1
  149. package/dist/tool-system/builtin/config.js +16 -11
  150. package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
  151. package/dist/tool-system/builtin/cron-list.definition.js +6 -0
  152. package/dist/tool-system/builtin/cron.d.ts +1 -2
  153. package/dist/tool-system/builtin/cron.js +9 -7
  154. package/dist/tool-system/builtin/drive-claude-code.d.ts +23 -2
  155. package/dist/tool-system/builtin/drive-claude-code.js +589 -48
  156. package/dist/tool-system/builtin/edit.js +5 -2
  157. package/dist/tool-system/builtin/generate-video.d.ts +1 -0
  158. package/dist/tool-system/builtin/generate-video.js +13 -4
  159. package/dist/tool-system/builtin/index.d.ts +8 -3
  160. package/dist/tool-system/builtin/index.js +50 -22
  161. package/dist/tool-system/builtin/lsp.d.ts +2 -1
  162. package/dist/tool-system/builtin/lsp.js +6 -3
  163. package/dist/tool-system/builtin/memory.js +40 -8
  164. package/dist/tool-system/builtin/notebook-edit.js +5 -2
  165. package/dist/tool-system/builtin/powershell.d.ts +5 -2
  166. package/dist/tool-system/builtin/powershell.js +11 -7
  167. package/dist/tool-system/builtin/read.js +118 -6
  168. package/dist/tool-system/builtin/sleep.d.ts +1 -2
  169. package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
  170. package/dist/tool-system/builtin/sleep.definition.js +28 -0
  171. package/dist/tool-system/builtin/sleep.js +1 -22
  172. package/dist/tool-system/builtin/view-image.d.ts +2 -2
  173. package/dist/tool-system/builtin/view-image.js +109 -19
  174. package/dist/tool-system/builtin/worktree.d.ts +4 -4
  175. package/dist/tool-system/builtin/worktree.js +297 -74
  176. package/dist/tool-system/builtin/write.js +5 -3
  177. package/dist/tool-system/context.d.ts +41 -1
  178. package/dist/tool-system/executor.d.ts +1 -5
  179. package/dist/tool-system/executor.js +94 -115
  180. package/dist/tool-system/mcp-manager.d.ts +31 -6
  181. package/dist/tool-system/mcp-manager.js +193 -78
  182. package/dist/tool-system/path-policy.d.ts +2 -0
  183. package/dist/tool-system/path-policy.js +41 -12
  184. package/dist/tool-system/permission.d.ts +28 -7
  185. package/dist/tool-system/permission.js +130 -49
  186. package/dist/tool-system/registry.js +11 -4
  187. package/dist/tool-system/tool-result-redaction.d.ts +7 -0
  188. package/dist/tool-system/tool-result-redaction.js +48 -0
  189. package/dist/tool-system/workspace-bridge.d.ts +11 -0
  190. package/dist/tool-system/workspace-bridge.js +1 -0
  191. package/dist/types.d.ts +78 -11
  192. package/dist/utils/toolDisplay.js +1 -1
  193. package/package.json +4 -3
@@ -0,0 +1,195 @@
1
+ import { existsSync } from "node:fs";
2
+ import { isAbsolute, join } from "node:path";
3
+ import { capabilitiesFor } from "../llm/capabilities/index.js";
4
+ import { logger } from "../logging/logger.js";
5
+ import { tryCompressImages } from "./image-compression.js";
6
+ import { byteLengthFromBase64, collectAttachedImagePaths, dropOversizedImages, enforceImagePolicy, } from "./image-policy.js";
7
+ import { buildInputAttachmentContext } from "./input-attachments.js";
8
+ import { parseTaskWithImages } from "./parse-task.js";
9
+ export async function prepareRunImageInput(args) {
10
+ const { task, cwd, llm, sessionId } = args;
11
+ // Parse `<codeshell-image>` blocks out of the raw task string before
12
+ // any other gate looks at it. Two concerns:
13
+ // 1. The noise detector sees raw base64 as gibberish and would reject the
14
+ // whole turn — split images out first so it only inspects prose.
15
+ // 2. Models that don't accept vision must be refused immediately, with the
16
+ // image bytes intact for the user to retry on another model.
17
+ let parsedTask;
18
+ try {
19
+ parsedTask = parseTaskWithImages(task);
20
+ }
21
+ catch (err) {
22
+ const msg = err.message;
23
+ logger.warn("engine.run.image_parse_failed", { error: msg });
24
+ return {
25
+ ok: false,
26
+ result: {
27
+ text: `ERROR: image attachment is malformed (${msg}). Drop the image and try again, or re-attach it.`,
28
+ reason: "image_error",
29
+ sessionId: sessionId ?? "image-parse-failed",
30
+ turnCount: 0,
31
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
32
+ },
33
+ };
34
+ }
35
+ const cap = capabilitiesFor((llm.providerKind ?? llm.provider), llm.model);
36
+ let attachmentContext;
37
+ try {
38
+ attachmentContext = await buildInputAttachmentContext(args.attachments, cwd, {
39
+ includeImageBytes: cap.supportsVision,
40
+ expectedSessionId: sessionId,
41
+ });
42
+ }
43
+ catch (err) {
44
+ const message = err instanceof Error ? err.message : String(err);
45
+ logger.error("engine.run.input_attachment_exception", {
46
+ stage: "build_input_attachment_context",
47
+ attachmentCount: args.attachments?.length ?? 0,
48
+ error: message,
49
+ });
50
+ return {
51
+ ok: false,
52
+ result: {
53
+ text: `ERROR: input attachment could not be prepared (${message}). Re-attach it or choose a path inside the workspace.`,
54
+ reason: "image_error",
55
+ sessionId: sessionId ?? "input-attachment-exception",
56
+ turnCount: 0,
57
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
58
+ },
59
+ };
60
+ }
61
+ if (attachmentContext.errors.length > 0) {
62
+ const detail = attachmentContext.errors.join("; ");
63
+ logger.warn("engine.run.input_attachment_failed", { error: detail });
64
+ return {
65
+ ok: false,
66
+ result: {
67
+ text: `ERROR: input attachment could not be read (${detail}). Re-attach it or choose a path inside the workspace.`,
68
+ reason: "image_error",
69
+ sessionId: sessionId ?? "input-attachment-failed",
70
+ turnCount: 0,
71
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
72
+ },
73
+ };
74
+ }
75
+ if (attachmentContext.text || attachmentContext.hasStructuredImageAttachments) {
76
+ parsedTask = {
77
+ text: [parsedTask.text, attachmentContext.text].filter(Boolean).join("\n\n"),
78
+ images: [...parsedTask.images, ...attachmentContext.images],
79
+ hasImages: parsedTask.hasImages ||
80
+ attachmentContext.images.length > 0 ||
81
+ attachmentContext.hasStructuredImageAttachments,
82
+ };
83
+ }
84
+ if (parsedTask.hasImages) {
85
+ if (!cap.supportsVision) {
86
+ logger.warn("engine.run.vision_not_supported", {
87
+ provider: llm.provider,
88
+ model: llm.model,
89
+ imageCount: parsedTask.images.length,
90
+ });
91
+ return {
92
+ ok: false,
93
+ result: {
94
+ text: `ERROR: model "${llm.model}" does not accept image input. ` +
95
+ `Switch to a vision-capable model (e.g. gpt-4o, claude-sonnet, gemini-1.5-pro) and resend.`,
96
+ reason: "image_error",
97
+ sessionId: sessionId ?? "vision-not-supported",
98
+ turnCount: 0,
99
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
100
+ },
101
+ };
102
+ }
103
+ // Size gate. Hosts are expected to pre-compress to IMAGE_TARGETS; this is
104
+ // the last cheap refusal point before a provider request carries the bytes.
105
+ let verdict = enforceImagePolicy(parsedTask.images);
106
+ if (!verdict.ok && verdict.code === "image_too_large") {
107
+ const compressed = await tryCompressImages(parsedTask.images);
108
+ if (compressed.anyCompressed) {
109
+ parsedTask.images = compressed.images;
110
+ logger.info("engine.run.image_compressed", {
111
+ before: verdict.offender?.bytes,
112
+ after: compressed.images.reduce((s, i) => s + byteLengthFromBase64(i.base64), 0),
113
+ });
114
+ verdict = enforceImagePolicy(parsedTask.images);
115
+ }
116
+ }
117
+ // After compression, anything still over the per-image cap is dropped with
118
+ // a textual placeholder so oversized bytes do not poison conversation history.
119
+ if (!verdict.ok && verdict.code === "image_too_large") {
120
+ const drop = dropOversizedImages(parsedTask.images);
121
+ if (drop.droppedCount > 0) {
122
+ parsedTask.images = drop.kept;
123
+ parsedTask.hasImages = drop.kept.length > 0;
124
+ parsedTask.text = drop.placeholder + "\n\n" + parsedTask.text;
125
+ logger.warn("engine.run.image_dropped", {
126
+ droppedCount: drop.droppedCount,
127
+ keptCount: drop.kept.length,
128
+ });
129
+ verdict = enforceImagePolicy(parsedTask.images);
130
+ }
131
+ }
132
+ if (!verdict.ok) {
133
+ logger.warn("engine.run.image_policy_failed", {
134
+ code: verdict.code,
135
+ imageCount: verdict.totals.imageCount,
136
+ totalBytes: verdict.totals.totalBytes,
137
+ offender: verdict.offender,
138
+ });
139
+ return {
140
+ ok: false,
141
+ result: {
142
+ text: `ERROR: ${verdict.message}`,
143
+ reason: "image_error",
144
+ sessionId: sessionId ?? `image-policy-${verdict.code}`,
145
+ turnCount: 0,
146
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
147
+ },
148
+ };
149
+ }
150
+ }
151
+ // For downstream noise-detection + transcript persistence we want the text
152
+ // portion only. Image bytes ride in parsedTask.images and re-enter the message
153
+ // tree below.
154
+ const taskText = parsedTask.text;
155
+ return { ok: true, parsedTask, taskText };
156
+ }
157
+ export function buildRunUserMessageContent(parsedTask, cwd, taskText) {
158
+ // Compose the user-turn payload once so resume + cold paths agree on shape.
159
+ // When an attached image came from a workspace file, surface that path to the
160
+ // model as text so file-oriented tools can use the real on-disk path.
161
+ const attachedPaths = collectAttachedImagePaths(parsedTask.images, (name) => (isAbsolute(name) ? name : join(cwd, name)), existsSync);
162
+ const pathHint = attachedPaths.length > 0
163
+ ? `\n\n<attached-image-paths>\n${attachedPaths.join("\n")}\n</attached-image-paths>\n` +
164
+ `(上面附带的图片在工作区的真实路径,如需把它们作为工具输入(例如 GenerateImage 的 referenceImages、图生图参考图),直接使用这些路径。)`
165
+ : "";
166
+ const userMessageContent = parsedTask.hasImages
167
+ ? [
168
+ ...(parsedTask.text || pathHint
169
+ ? [{ type: "text", text: `${parsedTask.text}${pathHint}` }]
170
+ : []),
171
+ ...parsedTask.images.map((img) => ({
172
+ type: "image",
173
+ source: {
174
+ type: "base64",
175
+ media_type: img.mime,
176
+ data: img.base64,
177
+ },
178
+ })),
179
+ ]
180
+ : taskText;
181
+ logger.info("engine.run.user_message_content_built", {
182
+ contentShape: Array.isArray(userMessageContent) ? "content_blocks" : "text",
183
+ textBlockCount: Array.isArray(userMessageContent)
184
+ ? userMessageContent.filter((block) => block.type === "text").length
185
+ : userMessageContent
186
+ ? 1
187
+ : 0,
188
+ imageBlockCount: Array.isArray(userMessageContent)
189
+ ? userMessageContent.filter((block) => block.type === "image").length
190
+ : 0,
191
+ attachedPathCount: attachedPaths.length,
192
+ textLength: taskText.length,
193
+ });
194
+ return userMessageContent;
195
+ }
@@ -6,13 +6,15 @@
6
6
  * it. Pure + side-effect-free so it unit-tests without the Engine/TurnLoop
7
7
  * harness (same rationale as the renderer's queuedInput.ts).
8
8
  */
9
+ import type { InputAttachmentMeta } from "../protocol/types.js";
9
10
  export interface SteerItem {
10
11
  id: string;
11
12
  text: string;
12
13
  clientMessageId?: string;
14
+ attachments?: InputAttachmentMeta[];
13
15
  }
14
16
  /** Append a steer entry. Blank text is dropped (returns the list unchanged). */
15
- export declare function enqueueSteerItem(list: SteerItem[], id: string, text: string, clientMessageId?: string): SteerItem[];
17
+ export declare function enqueueSteerItem(list: SteerItem[], id: string, text: string, clientMessageId?: string, attachments?: InputAttachmentMeta[]): SteerItem[];
16
18
  /**
17
19
  * Take everything currently queued and clear the list. Returns the drained
18
20
  * entries (in order) and the now-empty remainder. The turn loop calls this at
@@ -1,9 +1,17 @@
1
1
  /** Append a steer entry. Blank text is dropped (returns the list unchanged). */
2
- export function enqueueSteerItem(list, id, text, clientMessageId) {
2
+ export function enqueueSteerItem(list, id, text, clientMessageId, attachments) {
3
3
  const t = text?.trim();
4
4
  if (!id || !t)
5
5
  return list;
6
- return [...list, { id, text: t, ...(clientMessageId ? { clientMessageId } : {}) }];
6
+ return [
7
+ ...list,
8
+ {
9
+ id,
10
+ text: t,
11
+ ...(clientMessageId ? { clientMessageId } : {}),
12
+ ...(attachments && attachments.length > 0 ? { attachments: [...attachments] } : {}),
13
+ },
14
+ ];
7
15
  }
8
16
  /**
9
17
  * Take everything currently queued and clear the list. Returns the drained
@@ -1,13 +1,16 @@
1
1
  /**
2
- * Streaming tool queue enqueues tools for execution during streaming,
3
- * starts concurrency-safe tools immediately, queues unsafe tools for
4
- * sequential execution.
2
+ * Tool execution queue for a completed model response.
3
+ *
4
+ * Despite the historical name, TurnLoop currently enqueues tool calls only
5
+ * after `callModelWithFallback()` returns a complete LLMResponse. The queue's
6
+ * job is concurrency policy inside that post-response batch: start
7
+ * concurrency-safe tools as soon as they are enqueued, keep unsafe tools
8
+ * sequential, and return results in original tool-call order.
5
9
  *
6
10
  * Usage:
7
11
  * const queue = new StreamingToolQueue(executor);
8
- * // During streaming, as tool_use blocks arrive:
12
+ * // After the full LLMResponse is available:
9
13
  * queue.enqueue(toolCall);
10
- * // After streaming completes:
11
14
  * const results = await queue.drain();
12
15
  */
13
16
  import type { ToolCall, ToolResult } from "../types.js";
@@ -21,8 +24,9 @@ export declare class StreamingToolQueue {
21
24
  private draining;
22
25
  constructor(executor: ToolExecutor);
23
26
  /**
24
- * Enqueue a tool for execution. Concurrency-safe tools start immediately;
25
- * unsafe tools are queued for sequential execution during drain().
27
+ * Enqueue a tool from the completed response. Concurrency-safe tools start
28
+ * immediately within this post-response phase; unsafe tools are queued for
29
+ * sequential execution during drain().
26
30
  */
27
31
  enqueue(call: ToolCall): void;
28
32
  /**
@@ -1,13 +1,16 @@
1
1
  /**
2
- * Streaming tool queue enqueues tools for execution during streaming,
3
- * starts concurrency-safe tools immediately, queues unsafe tools for
4
- * sequential execution.
2
+ * Tool execution queue for a completed model response.
3
+ *
4
+ * Despite the historical name, TurnLoop currently enqueues tool calls only
5
+ * after `callModelWithFallback()` returns a complete LLMResponse. The queue's
6
+ * job is concurrency policy inside that post-response batch: start
7
+ * concurrency-safe tools as soon as they are enqueued, keep unsafe tools
8
+ * sequential, and return results in original tool-call order.
5
9
  *
6
10
  * Usage:
7
11
  * const queue = new StreamingToolQueue(executor);
8
- * // During streaming, as tool_use blocks arrive:
12
+ * // After the full LLMResponse is available:
9
13
  * queue.enqueue(toolCall);
10
- * // After streaming completes:
11
14
  * const results = await queue.drain();
12
15
  */
13
16
  export class StreamingToolQueue {
@@ -21,8 +24,9 @@ export class StreamingToolQueue {
21
24
  this.executor = executor;
22
25
  }
23
26
  /**
24
- * Enqueue a tool for execution. Concurrency-safe tools start immediately;
25
- * unsafe tools are queued for sequential execution during drain().
27
+ * Enqueue a tool from the completed response. Concurrency-safe tools start
28
+ * immediately within this post-response phase; unsafe tools are queued for
29
+ * sequential execution during drain().
26
30
  */
27
31
  enqueue(call) {
28
32
  this.callOrder.push(call.id);
@@ -4,15 +4,16 @@
4
4
  * Following Claude Code's po_() pattern:
5
5
  * pre_check → model_call → post_check → tool_exec → context_mgmt → hook_notify → next turn
6
6
  */
7
- import type { Message, StreamCallback, TerminalReason, ContentBlock, ToolResult, TokenUsage } from "../types.js";
7
+ import type { Message, StreamCallback, TerminalReason, ContentBlock, ToolResult, TokenUsage, ContextUsageAnchor } from "../types.js";
8
8
  import type { SteerItem } from "./steer-queue.js";
9
9
  import { ModelFacade } from "./model-facade.js";
10
10
  import { ToolExecutor } from "../tool-system/executor.js";
11
11
  import { ContextManager } from "../context/manager.js";
12
12
  import { HookRegistry } from "../hooks/registry.js";
13
+ import { type GoalJudgeRuntimeContext } from "../hooks/goal-stop-hook.js";
13
14
  import { Transcript } from "../session/transcript.js";
14
15
  import { type CumulativeUsageCounters } from "./session-usage.js";
15
- import { type GoalConfig, type GoalExtension } from "./goal.js";
16
+ import { type GoalConfig, type GoalExtension, type GoalTerminationReason } from "./goal.js";
16
17
  export interface TurnLoopConfig {
17
18
  maxTurns: number;
18
19
  maxToolCallsPerTurn: number;
@@ -25,6 +26,11 @@ export interface TurnLoopConfig {
25
26
  * response, then downgrades them to placeholders in the working history.
26
27
  */
27
28
  freshImageMessages?: Iterable<Message>;
29
+ /**
30
+ * Per-run injected context that must be visible to the model but must not be
31
+ * summarized into durable history. Engine uses this for dynamicContext.
32
+ */
33
+ volatileContextMessages?: Iterable<Message>;
28
34
  /**
29
35
  * Fired after each turn boundary is recorded. Lets the engine flush an
30
36
  * up-to-date snapshot to state.json mid-run, so a long run doesn't leave
@@ -88,6 +94,13 @@ export interface TurnLoopDeps {
88
94
  * Returns the updated counters so usage_update can carry both metric scopes.
89
95
  */
90
96
  recordCumulativeUsage?: (usage: TokenUsage) => CumulativeUsageCounters;
97
+ /**
98
+ * Lightweight per-session prompt-cache diagnostic, owned by Engine because it
99
+ * needs memory across TurnLoop instances.
100
+ */
101
+ recordCacheReadDiagnostics?: (usage: TokenUsage) => void;
102
+ /** Persist the latest context-estimation anchor derived from provider usage. */
103
+ recordContextUsageAnchor?: (anchor: ContextUsageAnchor) => void;
91
104
  /**
92
105
  * Reads/clears any user messages queued for THIS session via the steering
93
106
  * channel (Engine.enqueueSteer) while a run is in flight. Consumed at the top
@@ -97,12 +110,20 @@ export interface TurnLoopDeps {
97
110
  * tests (turn loop tolerates undefined).
98
111
  */
99
112
  consumeSteer?: (source?: "normal_step" | "finalize_backfill") => SteerItem[];
113
+ /** Restore steer items whose model-facing content could not be prepared. */
114
+ restoreSteer?: (items: SteerItem[]) => void;
115
+ /** Build model-facing content for a queued steer item, including attachments. */
116
+ buildSteerUserMessageContent?: (item: SteerItem) => string | ContentBlock[] | Promise<string | ContentBlock[]>;
100
117
  /**
101
118
  * Execution-level idempotency guard for host-supplied user/steer intents.
102
119
  * Returns false only when a present clientMessageId has already entered this
103
120
  * run or the persisted transcript. Messages without an id bypass this guard.
104
121
  */
105
122
  claimClientMessageId?: (clientMessageId: string, source: "steer") => boolean;
123
+ /** Release a steer id reservation when preparation fails before persistence. */
124
+ releaseClientMessageId?: (clientMessageId: string) => void;
125
+ /** Make tools launched after an injected steer attribute side effects to that steer. */
126
+ setOriginClientMessageId?: (clientMessageId: string | undefined) => void;
106
127
  /**
107
128
  * Clear this session's PERSISTED goal (state.activeGoal) and drop the
108
129
  * in-flight goal-stop hook, so a user-initiated cancel_goal both stops the
@@ -112,11 +133,18 @@ export interface TurnLoopDeps {
112
133
  * goalTracker short-circuit still stops the run).
113
134
  */
114
135
  clearPersistedGoal?: () => void;
136
+ /**
137
+ * Private Goal-judge evidence seam. Engine stores this snapshot in the
138
+ * built-in judge closure; it is never added to the public on_stop context.
139
+ */
140
+ updateGoalJudgeContext?: (context: GoalJudgeRuntimeContext) => void;
115
141
  }
116
142
  export interface TurnLoopResult {
117
143
  text: string;
118
144
  reason: TerminalReason;
119
145
  messages: Message[];
146
+ /** Goal-specific forced terminal outcome; public TerminalReason stays stable. */
147
+ goalTermination?: GoalTerminationReason;
120
148
  }
121
149
  /**
122
150
  * 把一个 ToolResult 映射成发给 LLM 的 tool_result ContentBlock。
@@ -141,7 +169,9 @@ export declare class TurnLoop {
141
169
  private lastCtxEmit;
142
170
  private currentTurnUsage;
143
171
  private currentCumulativeUsage;
172
+ private readonly sensitiveToolResultRedactions;
144
173
  private readonly pendingImageMessages;
174
+ private readonly volatileContextMessages;
145
175
  /**
146
176
  * Consecutive on_stop blocks (Goal mode kept the agent going). Reset to 0
147
177
  * on any unblocked completion. When it reaches config.maxStopBlocks the
@@ -200,7 +230,11 @@ export declare class TurnLoop {
200
230
  */
201
231
  private markStopped;
202
232
  private prepareMessagesForModel;
233
+ private stripVolatileContextMessages;
234
+ private appendVolatileContextMessages;
203
235
  private markPendingImagesConsumed;
236
+ private redactConsumedSensitiveToolResults;
237
+ private modelCallRecordingOptions;
204
238
  private trackFreshImageMessage;
205
239
  private emitHook;
206
240
  /**
@@ -217,6 +251,7 @@ export declare class TurnLoop {
217
251
  * Run the multi-turn agent loop until completion.
218
252
  */
219
253
  run(initialMessages: Message[]): Promise<TurnLoopResult>;
254
+ private runUnredacted;
220
255
  /**
221
256
  * Call model with streaming fallback.
222
257
  * If streaming fails, emit tombstone and retry non-streaming.