@vellumai/assistant 0.11.4-staging.2 → 0.11.4-staging.4

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 (209) hide show
  1. package/AGENTS.md +8 -2
  2. package/ARCHITECTURE.md +2 -0
  3. package/docs/architecture/memory.md +15 -0
  4. package/docs/browser-use-architecture-phase2.md +128 -56
  5. package/docs/flux-turn-detection-spike.md +11 -6
  6. package/docs/guardian-request-flow.md +35 -0
  7. package/knip.json +3 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  10. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
  11. package/openapi.yaml +136 -76
  12. package/package.json +1 -1
  13. package/scripts/write-plugin-api-shim.ts +10 -0
  14. package/src/__tests__/app-control-flow.test.ts +1 -0
  15. package/src/__tests__/approval-routes-http.test.ts +2 -2
  16. package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
  17. package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
  18. package/src/__tests__/compaction-events.test.ts +8 -10
  19. package/src/__tests__/conversation-agent-loop.test.ts +4 -1
  20. package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
  21. package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
  22. package/src/__tests__/conversation-notifiers-provenance.test.ts +1 -1
  23. package/src/__tests__/conversation-queue.test.ts +39 -62
  24. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  25. package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
  26. package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
  27. package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
  28. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
  29. package/src/__tests__/conversation-runtime-assembly.test.ts +53 -0
  30. package/src/__tests__/conversation-slash-queue.test.ts +3 -0
  31. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  32. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
  33. package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
  34. package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
  35. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
  36. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
  37. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
  38. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
  39. package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
  40. package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
  41. package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
  42. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
  43. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  44. package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
  45. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  46. package/src/__tests__/document-sync-tags.test.ts +0 -75
  47. package/src/__tests__/file-ops-service.test.ts +163 -30
  48. package/src/__tests__/filesystem-tools.test.ts +23 -24
  49. package/src/__tests__/gateway-only-guard.test.ts +2 -5
  50. package/src/__tests__/host-file-read-tool.test.ts +16 -19
  51. package/src/__tests__/http-user-message-parity.test.ts +1 -1
  52. package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
  53. package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
  54. package/src/__tests__/media-generate-image.test.ts +131 -21
  55. package/src/__tests__/memory-retrieval-hook.test.ts +94 -2
  56. package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
  57. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -3
  58. package/src/__tests__/proxy-approval-callback.test.ts +1 -0
  59. package/src/__tests__/qdrant-manager.test.ts +14 -1
  60. package/src/__tests__/run-due-schedules.test.ts +21 -0
  61. package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
  62. package/src/__tests__/schedule-routes.test.ts +23 -0
  63. package/src/__tests__/schedule-store.test.ts +17 -0
  64. package/src/__tests__/secret-ingress-http.test.ts +1 -1
  65. package/src/__tests__/send-endpoint-busy.test.ts +3 -3
  66. package/src/__tests__/starter-task-flow.test.ts +5 -4
  67. package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
  68. package/src/__tests__/subagent-spawn-and-await.test.ts +4 -7
  69. package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
  70. package/src/__tests__/subagent-tools.test.ts +81 -101
  71. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
  72. package/src/__tests__/tool-executor.test.ts +5 -1
  73. package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
  74. package/src/__tests__/ui-visual-surface.test.ts +1 -1
  75. package/src/__tests__/ui-voice-picker-surface.test.ts +1 -1
  76. package/src/__tests__/ui-work-result-surface.test.ts +1 -1
  77. package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
  78. package/src/__tests__/voice-session-bridge.test.ts +85 -29
  79. package/src/acp/session-manager.ts +8 -1
  80. package/src/api/events/host-file.ts +2 -2
  81. package/src/api/surfaces.ts +5 -0
  82. package/src/calls/__tests__/voice-session-bridge.test.ts +21 -10
  83. package/src/calls/__tests__/voice-triage-escalate.test.ts +8 -0
  84. package/src/calls/voice-session-bridge.ts +44 -25
  85. package/src/calls/voice-triage-escalate.ts +1 -0
  86. package/src/cli/bundled-modules.ts +29 -0
  87. package/src/cli/commands/db/repair.ts +4 -8
  88. package/src/cli/commands/domain.ts +6 -3
  89. package/src/cli/commands/email.ts +6 -3
  90. package/src/cli/commands/keys.ts +8 -3
  91. package/src/cli/commands/plugins.ts +85 -36
  92. package/src/cli/commands/schedules.ts +35 -1
  93. package/src/cli/lib/bundled-marketplace.json +1 -1
  94. package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
  95. package/src/config/assistant-feature-flags.ts +36 -15
  96. package/src/config/balanced-model-experiment.ts +35 -0
  97. package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
  98. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
  99. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
  100. package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
  101. package/src/config/bundled-skills/subagent/SKILL.md +17 -12
  102. package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
  103. package/src/config/call-site-defaults.ts +7 -0
  104. package/src/config/default-profile-catalog.ts +96 -4
  105. package/src/config/feature-flag-registry.json +11 -11
  106. package/src/config/skills.ts +9 -2
  107. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  108. package/src/daemon/conversation-agent-loop.ts +14 -13
  109. package/src/daemon/conversation-notifiers.ts +11 -9
  110. package/src/daemon/conversation-process.ts +0 -27
  111. package/src/daemon/conversation-runtime-assembly.ts +9 -2
  112. package/src/daemon/conversation-store.ts +4 -4
  113. package/src/daemon/conversation-surfaces.ts +27 -13
  114. package/src/daemon/conversation-tool-setup.ts +2 -4
  115. package/src/daemon/conversation.ts +104 -51
  116. package/src/daemon/doordash-steps.ts +2 -2
  117. package/src/daemon/lifecycle.ts +14 -1
  118. package/src/daemon/process-message.ts +0 -13
  119. package/src/daemon/windows-compiled-entry.ts +4 -0
  120. package/src/documents/document-store.ts +5 -235
  121. package/src/hooks/types.ts +5 -0
  122. package/src/ipc/gateway-flag-listener.ts +17 -3
  123. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +118 -0
  124. package/src/live-voice/live-voice-manager.ts +16 -3
  125. package/src/live-voice/live-voice-session.ts +63 -9
  126. package/src/live-voice/windows-compiled-live-voice.ts +4 -0
  127. package/src/monitoring/control.ts +1 -0
  128. package/src/monitoring/db-integrity-sample.ts +4 -5
  129. package/src/notifications/AGENTS.md +2 -0
  130. package/src/notifications/approval-card-data.ts +33 -0
  131. package/src/permissions/prompter.ts +1 -5
  132. package/src/persistence/conversation-queries.ts +66 -16
  133. package/src/persistence/embeddings/qdrant-manager.ts +84 -49
  134. package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
  135. package/src/persistence/schema/documents.ts +4 -4
  136. package/src/plugin-api/constants.ts +8 -0
  137. package/src/plugin-api/index.ts +5 -1
  138. package/src/plugin-api/webhook-url.ts +13 -11
  139. package/src/plugins/defaults/main.ts +6 -7
  140. package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
  141. package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
  142. package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
  143. package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
  144. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
  145. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
  146. package/src/plugins/defaults/memory/v3/injector.ts +8 -0
  147. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
  148. package/src/plugins/defaults/memory/worker-control.ts +1 -0
  149. package/src/plugins/defaults/worker-entrypoints.ts +3 -0
  150. package/src/plugins/mtime-cache.ts +17 -0
  151. package/src/prompts/templates/system-sections.ts +0 -7
  152. package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
  153. package/src/providers/__tests__/retry-callsite.test.ts +20 -0
  154. package/src/providers/openai/chat-completions-provider.ts +11 -1
  155. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +11 -6
  156. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +9 -49
  157. package/src/routes/control.ts +1 -0
  158. package/src/routes/route-host-client.ts +1 -0
  159. package/src/runtime/AGENTS.md +16 -17
  160. package/src/runtime/agent-wake.ts +15 -12
  161. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +170 -1
  162. package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
  163. package/src/runtime/routes/conversation-list-routes.ts +54 -22
  164. package/src/runtime/routes/conversation-management-routes.ts +2 -3
  165. package/src/runtime/routes/conversation-routes.ts +11 -13
  166. package/src/runtime/routes/documents-routes.ts +3 -222
  167. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
  168. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
  169. package/src/runtime/routes/playground/inject-failures.ts +2 -2
  170. package/src/runtime/routes/playground/reset-circuit.ts +1 -1
  171. package/src/runtime/routes/schedule-routes.ts +94 -7
  172. package/src/runtime/routes/workspace-routes.ts +0 -9
  173. package/src/runtime/routes/workspace-utils.ts +3 -13
  174. package/src/runtime/services/conversation-serializer.ts +7 -2
  175. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
  176. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
  177. package/src/schedule/plugin-schedule-availability.ts +58 -0
  178. package/src/schedule/plugin-schedule-declarations.ts +23 -27
  179. package/src/schedule/plugin-schedule-reconciler.ts +12 -3
  180. package/src/schedule/schedule-store.ts +5 -1
  181. package/src/schedule/scheduler.ts +9 -4
  182. package/src/schedule/worker-control.ts +1 -0
  183. package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
  184. package/src/subagent/consult-context.ts +11 -11
  185. package/src/subagent/consult-prompt.ts +26 -35
  186. package/src/subagent/manager.ts +20 -37
  187. package/src/subagent/notify.ts +7 -1
  188. package/src/subagent/types.ts +15 -13
  189. package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
  190. package/src/tools/acp/spawn.ts +6 -4
  191. package/src/tools/filesystem/read.ts +27 -10
  192. package/src/tools/host-filesystem/read.ts +27 -15
  193. package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
  194. package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
  195. package/src/tools/shared/filesystem/types.ts +5 -5
  196. package/src/tools/skills/scaffold-managed.ts +25 -7
  197. package/src/tools/subagent/spawn.ts +28 -88
  198. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  199. package/src/util/__tests__/worker-process-command.test.ts +37 -0
  200. package/src/util/logger.ts +16 -0
  201. package/src/util/worker-process.ts +37 -4
  202. package/src/windows-compiled-cli.ts +32 -0
  203. package/src/windows-compiled-entry.ts +4 -0
  204. package/src/windows-compiled-logger.ts +6 -0
  205. package/src/windows-compiled-worker-entry.ts +29 -0
  206. package/src/__tests__/document-workspace-file.test.ts +0 -467
  207. package/src/daemon/interactive-turn-sender.ts +0 -59
  208. package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
  209. package/src/subagent/consult-transcript.ts +0 -90
