@vellumai/assistant 0.12.2-staging.7 → 0.12.2

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 (120) hide show
  1. package/docs/architecture/memory.md +2 -11
  2. package/docs/desktop-browser-cli.md +2 -4
  3. package/node_modules/@vellumai/environments/src/shell.test.ts +0 -21
  4. package/node_modules/@vellumai/environments/src/shell.ts +0 -24
  5. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +2 -8
  6. package/openapi.yaml +2 -6
  7. package/package.json +1 -1
  8. package/scripts/smoke-desktop-browser-cli.ts +0 -1
  9. package/src/__tests__/agent-loop.test.ts +0 -124
  10. package/src/__tests__/approval-interception-trust-gates.test.ts +0 -40
  11. package/src/__tests__/channel-approval.test.ts +14 -9
  12. package/src/__tests__/conversation-agent-loop.test.ts +0 -25
  13. package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -0
  14. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -138
  15. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  16. package/src/__tests__/subagent-tool-gate-mode.test.ts +0 -169
  17. package/src/__tests__/terminal-tools.test.ts +0 -8
  18. package/src/__tests__/unicode.test.ts +0 -36
  19. package/src/agent/loop.ts +0 -19
  20. package/src/api/index.ts +0 -6
  21. package/src/approvals/approval-primitive.ts +2 -5
  22. package/src/approvals/scoped-approval-grants.ts +2 -6
  23. package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -43
  24. package/src/daemon/conversation-agent-loop.ts +0 -2
  25. package/src/daemon/conversation-tool-setup.ts +1 -57
  26. package/src/daemon/conversation.ts +0 -17
  27. package/src/daemon/daemon-control.ts +6 -2
  28. package/src/daemon/orphan-reaper.ts +3 -4
  29. package/src/daemon/tool-setup-types.ts +0 -6
  30. package/src/daemon/wake-conversation-ops.ts +15 -38
  31. package/src/desktop/desktop-automation-lease.test.ts +0 -143
  32. package/src/desktop/desktop-automation-lease.ts +3 -39
  33. package/src/messaging/provider-message-metadata.ts +3 -3
  34. package/src/notifications/__tests__/copy-composer.test.ts +0 -70
  35. package/src/notifications/copy-composer.ts +3 -11
  36. package/src/oauth/seed-providers.ts +30 -0
  37. package/src/persistence/conversation-plugin-facade.ts +0 -13
  38. package/src/persistence/schema/index.ts +0 -1
  39. package/src/persistence/steps.ts +0 -2
  40. package/src/plugin-api/conversation-turn.ts +7 -31
  41. package/src/plugin-api/index.ts +1 -9
  42. package/src/plugins/defaults/memory/AGENTS.md +2 -14
  43. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +0 -43
  44. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +0 -46
  45. package/src/plugins/defaults/memory/buffer-format.ts +0 -40
  46. package/src/plugins/defaults/memory/context-search/agent-runner.ts +2 -1
  47. package/src/plugins/defaults/memory/context-search/format.ts +1 -2
  48. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +1 -2
  49. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -2
  50. package/src/plugins/defaults/memory/graph/capability-seed.ts +2 -1
  51. package/src/plugins/defaults/memory/graph/tool-handlers.ts +42 -1
  52. package/src/plugins/defaults/memory/host-utils.ts +10 -0
  53. package/src/plugins/defaults/memory/injectors.ts +3 -4
  54. package/src/plugins/defaults/memory/memory-retrospective-job.ts +181 -55
  55. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +99 -407
  56. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +0 -10
  57. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +7 -107
  58. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +86 -307
  59. package/src/plugins/defaults/memory/substrate/page-index.ts +1 -2
  60. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +49 -89
  61. package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
  62. package/src/plugins/defaults/memory/tools.ts +1 -1
  63. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
  64. package/src/plugins/defaults/memory/v1/graph/extraction.ts +1 -2
  65. package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
  66. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +0 -5
  67. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +2 -5
  68. package/src/plugins/defaults/memory/v2/reranker.ts +1 -2
  69. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +1 -81
  70. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +0 -87
  71. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +0 -21
  72. package/src/plugins/defaults/memory/v3/card.ts +1 -2
  73. package/src/plugins/defaults/memory/v3/injector.ts +178 -212
  74. package/src/plugins/defaults/memory/v3/orchestrate.ts +22 -86
  75. package/src/plugins/defaults/memory/v3/pool-select.ts +7 -10
  76. package/src/plugins/defaults/memory/v3/sections.ts +1 -2
  77. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +1 -10
  78. package/src/plugins/defaults/tool-result-truncate/terminal.ts +46 -1
  79. package/src/runtime/AGENTS.md +1 -1
  80. package/src/runtime/__tests__/agent-wake.test.ts +1 -86
  81. package/src/runtime/agent-wake.ts +4 -20
  82. package/src/runtime/guardian-action-service.ts +17 -2
  83. package/src/runtime/guardian-reply-router.ts +8 -1
  84. package/src/runtime/routes/channel-route-shared.ts +9 -1
  85. package/src/runtime/routes/desktop-setup-routes.test.ts +2 -2
  86. package/src/runtime/routes/desktop-setup-routes.ts +3 -7
  87. package/src/runtime/routes/guardian-approval-interception.ts +0 -24
  88. package/src/runtime/routes/inbound-message-handler.ts +3 -2
  89. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +1 -1
  90. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -7
  91. package/src/schedule/run-script.ts +2 -2
  92. package/src/tools/host-terminal/host-shell.ts +6 -12
  93. package/src/tools/shared/filesystem/file-ops-service.ts +31 -1
  94. package/src/tools/shared/shell-output.test.ts +0 -10
  95. package/src/tools/shared/shell-output.ts +2 -14
  96. package/src/tools/skills/sandbox-runner.ts +2 -13
  97. package/src/tools/skills/scaffold-managed.ts +1 -2
  98. package/src/tools/terminal/__tests__/safe-env.test.ts +0 -29
  99. package/src/tools/terminal/safe-env.ts +1 -30
  100. package/src/tools/terminal/sanitized-bash.ts +2 -15
  101. package/src/tools/terminal/shell.test.ts +0 -29
  102. package/src/tools/terminal/shell.ts +7 -13
  103. package/src/util/host-process.test.ts +1 -17
  104. package/src/util/host-process.ts +0 -24
  105. package/src/util/unicode.ts +0 -29
  106. package/src/__tests__/db-conversation-tool-surface.test.ts +0 -144
  107. package/src/api/events/desktop-activity-changed.ts +0 -10
  108. package/src/persistence/conversation-tool-surface.ts +0 -86
  109. package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +0 -78
  110. package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +0 -29
  111. package/src/persistence/schema/conversation-tool-surfaces.ts +0 -22
  112. package/src/plugin-api/plugin-channel-turn-trust.test.ts +0 -133
  113. package/src/plugin-api/plugin-channel-turn-trust.ts +0 -71
  114. package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +0 -320
  115. package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +0 -17
  116. package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +0 -161
  117. package/src/plugins/defaults/memory/buffer-file.ts +0 -354
  118. package/src/plugins/defaults/memory/memory-run-evidence.ts +0 -213
  119. package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +0 -34
  120. package/src/tools/terminal/shell-launch.test.ts +0 -162
@@ -66,34 +66,6 @@ mock.module("../daemon/conversation-skill-tools.js", () => ({
66
66
  })),
67
67
  }));
68
68
 
69
- // Records every wire tool surface the resolver persists (conversation id +
70
- // tool names + the hash it already knew), and lets a test make the write fail.
71
- let recordedSurfaces: Array<{
72
- conversationId: string;
73
- toolNames: string[];
74
- knownHash: string | undefined;
75
- }> = [];
76
- let recordSurfaceThrows: Error | null = null;
77
- mock.module("../persistence/conversation-tool-surface.js", () => ({
78
- recordConversationToolSurface: (
79
- conversationId: string,
80
- tools: ToolDefinition[],
81
- knownHash?: string,
82
- ) => {
83
- recordedSurfaces.push({
84
- conversationId,
85
- toolNames: tools.map((t) => t.name),
86
- knownHash,
87
- });
88
- if (recordSurfaceThrows) {
89
- throw recordSurfaceThrows;
90
- }
91
- return `hash:${tools.map((t) => t.name).join(",")}`;
92
- },
93
- hashConversationToolSurface: (tools: ToolDefinition[]) =>
94
- `hash:${tools.map((t) => t.name).join(",")}`,
95
- }));
96
-
97
69
  // ---------------------------------------------------------------------------
98
70
  // Imports after mocks are in place
99
71
  // ---------------------------------------------------------------------------
@@ -103,7 +75,6 @@ import { createSurfaceMutex } from "../daemon/conversation-surfaces.js";
103
75
  import {
104
76
  createResolveToolsCallback,
105
77
  createToolExecutor,
106
- createWireToolSurfaceRecorder,
107
78
  isRefusedInReadOnlyPass,
108
79
  type SubagentToolStats,
109
80
  } from "../daemon/conversation-tool-setup.js";
@@ -1146,143 +1117,3 @@ describe("createResolveToolsCallback — read-only hides dynamic MCP tools", ()
1146
1117
  expect(resolve(EMPTY_HISTORY).map((t) => t.name)).toContain("srv_send");
1147
1118
  });
1148
1119
  });