@@ -1,59 +0,0 @@
1
- /**
2
- * The interactive-turn sender contract, in one place.
3
- *
4
- * `Conversation.sendToClient` is conversation-level mutable state: born a
5
- * no-op, bound to the SSE hub for the duration of an interactive turn, and
6
- * restored when that turn ends. Some subsystems read this conversation-level
7
- * sender instead of the turn's own `onEvent` sink; the `PermissionPrompter`
8
- * is the load-bearing one, so a `confirmation_request` raised by a turn that
9
- * never bound the sender is emitted into the no-op, reaches no client, and
10
- * hangs until the permission timeout auto-denies. Every path that runs an
11
- * interactive agent turn MUST therefore bind before the loop and restore
12
- * after it. The queue drain bypasses `processMessage`, so it uses this
13
- * helper directly.
14
- *
15
- * The restore is a snapshot of the sender state taken at bind time, not an
16
- * unconditional reset to a no-op: a live binding that predated the turn
17
- * (e.g. installed by the send route while a non-interactive turn was
18
- * running) survives the turn, so out-of-turn producers that read the live
19
- * sender (call transcript and completion notifiers) keep their client. In
20
- * the ordinary interactive flow the snapshot is the no-op the previous
21
- * turn's restore left behind, so restoring it is identical to the reset
22
- * this contract has always performed. The restore is also identity-guarded:
23
- * it only replaces the hub binding this contract installed, so a sender
24
- * some other subsystem rebound mid-turn to anything else is left alone.
25
- *
26
- * TRANSITIONAL: this helper is the paved road for a pattern we want less
27
- * of. Bind/restore of shared mutable sender state is why the drain, the
28
- * compaction card, and the host-browser proxy have each needed their own
29
- * restore step. The durable design is a per-turn sink threaded to the
30
- * prompter and the other conversation-level-sender readers, at which point
31
- * this module and every call site of it should be deleted. Prefer that
32
- * refactor over adding new bind/restore call sites.
33
- */
34
-
35
- import { broadcastMessage } from "../runtime/assistant-event-hub.js";
36
- import { getSubagentManager } from "../subagent/index.js";
37
- import type { Conversation } from "./conversation.js";
38
-
39
- /**
40
- * Point the conversation-level sender (and the subagent parent sender) at
41
- * the SSE hub for the duration of an interactive turn. Returns the restore
42
- * closure the turn's cleanup must invoke.
43
- */
44
- export function bindInteractiveTurnSender(
45
- conversation: Conversation,
46
- ): () => void {
47
- const previousSender = conversation.getCurrentSender();
48
- const previousHasNoClient = conversation.hasNoClient;
49
- conversation.updateClient(broadcastMessage, false);
50
- getSubagentManager().updateParentSender(
51
- conversation.conversationId,
52
- broadcastMessage,
53
- );
54
- return () => {
55
- if (conversation.getCurrentSender() === broadcastMessage) {
56
- conversation.updateClient(previousSender, previousHasNoClient);
57
- }
58
- };
59
- }
@@ -1,184 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import type { ContentBlock, Message } from "../../providers/types.js";
4
- import { sanitizeConsultTranscript } from "../consult-transcript.js";
5
-
6
- const text = (t: string): ContentBlock => ({ type: "text", text: t });
7
-
8
- describe("sanitizeConsultTranscript", () => {
9
- test("drops thinking and redacted-thinking blocks", () => {
10
- const messages: Message[] = [
11
- { role: "user", content: [text("do the task")] },
12
- {
13
- role: "assistant",
14
- content: [
15
- { type: "thinking", thinking: "secret", signature: "sig" },
16
- { type: "redacted_thinking", data: "blob" },
17
- text("here is my answer"),
18
- ],
19
- },
20
- ];
21
- const out = sanitizeConsultTranscript(messages);
22
- expect(out).toHaveLength(2);
23
- expect(out[1].content).toEqual([text("here is my answer")]);
24
- });
25
-
26
- test("drops a file block", () => {
27
- const messages: Message[] = [
28
- {
29
- role: "user",
30
- content: [
31
- text("read this"),
32
- {
33
- type: "file",
34
- source: {
35
- type: "base64",
36
- media_type: "application/pdf",
37
- data: "z",
38
- filename: "f.pdf",
39
- },
40
- },
41
- ],
42
- },
43
- ];
44
- const out = sanitizeConsultTranscript(messages);
45
- expect(out[0].content).toEqual([text("read this")]);
46
- });
47
-
48
- test("strips the pending tool_use from the final assistant turn", () => {
49
- const messages: Message[] = [
50
- { role: "user", content: [text("task")] },
51
- {
52
- role: "assistant",
53
- content: [
54
- text("let me consult the advisor"),
55
- { type: "tool_use", id: "t1", name: "advisor", input: {} },
56
- ],
57
- },
58
- ];
59
- const out = sanitizeConsultTranscript(messages);
60
- expect(out[1].content).toEqual([text("let me consult the advisor")]);
61
- });
62
-
63
- test("preserves completed client tool_use / tool_result pairs in earlier turns", () => {
64
- const messages: Message[] = [
65
- { role: "user", content: [text("task")] },
66
- {
67
- role: "assistant",
68
- content: [{ type: "tool_use", id: "a", name: "bash", input: {} }],
69
- },
70
- {
71
- role: "user",
72
- content: [{ type: "tool_result", tool_use_id: "a", content: "output" }],
73
- },
74
- { role: "assistant", content: [text("done")] },
75
- ];
76
- const out = sanitizeConsultTranscript(messages);
77
- expect(out[1].content[0]).toEqual({
78
- type: "tool_use",
79
- id: "a",
80
- name: "bash",
81
- input: {},
82
- });
83
- expect(out[2].content[0]).toEqual({
84
- type: "tool_result",
85
- tool_use_id: "a",
86
- content: "output",
87
- });
88
- });
89
-
90
- test("drops a web-search server_tool_use AND its result together — no orphan", () => {
91
- const messages: Message[] = [
92
- { role: "user", content: [text("look it up")] },
93
- {
94
- role: "assistant",
95
- content: [
96
- text("searching"),
97
- { type: "server_tool_use", id: "ws1", name: "web_search", input: {} },
98
- ],
99
- },
100
- {
101
- role: "user",
102
- content: [
103
- {
104
- type: "web_search_tool_result",
105
- tool_use_id: "ws1",
106
- content: [{ title: "x", url: "y" }],
107
- },
108
- ],
109
- },
110
- { role: "assistant", content: [text("found it; here is the answer")] },
111
- ];
112
- const out = sanitizeConsultTranscript(messages);
113
- const flat = out.flatMap((m) => m.content);
114
- expect(flat.some((b) => b.type === "server_tool_use")).toBe(false);
115
- expect(flat.some((b) => b.type === "web_search_tool_result")).toBe(false);
116
- expect(flat).toContainEqual(text("searching"));
117
- expect(flat).toContainEqual(text("found it; here is the answer"));
118
- });
119
-
120
- test("keeps top-level image blocks", () => {
121
- const img: ContentBlock = {
122
- type: "image",
123
- source: { type: "base64", media_type: "image/png", data: "x" },
124
- };
125
- const messages: Message[] = [
126
- { role: "user", content: [text("look at this"), img] },
127
- ];
128
- const out = sanitizeConsultTranscript(messages);
129
- expect(out[0].content).toEqual([text("look at this"), img]);
130
- });
131
-
132
- test("keeps image contentBlocks on a tool_result, dropping disallowed ones", () => {
133
- const img: ContentBlock = {
134
- type: "image",
135
- source: { type: "base64", media_type: "image/png", data: "x" },
136
- };
137
- const messages: Message[] = [
138
- {
139
- role: "user",
140
- content: [
141
- {
142
- type: "tool_result",
143
- tool_use_id: "a",
144
- content: "screenshot",
145
- contentBlocks: [
146
- img,
147
- {
148
- type: "file",
149
- source: {
150
- type: "base64",
151
- media_type: "application/pdf",
152
- data: "z",
153
- filename: "f.pdf",
154
- },
155
- },
156
- ],
157
- },
158
- ],
159
- },
160
- ];
161
- const out = sanitizeConsultTranscript(messages);
162
- expect(out[0].content[0]).toEqual({
163
- type: "tool_result",
164
- tool_use_id: "a",
165
- content: "screenshot",
166
- contentBlocks: [img],
167
- });
168
- });
169
-
170
- test("drops messages that are empty after sanitization", () => {
171
- const messages: Message[] = [
172
- { role: "user", content: [text("task")] },
173
- {
174
- role: "assistant",
175
- content: [{ type: "thinking", thinking: "secret", signature: "sig" }],
176
- },
177
- { role: "assistant", content: [text("answer")] },
178
- ];
179
- const out = sanitizeConsultTranscript(messages);
180
- expect(out).toHaveLength(2);
181
- expect(out[0].content).toEqual([text("task")]);
182
- expect(out[1].content).toEqual([text("answer")]);
183
- });
184
- });
@@ -1,90 +0,0 @@
1
- /**
2
- * Sanitize an inherited parent transcript before it is injected into a
3
- * advisor consult subagent.
4
- *
5
- * `Conversation.injectInheritedContext` injects the parent's messages VERBATIM
6
- * with no sanitization, so the advisor path must run this over the parent
7
- * messages before they are injected.
8
- *
9
- * Strips blocks the advisor shouldn't (or can't) replay:
10
- * - thinking / redacted-thinking (the advisor tool drops thinking),
11
- * - files,
12
- * - `server_tool_use` AND `web_search_tool_result` — provider-side tool calls
13
- * (e.g. web search) and their results are dropped *together*. Dropping the
14
- * result without its paired `server_tool_use` would leave an orphaned call
15
- * block the provider rejects, so any consult after prior web-search history
16
- * would fail; dropping both keeps the sequence valid.
17
- *
18
- * Images are preserved — top-level and nested inside `tool_result.contentBlocks`
19
- * (which are recursively sanitized) — so the advisor sees what the executor saw.
20
- * Visual tasks depend on it; the advisor profile is expected to be vision-capable.
21
- *
22
- * It also strips the *pending* client tool calls from the final assistant turn:
23
- * at capture time the last assistant message can carry a `tool_use` with no
24
- * matching `tool_result` yet, so sending it would be a dangling call. Earlier,
25
- * completed `tool_use` / `tool_result` pairs are preserved intact.
26
- *
27
- * In-flight-turn parity — ensuring the assistant's just-written plan from the
28
- * current turn is included while its dangling tool_use is stripped — is handled
29
- * by the consumer (the wiring that calls this before injection); this function
30
- * only strips a dangling `tool_use` IF it appears on the final assistant turn.
31
- */
32
-
33
- import type { ContentBlock, Message } from "../providers/types.js";
34
-
35
- /** Drop disallowed blocks; recursively sanitize tool_result content. `null` = drop. */
36
- function sanitize(block: ContentBlock): ContentBlock | null {
37
- switch (block.type) {
38
- case "thinking":
39
- case "redacted_thinking":
40
- case "file":
41
- case "server_tool_use":
42
- case "web_search_tool_result":
43
- return null;
44
- case "tool_result": {
45
- if (!block.contentBlocks) {
46
- return block;
47
- }
48
- // Keep images (and other allowed blocks) nested in the tool result; drop
49
- // the disallowed ones.
50
- const contentBlocks = block.contentBlocks
51
- .map(sanitize)
52
- .filter((b): b is ContentBlock => b !== null);
53
- return {
54
- ...block,
55
- contentBlocks: contentBlocks.length > 0 ? contentBlocks : undefined,
56
- };
57
- }
58
- default:
59
- // text, image, tool_use — kept.
60
- return block;
61
- }
62
- }
63
-
64
- export function sanitizeConsultTranscript(
65
- messages: ReadonlyArray<Message>,
66
- ): Message[] {
67
- const out: Message[] = [];
68
- const lastIndex = messages.length - 1;
69
-
70
- messages.forEach((message, index) => {
71
- let content = message.content
72
- .map(sanitize)
73
- .filter((b): b is ContentBlock => b !== null);
74
-
75
- // The final assistant turn's client tool calls have no results yet — drop
76
- // them so we never send a dangling tool_use. (server_tool_use is already
77
- // dropped above.)
78
- if (index === lastIndex && message.role === "assistant") {
79
- content = content.filter(
80
- (b) => b.type !== "tool_use" && b.type !== "server_tool_use",
81
- );
82
- }
83
-
84
- if (content.length > 0) {
85
- out.push({ role: message.role, content });
86
- }
87
- });
88
-
89
- return out;
90
- }