1149
-
1150
- // ---------------------------------------------------------------------------
1151
- // Resolver: wire tool surface record + replay (fork cache parity)
1152
- // ---------------------------------------------------------------------------
1153
-
1154
- describe("createResolveToolsCallback: wire tool surface record and replay", () => {
1155
- beforeEach(() => {
1156
- recordedSurfaces = [];
1157
- recordSurfaceThrows = null;
1158
- projectedSkillToolNames = [];
1159
- });
1160
-
1161
- test("the resolver itself never records: out-of-band callers read it too", () => {
1162
- const toolDefs = [makeToolDef("remember"), makeToolDef("tool_b")];
1163
- const ctx = makeProjectionCtx({ conversationId: "conv-live" });
1164
- const resolve = createResolveToolsCallback(toolDefs, ctx)!;
1165
-
1166
- // The `/compact` token count resolves outside any turn, where presence
1167
- // reads clientless; recording here would overwrite the sent surface.
1168
- resolve(EMPTY_HISTORY);
1169
-
1170
- expect(recordedSurfaces).toEqual([]);
1171
- expect(ctx.recordedToolSurfaceHash).toBeUndefined();
1172
- });
1173
-
1174
- test("the send-boundary recorder records the sent array, keyed by conversation, and remembers its hash", () => {
1175
- const ctx = makeProjectionCtx({ conversationId: "conv-live" });
1176
- const record = createWireToolSurfaceRecorder(ctx);
1177
- const sent = [makeToolDef("remember"), makeToolDef("web_search")];
1178
-
1179
- record(sent);
1180
- record(sent);
1181
-
1182
- // Every provider call records; the persistence layer dedupes on the hash
1183
- // handed back from the previous call.
1184
- expect(recordedSurfaces).toEqual([
1185
- {
1186
- conversationId: "conv-live",
1187
- toolNames: ["remember", "web_search"],
1188
- knownHash: undefined,
1189
- },
1190
- {
1191
- conversationId: "conv-live",
1192
- toolNames: ["remember", "web_search"],
1193
- knownHash: "hash:remember,web_search",
1194
- },
1195
- ]);
1196
- expect(ctx.recordedToolSurfaceHash).toBe("hash:remember,web_search");
1197
- });
1198
-
1199
- test("a failed record is swallowed and still counts as recorded", () => {
1200
- recordSurfaceThrows = new Error("no such table");
1201
- const record = createWireToolSurfaceRecorder(
1202
- makeProjectionCtx({ conversationId: "conv-live" }),
1203
- );
1204
-
1205
- expect(() => record([makeToolDef("remember")])).not.toThrow();
1206
- record([makeToolDef("remember")]);
1207
-
1208
- // The second call carries the hash from the failed first one, so a
1209
- // persistent failure is retried once per distinct surface, not per call.
1210
- expect(recordedSurfaces.map((r) => r.knownHash)).toEqual([
1211
- undefined,
1212
- "hash:remember",
1213
- ]);
1214
- });
1215
-
1216
- test("the recorder skips arrays that are not the conversation's own surface", () => {
1217
- const tools = [makeToolDef("remember")];
1218
-
1219
- // A replaying wake sends its source's array.
1220
- createWireToolSurfaceRecorder(
1221
- makeProjectionCtx({ conversationId: "conv-fork", wireToolReplay: tools }),
1222
- )(tools);
1223
- // A tools-disabled call sends nothing; a fork replaying [] could never
1224
- // call remember.
1225
- createWireToolSurfaceRecorder(
1226
- makeProjectionCtx({ conversationId: "conv-live" }),
1227
- )([]);
1228
- // Disk-pressure cleanup narrows the wire to cleanup tools.
1229
- createWireToolSurfaceRecorder(
1230
- makeProjectionCtx({
1231
- conversationId: "conv-live",
1232
- diskPressureCleanupModeActive: true,
1233
- }),
1234
- )(tools);
1235
-
1236
- expect(recordedSurfaces).toEqual([]);
1237
- });
1238
-
1239
- test("replays wireToolReplay verbatim on the wire while execution still derives from the fork's own context", () => {
1240
- projectedSkillToolNames = ["scaffold_managed_skill"];
1241
- const replay: ToolDefinition[] = [
1242
- makeToolDef("bash"),
1243
- makeToolDef("remember"),
1244
- makeToolDef("bell_jingle"),
1245
- {
1246
- type: "web_search_20250305",
1247
- name: "web_search",
1248
- max_uses: 5,
1249
- } as unknown as ToolDefinition,
1250
- ];
1251
- // The fork's own registry lacks bell_jingle (a user-plugin tool the
1252
- // worker never loads) and web_search (a server tool the loop appends).
1253
- const toolDefs = [makeToolDef("remember"), makeToolDef("bash")];
1254
- const ctx = makeProjectionCtx({
1255
- conversationId: "conv-fork",
1256
- subagentAllowedTools: new Set(["remember"]),
1257
- subagentToolGateMode: "execution",
1258
- wireToolReplay: replay,
1259
- });
1260
- const resolve = createResolveToolsCallback(toolDefs, ctx)!;
1261
-
1262
- const tools = resolve(EMPTY_HISTORY);
1263
-
1264
- // Byte-identical to the source's recorded array, in the source's order,
1265
- // including definitions this process could never resolve.
1266
- expect(JSON.stringify(tools)).toBe(JSON.stringify(replay));
1267
- // Execution-side inventory is still the fork's own derivation (plus the
1268
- // preactivated skill tools), never widened by the replayed definitions.
1269
- expect(ctx.allowedToolNames).toEqual(
1270
- new Set(["remember", "bash", "scaffold_managed_skill"]),
1271
- );
1272
- });
1273
-
1274
- test("replay returns a fresh array each call so the loop cannot mutate the recorded one", () => {
1275
- const replay = [makeToolDef("remember")];
1276
- const ctx = makeProjectionCtx({
1277
- conversationId: "conv-fork",
1278
- wireToolReplay: replay,
1279
- });
1280
- const resolve = createResolveToolsCallback([makeToolDef("remember")], ctx)!;
1281
-
1282
- const first = resolve(EMPTY_HISTORY);
1283
- first.push(makeToolDef("injected"));
1284
-
1285
- expect(resolve(EMPTY_HISTORY).map((t) => t.name)).toEqual(["remember"]);
1286
- expect(replay.map((t) => t.name)).toEqual(["remember"]);
1287
- });
1288
- });
@@ -44,7 +44,6 @@ import {
44
44
  formatShellOutput,
45
45
  MAX_OUTPUT_LENGTH,
46
46
  OUTPUT_TRUNCATED_TAG,
47
- SHELL_DID_NOT_START_MESSAGE,
48
47
  } from "../tools/shared/shell-output.js";
49
48
  import {
50
49
  ALWAYS_INJECTED_ENV_VARS,
@@ -335,13 +334,6 @@ describe("formatShellOutput", () => {
335
334
  expect(result.isError).toBe(false);
336
335
  });
337
336
 
338
- test("a close with exit 0 and empty pipes is an error when the process never started", () => {
339
- const result = formatShellOutput("", "", 0, false, 120, { started: false });
340
- expect(result.content).toBe(SHELL_DID_NOT_START_MESSAGE);
341
- expect(result.isError).toBe(true);
342
- expect(result.content).not.toBe("<command_completed />");
343
- });
344
-
345
337
  test("failed command with no output shows exit code tag and descriptive message", () => {
346
338
  const result = formatShellOutput("", "", 1, false, 120);
347
339
  expect(result.content).toContain('<command_exit code="1" />');
@@ -4,7 +4,6 @@ import {
4
4
  safeStringSlice,
5
5
  stripOrphanedSurrogates,
6
6
  stripOrphanedSurrogatesDeep,
7
- surrogateSafeWindow,
8
7
  } from "../util/unicode.js";
9
8
 
10
9
  // U+1F389 PARTY POPPER = "\uD83C\uDF89" (a surrogate pair).
@@ -303,38 +302,3 @@ describe("stripOrphanedSurrogatesDeep", () => {
303
302
  expect(() => JSON.parse(json)).not.toThrow();
304
303
  });
305
304
  });
306
-
307
- describe("surrogateSafeWindow", () => {
308
- const codeAt = (text: string) => (i: number) => text.charCodeAt(i);
309
-
310
- test("a start that lands on a low surrogate backs up to include the pair", () => {
311
- const text = `a${EMOJI}b`;
312
- expect(surrogateSafeWindow(text.length, codeAt(text), 2, 10)).toEqual({
313
- start: 1,
314
- end: 4,
315
- });
316
- });
317
-
318
- test("an end that would cut a pair backs off one unit", () => {
319
- const text = `ab${EMOJI}cd`;
320
- expect(surrogateSafeWindow(text.length, codeAt(text), 0, 3)).toEqual({
321
- start: 0,
322
- end: 2,
323
- });
324
- });
325
-
326
- test("a one-unit window on a pair takes the whole pair so paging advances", () => {
327
- const text = `${EMOJI}x`;
328
- expect(surrogateSafeWindow(text.length, codeAt(text), 0, 1)).toEqual({
329
- start: 0,
330
- end: 2,
331
- });
332
- });
333
-
334
- test("clamps to the text bounds", () => {
335
- expect(surrogateSafeWindow(3, codeAt("abc"), 10, 5)).toEqual({
336
- start: 3,
337
- end: 3,
338
- });
339
- });
340
- });
package/src/agent/loop.ts CHANGED
@@ -1047,17 +1047,6 @@ export interface AgentLoopConstructorOptions {
1047
1047
  tools?: ToolDefinition[];
1048
1048
  toolExecutor?: LoopToolExecutor;
1049
1049
  resolveTools?: (history: Message[]) => ToolDefinition[];
1050
- /**
1051
- * Observer for the final tool array of each provider call, invoked
1052
- * immediately before the request leaves with exactly what goes on the wire
1053
- * (after any provider-native tool is appended, past the inter-call throttle
1054
- * and the pre-model hooks, and not at all once the run is aborted). This is
1055
- * the only point that sees the sent array: the dynamic `resolveTools`
1056
- * callback is also consulted out of band (token counting, compaction
1057
- * estimates), so a consumer that needs "what the last request sent"
1058
- * subscribes here rather than wrapping the resolver. Must not throw.
1059
- */
1060
- onToolsSent?: (tools: ToolDefinition[]) => void;
1061
1050
  /**
1062
1051
  * Conversation this loop drives. Scopes the loop-held compaction circuit
1063
1052
  * breaker and is the source of truth the loop's pipeline contexts and
@@ -1098,7 +1087,6 @@ export class AgentLoop {
1098
1087
  private config: AgentLoopConfig;
1099
1088
  private tools: ToolDefinition[];
1100
1089
  private resolveTools: ((history: Message[]) => ToolDefinition[]) | null;
1101
- private onToolsSent: ((tools: ToolDefinition[]) => void) | null;
1102
1090
  private toolExecutor: LoopToolExecutor | null;
1103
1091
 
1104
1092
  /**
@@ -1134,7 +1122,6 @@ export class AgentLoop {
1134
1122
  tools,
1135
1123
  toolExecutor,
1136
1124
  resolveTools,
1137
- onToolsSent,
1138
1125
  conversationId,
1139
1126
  resolveConversationDir,
1140
1127
  transformCompactedHistory,
@@ -1144,7 +1131,6 @@ export class AgentLoop {
1144
1131
  this.config = { ...DEFAULT_CONFIG, ...config };
1145
1132
  this.tools = tools ?? [];
1146
1133
  this.resolveTools = resolveTools ?? null;
1147
- this.onToolsSent = onToolsSent ?? null;
1148
1134
  this.toolExecutor = toolExecutor ?? null;
1149
1135
  this.conversationId = conversationId;
1150
1136
  this.resolveConversationDir = resolveConversationDir ?? null;
@@ -2255,11 +2241,6 @@ export class AgentLoop {
2255
2241
  // Latency: the request is about to leave for the provider. The span
2256
2242
  // from here to the first streamed token is time-to-first-token.
2257
2243
  latencyTracker?.mark("request_sent");
2258
- // Every await that could cancel the call is behind us; a run aborted
2259
- // during the throttle or a hook never reports an array it did not send.
2260
- if (!signal?.aborted) {
2261
- this.onToolsSent?.(currentTools);
2262
- }
2263
2244
  let response: ProviderResponse;
2264
2245
  try {
2265
2246
  response = await traceAsyncSection("agent-loop:provider-send", () =>
package/src/api/index.ts CHANGED
@@ -34,7 +34,6 @@ import { ConversationErrorEventSchema } from "./events/conversation-error.js";
34
34
  import { ConversationInferenceProfileUpdatedEventSchema } from "./events/conversation-inference-profile-updated.js";
35
35
  import { ConversationNoticeEventSchema } from "./events/conversation-notice.js";
36
36
  import { ConversationTitleUpdatedEventSchema } from "./events/conversation-title-updated.js";
37
- import { DesktopActivityChangedEventSchema } from "./events/desktop-activity-changed.js";
38
37
  import { DiskPressureStatusChangedEventSchema } from "./events/disk-pressure-status-changed.js";
39
38
  import { DocumentCommentCreatedEventSchema } from "./events/document-comment-created.js";
40
39
  import { DocumentCommentDeletedEventSchema } from "./events/document-comment-deleted.js";
@@ -343,10 +342,6 @@ export {
343
342
  type ConversationTitleUpdatedEvent,
344
343
  ConversationTitleUpdatedEventSchema,
345
344
  } from "./events/conversation-title-updated.js";
346
- export {
347
- type DesktopActivityChangedEvent,
348
- DesktopActivityChangedEventSchema,
349
- } from "./events/desktop-activity-changed.js";
350
345
  export {
351
346
  type DiskPressureBlockedCapability,
352
347
  DiskPressureBlockedCapabilitySchema,
@@ -1005,7 +1000,6 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
1005
1000
  ConversationInferenceProfileUpdatedEventSchema,
1006
1001
  ConversationNoticeEventSchema,
1007
1002
  ConversationTitleUpdatedEventSchema,
1008
- DesktopActivityChangedEventSchema,
1009
1003
  DiskPressureStatusChangedEventSchema,
1010
1004
  DocumentCommentCreatedEventSchema,
1011
1005
  DocumentCommentDeletedEventSchema,
@@ -213,10 +213,7 @@ function consumeGrantSync(params: ConsumeGrantParams): ConsumeGrantResult {
213
213
  return { ok: true, grant: reqResult.grant };
214
214
  }
215
215
 
216
- // Debug, not info: `consumeGrantForInvocation` and the inline grant wait
217
- // poll this function, so a miss is the expected steady state while a
218
- // guardian decides. Hits and the wait's outcome are logged at info.
219
- log.debug(
216
+ log.info(
220
217
  {
221
218
  event: "approval_primitive_consume_miss",
222
219
  mode: "request_id",
@@ -258,7 +255,7 @@ function consumeGrantSync(params: ConsumeGrantParams): ConsumeGrantResult {
258
255
  return { ok: true, grant: sigResult.grant };
259
256
  }
260
257
 
261
- log.debug(
258
+ log.info(
262
259
  {
263
260
  event: "approval_primitive_consume_miss",
264
261
  mode: "tool_signature",
@@ -193,10 +193,7 @@ function consumeScopedApprovalGrantByRequestId(
193
193
  .get();
194
194
 
195
195
  if (!candidate) {
196
- // Debug, not info: the inline grant wait and the voice retry loop call
197
- // this every poll, so a miss is the expected steady state while a
198
- // guardian decides. The caller logs the outcome of the wait.
199
- log.debug(
196
+ log.info(
200
197
  {
201
198
  event: "scoped_grant_consume_miss",
202
199
  requestId,
@@ -367,8 +364,7 @@ function consumeScopedApprovalGrantByToolSignature(
367
364
  .get();
368
365
 
369
366
  if (!candidate) {
370
- // Debug for the same reason as the request-id miss above: polled.
371
- log.debug(
367
+ log.info(
372
368
  {
373
369
  event: "scoped_grant_consume_miss",
374
370
  toolName: params.toolName,
@@ -6,7 +6,6 @@
6
6
 
7
7
  import { beforeEach, describe, expect, mock, test } from "bun:test";
8
8
 
9
- import { CONSOLIDATION_ALLOWED_TOOLS } from "../../plugins/defaults/memory/substrate/consolidation-tool-surface.js";
10
9
  import type { Conversation } from "../conversation.js";
11
10
  import type { ChannelCapabilities } from "../conversation-runtime-assembly.js";
12
11
 
@@ -780,45 +779,3 @@ describe("isToolActiveForContext — allowlist-only tools (delete_memory_page)",
780
779
  ).toBe(false);
781
780
  });
782
781
  });
783
-
784
- describe("isToolActiveForContext — memory consolidation surface resolves onto the wire", () => {
785
- // A background job conversation is not a subagent, so SUBAGENT_ONLY tools
786
- // are filtered off before the allowlist applies. Every name the
787
- // consolidation run allowlists must survive that gate, or the tool is
788
- // silently missing from the run while the allowlist still claims it.
789
- const consolidationCtx = () =>
790
- makeCtx({
791
- subagentAllowedTools: new Set(CONSOLIDATION_ALLOWED_TOOLS),
792
- subagentToolGateMode: "wire",
793
- });
794
-
795
- test("every allowlisted tool is active for a background conversation", () => {
796
- for (const name of CONSOLIDATION_ALLOWED_TOOLS) {
797
- expect(isToolActiveForContext(name, consolidationCtx()), name).toBe(true);
798
- }
799
- });
800
-
801
- test("the shell and the page-delete primitive are on the surface", () => {
802
- expect(isToolActiveForContext("bash", consolidationCtx())).toBe(true);
803
- expect(
804
- isToolActiveForContext("delete_memory_page", consolidationCtx()),
805
- ).toBe(true);
806
- });
807
-
808
- test("network egress and host-proxy tools stay off the wire", () => {
809
- for (const name of [
810
- "web_fetch",
811
- "web_search",
812
- "network_request",
813
- "host_bash",
814
- "host_file_read",
815
- "host_file_write",
816
- "host_file_edit",
817
- "host_cu",
818
- ]) {
819
- expect(isToolActiveForContext(name, consolidationCtx()), name).toBe(
820
- false,
821
- );
822
- }
823
- });
824
- });
@@ -38,7 +38,6 @@ import {
38
38
  import { getConfig } from "../config/loader.js";
39
39
  import type { LLMCallSite } from "../config/schemas/llm.js";
40
40
  import { isSendUserMessageActiveForTurn } from "../config/send-user-message-gate.js";
41
- import { desktopAutomationLease } from "../desktop/desktop-automation-lease.js";
42
41
  import { writeRelationshipState } from "../home/relationship-state-writer.js";
43
42
  import type { UserPromptSubmitInputContext } from "../hooks/types.js";
44
43
  import {
@@ -914,7 +913,6 @@ export async function runAgentLoopImpl(
914
913
  if (ctx.pendingReactionRecords?.length) {
915
914
  ownedReactionRecords.push(...ctx.pendingReactionRecords.splice(0));
916
915
  }
917
- desktopAutomationLease.releaseForConversation(ctx.conversationId);
918
916
  ctx.abortController = null;
919
917
  ctx.setProcessing(false);
920
918
  unregisterInflightTurn(ctx.conversationId, state);
@@ -22,10 +22,6 @@ import {
22
22
  import { supportsChannelReaction } from "../messaging/providers/index.js";
23
23
  import type { PermissionPrompter } from "../permissions/prompter.js";
24
24
  import type { SecretPrompter } from "../permissions/secret-prompter.js";
25
- import {
26
- hashConversationToolSurface,
27
- recordConversationToolSurface,
28
- } from "../persistence/conversation-tool-surface.js";
29
25
  import { getBindingByConversation } from "../persistence/external-conversation-store.js";
30
26
  import { getAllDefaultPluginNames } from "../plugins/defaults/main.js";
31
27
  import { isActivationSession } from "../plugins/defaults/memory/activation-session-store.js";
@@ -947,50 +943,6 @@ export function canSpawnSubagentsForTurn(ctx: Conversation): boolean {
947
943
  );
948
944
  }
949
945
 
950
- /**
951
- * Build the agent loop's `onToolsSent` observer for a conversation: record
952
- * the tool array each provider call sends so a later fork wake can replay it
953
- * (`recordConversationToolSurface`). Only the loop's send boundary sees the
954
- * sent array. The resolver is also consulted out of band (the token count
955
- * behind `/compact` and `/clean`, compaction estimates), where a read outside
956
- * any turn resolves a clientless surface that would overwrite the one the
957
- * conversation's turns actually send.
958
- *
959
- * Arrays that are not the conversation's own surface are skipped: a replaying
960
- * wake sends its source's array, an empty array is a tools-disabled call (a
961
- * fork replaying it could never call `remember`), and disk-pressure cleanup
962
- * mode narrows the wire to cleanup tools. Best-effort: a failed write is
963
- * logged and the array still counts as recorded, so a persistent failure logs
964
- * once per distinct surface rather than once per provider call.
965
- */
966
- export function createWireToolSurfaceRecorder(
967
- ctx: Conversation,
968
- ): (tools: ToolDefinition[]) => void {
969
- return (tools) => {
970
- if (
971
- !ctx.conversationId ||
972
- ctx.wireToolReplay ||
973
- tools.length === 0 ||
974
- ctx.diskPressureCleanupModeActive === true
975
- ) {
976
- return;
977
- }
978
- try {
979
- ctx.recordedToolSurfaceHash = recordConversationToolSurface(
980
- ctx.conversationId,
981
- tools,
982
- ctx.recordedToolSurfaceHash,
983
- );
984
- } catch (err) {
985
- log.warn(
986
- { err, conversationId: ctx.conversationId },
987
- "failed to record the conversation's wire tool surface; continuing",
988
- );
989
- ctx.recordedToolSurfaceHash = hashConversationToolSurface(tools);
990
- }
991
- };
992
- }
993
-
994
946
  /**
995
947
  * Build a resolveTools callback that merges base tool definitions with
996
948
  * dynamically projected skill tools on each agent turn. Also updates
@@ -1285,14 +1237,6 @@ export function createResolveToolsCallback(
1285
1237
 
1286
1238
  ctx.allowedToolNames = turnAllowed;
1287
1239
 
1288
- // A wake replaying its source's recorded surface sends that array
1289
- // verbatim: the wire tool block is the first tier of the provider cache
1290
- // prefix (tools → system → messages), so only the same bytes read the
1291
- // source's cached prefix instead of rewriting it. Execution is unaffected:
1292
- // `allowedToolNames` above and the executor's allowlist gate still decide
1293
- // what may run.
1294
- return ctx.wireToolReplay
1295
- ? [...ctx.wireToolReplay]
1296
- : applyActivityField(allBaseDefs);
1240
+ return applyActivityField(allBaseDefs);
1297
1241
  };
1298
1242
  }
@@ -181,7 +181,6 @@ import {
181
181
  canSpawnSubagentsForTurn,
182
182
  createResolveToolsCallback,
183
183
  createToolExecutor,
184
- createWireToolSurfaceRecorder,
185
184
  } from "./conversation-tool-setup.js";
186
185
  import { canonicalizeTimeZone } from "./date-context.js";
187
186
  import { HostAppControlProxy } from "./host-app-control-proxy.js";
@@ -453,21 +452,6 @@ export class Conversation {
453
452
  * @internal
454
453
  */
455
454
  toolContextPin?: WakeToolContextPin;
456
- /**
457
- * Tool definitions sent verbatim in place of the resolved wire array, for
458
- * a wake replaying its source conversation's recorded surface
459
- * (`recordConversationToolSurface`). Set and restored alongside the
460
- * allowlist by `scopeWakeAllowedTools`; read only where the resolver returns
461
- * the wire array, so it never widens what may execute.
462
- * @internal
463
- */
464
- wireToolReplay?: readonly ToolDefinition[];
465
- /**
466
- * Hash of the wire tool array last recorded for this conversation in this
467
- * process, so an unchanged surface is not rewritten on every provider call.
468
- * @internal
469
- */
470
- recordedToolSurfaceHash?: string;
471
455
  /** @internal */ readonly skillProjectionState = new Map<string, string>();
472
456
  /** @internal */ readonly skillProjectionCache: SkillProjectionCache = {};
473
457
  /** @internal */ usageStats: UsageStats = {
@@ -1054,7 +1038,6 @@ export class Conversation {
1054
1038
  tools: toolDefs.length > 0 ? toolDefs : undefined,
1055
1039
  toolExecutor: toolDefs.length > 0 ? toolExecutor : undefined,
1056
1040
  resolveTools,
1057
- onToolsSent: createWireToolSurfaceRecorder(this),
1058
1041
  resolveConversationDir: () => {
1059
1042
  const conv = getConversation(this.conversationId);
1060
1043
  if (!conv) {
@@ -258,11 +258,15 @@ async function getDaemonStatus(): Promise<{
258
258
  return { running: true, pid };
259
259
  }
260
260
 
261
+ function getStartupLockPath(): string {
262
+ return getDaemonStartupLockPath();
263
+ }
264
+
261
265
  /** Attempt to acquire a startup lock. Returns true on success. Stale locks
262
266
  * (older than STARTUP_LOCK_STALE_MS) are forcibly removed to prevent
263
267
  * permanent deadlocks from a crashed caller. */
264
268
  function acquireStartupLock(): boolean {
265
- const lockPath = getDaemonStartupLockPath();
269
+ const lockPath = getStartupLockPath();
266
270
  try {
267
271
  // Ensure the root directory exists before attempting the lock file write.
268
272
  // On a first-time run, getRootDir() may not exist yet, and writeFileSync
@@ -289,7 +293,7 @@ function acquireStartupLock(): boolean {
289
293
 
290
294
  function releaseStartupLock(): void {
291
295
  try {
292
- unlinkSync(getDaemonStartupLockPath());
296
+ unlinkSync(getStartupLockPath());
293
297
  } catch {
294
298
  /* already removed */
295
299
  }
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Periodic reaper for orphaned subprocesses that reparent to the daemon.
3
3
  *
4
- * POSIX tools run commands in their own process group (`detached: true`) and,
5
- * on timeout/abort, group-kill with `process.kill(-pgid, SIGKILL)` (the bash
6
- * and host_bash tools, and the skill sandbox runner). Windows piped shell
7
- * children stay attached and are torn down with `taskkill /T`. The
4
+ * Tools run commands in their own process group (`detached: true`) and, on
5
+ * timeout/abort, group-kill with `process.kill(-pgid, SIGKILL)` (the bash and
6
+ * host_bash tools, and the skill sandbox runner). The
8
7
  * immediate child is reaped by Bun/libuv, but its descendants — e.g. git's
9
8
  * transport helpers or a skill runner's `bun` process — were never spawned by
10
9
  * the daemon, so when the group dies they reparent to PID 1. When the daemon
@@ -59,12 +59,6 @@ export interface SubagentToolStats {
59
59
  * parity for desktop/web sources; channel-routed sources resolve every tool
60
60
  * gate identically under `hasNoClient: true` with or without them.)
61
61
  *
62
- * A wake that also carries its source's recorded wire array
63
- * (`Conversation.wireToolReplay`, set by `scopeWakeAllowedTools`) sends that
64
- * array verbatim, so the pin then shapes only the execution-side inventory
65
- * (`allowedToolNames`); the pin alone carries wire parity for a source with
66
- * no recorded surface.
67
- *
68
62
  * Tool-definition resolution ONLY. The executor callback and host-proxy
69
63
  * attachment paths never read the pin, so it cannot make a host tool
70
64
  * runnable: in execution gate mode every non-allowlisted call is rejected