@vellumai/assistant 0.7.3 → 0.8.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 (169) hide show
  1. package/ARCHITECTURE.md +29 -28
  2. package/Dockerfile +1 -0
  3. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  4. package/bun.lock +3 -0
  5. package/knip.json +1 -0
  6. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  7. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  8. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  9. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  10. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  11. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  12. package/openapi.yaml +22 -4
  13. package/package.json +3 -1
  14. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  15. package/src/__tests__/approval-cascade.test.ts +8 -16
  16. package/src/__tests__/approval-routes-http.test.ts +6 -0
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  18. package/src/__tests__/call-constants.test.ts +10 -1
  19. package/src/__tests__/call-controller.test.ts +127 -0
  20. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
  21. package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
  22. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  23. package/src/__tests__/context-search-pkb-source.test.ts +12 -6
  24. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  25. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  27. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  28. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  29. package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
  30. package/src/__tests__/conversation-process-callsite.test.ts +1 -6
  31. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  32. package/src/__tests__/conversation-runtime-assembly.test.ts +15 -6
  33. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  34. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  35. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  36. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  37. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  38. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  39. package/src/__tests__/filing-service.test.ts +2 -19
  40. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
  41. package/src/__tests__/injector-chain.test.ts +24 -16
  42. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  43. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
  44. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  45. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  46. package/src/__tests__/oauth-cli.test.ts +121 -0
  47. package/src/__tests__/relay-server.test.ts +46 -2
  48. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  49. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  50. package/src/__tests__/secret-response-routing.test.ts +7 -5
  51. package/src/__tests__/server-history-render.test.ts +82 -0
  52. package/src/__tests__/skill-include-graph.test.ts +31 -0
  53. package/src/__tests__/skill-load-tool.test.ts +44 -16
  54. package/src/__tests__/skills.test.ts +39 -0
  55. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  56. package/src/__tests__/tool-executor.test.ts +155 -0
  57. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  58. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
  59. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  60. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  61. package/src/agent/loop.ts +11 -0
  62. package/src/approvals/guardian-decision-primitive.ts +0 -13
  63. package/src/approvals/guardian-request-resolvers.ts +4 -32
  64. package/src/calls/call-constants.ts +5 -8
  65. package/src/calls/call-controller.ts +130 -67
  66. package/src/calls/relay-server.ts +7 -1
  67. package/src/calls/voice-session-bridge.ts +1 -1
  68. package/src/cli/commands/memory-v2.ts +7 -7
  69. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -254
  70. package/src/cli/commands/oauth/connect.ts +10 -52
  71. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  72. package/src/config/feature-flag-registry.json +1 -17
  73. package/src/config/loader.ts +72 -19
  74. package/src/config/schemas/memory-v2.ts +1 -1
  75. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
  76. package/src/daemon/conversation-agent-loop-handlers.ts +32 -0
  77. package/src/daemon/conversation-agent-loop.ts +13 -10
  78. package/src/daemon/conversation-lifecycle.ts +22 -8
  79. package/src/daemon/conversation-surfaces.ts +16 -14
  80. package/src/daemon/conversation-tool-setup.ts +9 -5
  81. package/src/daemon/conversation.ts +1 -1
  82. package/src/daemon/handlers/shared.ts +26 -0
  83. package/src/daemon/host-bash-proxy.ts +1 -1
  84. package/src/daemon/host-browser-proxy.ts +1 -1
  85. package/src/daemon/host-cu-proxy.ts +1 -1
  86. package/src/daemon/host-file-proxy.ts +1 -1
  87. package/src/daemon/host-transfer-proxy.ts +2 -2
  88. package/src/daemon/lifecycle.ts +88 -73
  89. package/src/daemon/memory-v2-startup.ts +55 -14
  90. package/src/daemon/message-types/messages.ts +19 -1
  91. package/src/documents/document-store.ts +35 -1
  92. package/src/filing/filing-service.ts +2 -3
  93. package/src/heartbeat/heartbeat-service.ts +1 -1
  94. package/src/ipc/assistant-server.ts +93 -36
  95. package/src/ipc/skill-server.ts +99 -42
  96. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  97. package/src/memory/context-search/sources/memory-v2.ts +1 -17
  98. package/src/memory/context-search/sources/memory.ts +2 -2
  99. package/src/memory/context-search/sources/pkb.ts +2 -3
  100. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  101. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  102. package/src/memory/graph/conversation-graph-memory.ts +32 -9
  103. package/src/memory/graph/graph-search.test.ts +6 -5
  104. package/src/memory/graph/graph-search.ts +3 -4
  105. package/src/memory/graph/retriever.test.ts +12 -7
  106. package/src/memory/graph/retriever.ts +4 -5
  107. package/src/memory/graph/tool-handlers.ts +3 -4
  108. package/src/memory/graph/tools.ts +4 -4
  109. package/src/memory/indexer.ts +1 -2
  110. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
  111. package/src/memory/jobs/embed-concept-page.ts +223 -87
  112. package/src/memory/jobs-worker.ts +8 -4
  113. package/src/memory/pkb/pkb-search.test.ts +6 -5
  114. package/src/memory/pkb/pkb-search.ts +4 -5
  115. package/src/memory/qdrant-client.ts +3 -0
  116. package/src/memory/search/semantic.ts +4 -5
  117. package/src/memory/v2/__tests__/activation.test.ts +35 -5
  118. package/src/memory/v2/__tests__/consolidation-job.test.ts +21 -32
  119. package/src/memory/v2/__tests__/injection.test.ts +140 -23
  120. package/src/memory/v2/__tests__/qdrant.test.ts +310 -9
  121. package/src/memory/v2/__tests__/sim.test.ts +118 -7
  122. package/src/memory/v2/__tests__/static-context.test.ts +1 -13
  123. package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
  124. package/src/memory/v2/consolidation-job.ts +7 -8
  125. package/src/memory/v2/injection.ts +32 -12
  126. package/src/memory/v2/page-store.ts +39 -0
  127. package/src/memory/v2/prompts/consolidation.ts +5 -0
  128. package/src/memory/v2/qdrant.ts +209 -48
  129. package/src/memory/v2/sim.ts +67 -26
  130. package/src/memory/v2/static-context.ts +4 -8
  131. package/src/memory/v2/sweep-job.ts +5 -6
  132. package/src/memory/v2/types.ts +7 -0
  133. package/src/notifications/copy-composer.ts +46 -12
  134. package/src/notifications/decision-engine.ts +46 -0
  135. package/src/permissions/gateway-threshold-reader.ts +116 -8
  136. package/src/permissions/prompter.ts +86 -96
  137. package/src/permissions/secret-prompter.ts +31 -31
  138. package/src/plugins/defaults/injectors.ts +1 -2
  139. package/src/proactive-artifact/job.test.ts +51 -4
  140. package/src/proactive-artifact/job.ts +16 -2
  141. package/src/proactive-artifact/message-copy.ts +18 -1
  142. package/src/prompts/templates/SOUL.md +13 -28
  143. package/src/runtime/auth/route-policy.ts +1 -0
  144. package/src/runtime/channel-approvals.ts +3 -2
  145. package/src/runtime/guardian-reply-router.ts +0 -10
  146. package/src/runtime/pending-interactions.ts +19 -15
  147. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  148. package/src/runtime/routes/approval-routes.ts +7 -3
  149. package/src/runtime/routes/consolidation-routes.ts +8 -9
  150. package/src/runtime/routes/conversation-query-routes.ts +44 -1
  151. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  152. package/src/runtime/routes/filing-routes.ts +2 -3
  153. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +0 -3
  154. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  155. package/src/runtime/routes/memory-item-routes.ts +5 -6
  156. package/src/runtime/routes/memory-v2-routes.ts +103 -17
  157. package/src/skills/include-graph.ts +35 -13
  158. package/src/tools/document/document-tool.ts +20 -0
  159. package/src/tools/executor.ts +18 -2
  160. package/src/tools/memory/register.test.ts +7 -5
  161. package/src/tools/permission-checker.ts +15 -0
  162. package/src/tools/skills/load.ts +24 -20
  163. package/src/tools/tool-name-aliases.ts +19 -0
  164. package/src/tools/types.ts +19 -1
  165. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  166. package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
  167. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  168. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  169. package/src/workspace/migrations/registry.ts +6 -0
@@ -37,9 +37,13 @@
37
37
  * back to a shorter deterministic path via the shared socket-path resolver.
38
38
  */
39
39
 
40
- import { existsSync, mkdirSync, unlinkSync } from "node:fs";
40
+ import { existsSync, unlinkSync } from "node:fs";
41
41
  import { createServer, type Server, type Socket } from "node:net";
42
- import { dirname } from "node:path";
42
+
43
+ import {
44
+ ensureSocketDir,
45
+ SocketWatchdog,
46
+ } from "@vellumai/ipc-server-utils";
43
47
 
44
48
  import {
45
49
  type SkillRouteHandle,
@@ -233,6 +237,15 @@ class SkillIpcConnectionState implements SkillIpcConnection {
233
237
  // Server
234
238
  // ---------------------------------------------------------------------------
235
239
 
240
+ /** Optional configuration for {@link SkillIpcServer}. */
241
+ export interface SkillIpcServerOptions {
242
+ /**
243
+ * How often the socket-file watchdog stats the listening socket path.
244
+ * Set to `0` to disable. Defaults to {@link SocketWatchdog}'s 5000ms.
245
+ */
246
+ watchdogIntervalMs?: number;
247
+ }
248
+
236
249
  export class SkillIpcServer {
237
250
  private server: Server | null = null;
238
251
  private clients = new Set<Socket>();
@@ -252,8 +265,15 @@ export class SkillIpcServer {
252
265
  private connections = new WeakMap<Socket, SkillIpcConnectionState>();
253
266
  private nextConnectionId = 1;
254
267
  private socketPath: string;
268
+ private watchdog: SocketWatchdog;
269
+ /**
270
+ * Servers whose listener path has been replaced by a re-bind. Kept around
271
+ * so already-connected sockets continue to work; closed gracefully once
272
+ * their accept loops drain.
273
+ */
274
+ private legacyServers = new Set<Server>();
255
275
 
256
- constructor() {
276
+ constructor(options?: SkillIpcServerOptions) {
257
277
  const resolution = resolveSkillIpcSocketPath();
258
278
  this.socketPath = resolution.path;
259
279
  log.info(
@@ -266,6 +286,21 @@ export class SkillIpcServer {
266
286
  for (const route of skillIpcStreamingRoutes) {
267
287
  this.streamingMethods.set(route.method, route.handler);
268
288
  }
289
+
290
+ this.watchdog = new SocketWatchdog({
291
+ socketPath: this.socketPath,
292
+ intervalMs: options?.watchdogIntervalMs,
293
+ getServer: () => this.server,
294
+ createServer: () => this.createListeningServer(),
295
+ onRebind: (newServer, oldServer) => {
296
+ this.server = newServer;
297
+ this.legacyServers.add(oldServer);
298
+ oldServer.close(() => {
299
+ this.legacyServers.delete(oldServer);
300
+ });
301
+ },
302
+ log,
303
+ });
269
304
  }
270
305
 
271
306
  /** Register an additional method handler after construction. */
@@ -349,17 +384,71 @@ export class SkillIpcServer {
349
384
  /** Start listening on the Unix domain socket. */
350
385
  async start(): Promise<void> {
351
386
  // Ensure the parent directory exists before listening.
352
- const socketDir = dirname(this.socketPath);
353
- if (!existsSync(socketDir)) {
354
- mkdirSync(socketDir, { recursive: true, mode: 0o700 });
355
- }
387
+ ensureSocketDir(this.socketPath);
356
388
 
357
389
  // Probe before unlink so a second daemon can't silently orphan an active
358
390
  // listener (Unix lets you unlink a still-bound socket file). See
359
391
  // `ensureSocketPathFree` for the behavior matrix.
360
392
  await ensureSocketPathFree(this.socketPath);
361
393
 
362
- this.server = createServer((socket) => {
394
+ this.server = this.createListeningServer();
395
+ this.server.listen(this.socketPath, () => {
396
+ log.info({ path: this.socketPath }, "Skill IPC server listening");
397
+ });
398
+
399
+ this.watchdog.start();
400
+ }
401
+
402
+ /** Stop the server and disconnect all clients. */
403
+ stop(): void {
404
+ this.watchdog.stop();
405
+
406
+ for (const client of this.clients) {
407
+ this.teardownSubscriptions(client);
408
+ this.teardownConnection(client);
409
+ if (!client.destroyed) client.destroy();
410
+ }
411
+ this.clients.clear();
412
+
413
+ for (const legacy of this.legacyServers) {
414
+ legacy.close();
415
+ }
416
+ this.legacyServers.clear();
417
+
418
+ if (this.server) {
419
+ this.server.close();
420
+ this.server = null;
421
+ }
422
+
423
+ if (existsSync(this.socketPath)) {
424
+ try {
425
+ unlinkSync(this.socketPath);
426
+ } catch {
427
+ // Ignore
428
+ }
429
+ }
430
+ }
431
+
432
+ /** Get the socket path (for diagnostics). */
433
+ getSocketPath(): string {
434
+ return this.socketPath;
435
+ }
436
+
437
+ /**
438
+ * Re-bind the listening socket if its path entry is missing on disk.
439
+ *
440
+ * Public for tests so the watchdog can be exercised deterministically
441
+ * without waiting for the interval. Returns `true` when a re-bind was
442
+ * performed, `false` otherwise.
443
+ */
444
+ async rebindIfMissing(): Promise<boolean> {
445
+ return this.watchdog.rebindIfMissing();
446
+ }
447
+
448
+ // ── Internal ──────────────────────────────────────────────────────────
449
+
450
+ private createListeningServer(): Server {
451
+ const server = createServer((socket) => {
363
452
  this.clients.add(socket);
364
453
  const connection = new SkillIpcConnectionState(
365
454
  `skill-ipc-${this.nextConnectionId++}`,
@@ -406,45 +495,13 @@ export class SkillIpcServer {
406
495
  });
407
496
  });
408
497
 
409
- this.server.on("error", (err) => {
498
+ server.on("error", (err) => {
410
499
  log.error({ err }, "Skill IPC server error");
411
500
  });
412
501
 
413
- this.server.listen(this.socketPath, () => {
414
- log.info({ path: this.socketPath }, "Skill IPC server listening");
415
- });
416
- }
417
-
418
- /** Stop the server and disconnect all clients. */
419
- stop(): void {
420
- for (const client of this.clients) {
421
- this.teardownSubscriptions(client);
422
- this.teardownConnection(client);
423
- if (!client.destroyed) client.destroy();
424
- }
425
- this.clients.clear();
426
-
427
- if (this.server) {
428
- this.server.close();
429
- this.server = null;
430
- }
431
-
432
- if (existsSync(this.socketPath)) {
433
- try {
434
- unlinkSync(this.socketPath);
435
- } catch {
436
- // Ignore
437
- }
438
- }
439
- }
440
-
441
- /** Get the socket path (for diagnostics). */
442
- getSocketPath(): string {
443
- return this.socketPath;
502
+ return server;
444
503
  }
445
504
 
446
- // ── Internal ──────────────────────────────────────────────────────────
447
-
448
505
  private handleMessage(socket: Socket, line: string): void {
449
506
  let frame: IpcRequest & { result?: unknown; error?: string };
450
507
  try {
@@ -1,19 +1,16 @@
1
1
  /**
2
2
  * Tests for v1/v2 mutual exclusion in `maybeEnqueueGraphMaintenanceJobs`.
3
3
  *
4
- * The schedule is now mutually exclusive: when both the `memory-v2-enabled`
5
- * flag and `memory.v2.enabled` config are on, only `memory_v2_consolidate`
6
- * is scheduled; otherwise the four v1 entries (decay, consolidate,
7
- * pattern_scan, narrative) fire and the v2 entry does not.
4
+ * The schedule is mutually exclusive: when `memory.v2.enabled` is true,
5
+ * only `memory_v2_consolidate` is scheduled; otherwise the four v1
6
+ * entries (decay, consolidate, pattern_scan, narrative) fire and the v2
7
+ * entry does not.
8
8
  *
9
9
  * Coverage:
10
- * - Flag off → only v1 entries fire (no `memory_v2_consolidate`).
11
- * - Flag on + config off → only v1 entries fire.
12
- * - Flag on + config on, no prior checkpoint only the v2 entry fires.
13
- * - Flag on + config on, recent checkpoint → no v2 row (interval not
14
- * yet elapsed).
15
- * - Flag on + config on, stale checkpoint → v2 row enqueued, checkpoint
16
- * refreshed.
10
+ * - Config off → only v1 entries fire (no `memory_v2_consolidate`).
11
+ * - Config on, no prior checkpoint → only the v2 entry fires.
12
+ * - Config on, recent checkpoint no v2 row (interval not yet elapsed).
13
+ * - Config on, stale checkpoint → v2 row enqueued, checkpoint refreshed.
17
14
  *
18
15
  * The sweep job is intentionally NOT scheduled here: it is wired into the
19
16
  * `graph_extract` debounce in `indexer.ts`. Those triggers are covered by
@@ -27,7 +24,6 @@ import { tmpdir } from "node:os";
27
24
  import { join } from "node:path";
28
25
  import {
29
26
  afterAll,
30
- afterEach,
31
27
  beforeAll,
32
28
  beforeEach,
33
29
  describe,
@@ -69,8 +65,6 @@ const { getDb } = await import("../db-connection.js");
69
65
  const { initializeDb } = await import("../db-init.js");
70
66
  const { resetTestTables } = await import("../raw-query.js");
71
67
  const { memoryJobs } = await import("../schema.js");
72
- const { _setOverridesForTesting } =
73
- await import("../../config/assistant-feature-flags.js");
74
68
  const { applyNestedDefaults } = await import("../../config/loader.js");
75
69
  const { setMemoryCheckpoint, deleteMemoryCheckpoint } =
76
70
  await import("../checkpoints.js");
@@ -111,26 +105,10 @@ beforeEach(() => {
111
105
  resetTestTables("memory_jobs", "memory_checkpoints");
112
106
  });
113
107
 
114
- afterEach(() => {
115
- _setOverridesForTesting({});
116
- });
117
-
118
108
  // ---------------------------------------------------------------------------
119
109
 
120
110
  describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
121
- test("does not enqueue consolidate when memory-v2-enabled flag is off", () => {
122
- _setOverridesForTesting({ "memory-v2-enabled": false });
123
- const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
124
-
125
- // Force the interval to look elapsed. If the gate failed open, this
126
- // would be enough to enqueue a job.
127
- maybeEnqueueGraphMaintenanceJobs(config, Date.now());
128
-
129
- expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
130
- });
131
-
132
111
  test("does not enqueue consolidate when config.memory.v2.enabled is off", () => {
133
- _setOverridesForTesting({ "memory-v2-enabled": true });
134
112
  const config = buildConfig({ v2Enabled: false, intervalHours: 1 });
135
113
 
136
114
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
@@ -138,8 +116,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
138
116
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
139
117
  });
140
118
 
141
- test("enqueues consolidate when both gates are on and no checkpoint exists", () => {
142
- _setOverridesForTesting({ "memory-v2-enabled": true });
119
+ test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
143
120
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
144
121
 
145
122
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
@@ -153,7 +130,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
153
130
  });
154
131
 
155
132
  test("does not enqueue consolidate before the interval has elapsed", () => {
156
- _setOverridesForTesting({ "memory-v2-enabled": true });
157
133
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
158
134
 
159
135
  const now = Date.now();
@@ -166,7 +142,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
166
142
  });
167
143
 
168
144
  test("enqueues consolidate again once the interval elapses", () => {
169
- _setOverridesForTesting({ "memory-v2-enabled": true });
170
145
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
171
146
 
172
147
  const now = Date.now();
@@ -182,7 +157,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
182
157
  });
183
158
 
184
159
  test("respects a custom consolidation_interval_hours value", () => {
185
- _setOverridesForTesting({ "memory-v2-enabled": true });
186
160
  const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
187
161
 
188
162
  const now = Date.now();
@@ -206,7 +180,6 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
206
180
  });
207
181
 
208
182
  test("v1 maintenance entries are suppressed when v2 is active", () => {
209
- _setOverridesForTesting({ "memory-v2-enabled": true });
210
183
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
211
184
 
212
185
  // No checkpoints set — every entry would be due if it were scheduled.
@@ -225,27 +198,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
225
198
  expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
226
199
  });
227
200
 
228
- test("flag-off path fires v1 entries and does not enqueue v2", () => {
229
- _setOverridesForTesting({ "memory-v2-enabled": false });
230
- const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
231
-
232
- deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
233
- deleteMemoryCheckpoint("graph_maintenance:consolidate:last_run");
234
- deleteMemoryCheckpoint("graph_maintenance:pattern_scan:last_run");
235
- deleteMemoryCheckpoint("graph_maintenance:narrative:last_run");
236
- deleteMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY);
237
-
238
- maybeEnqueueGraphMaintenanceJobs(config, Date.now());
239
-
240
- expect(countPendingJobs("graph_decay")).toBe(1);
241
- expect(countPendingJobs("graph_consolidate")).toBe(1);
242
- expect(countPendingJobs("graph_pattern_scan")).toBe(1);
243
- expect(countPendingJobs("graph_narrative_refine")).toBe(1);
244
- expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
245
- });
246
-
247
- test("config-gate-off path fires v1 entries and does not enqueue v2", () => {
248
- _setOverridesForTesting({ "memory-v2-enabled": true });
201
+ test("v2-off path fires v1 entries and does not enqueue v2", () => {
249
202
  const config = buildConfig({ v2Enabled: false, intervalHours: 1 });
250
203
 
251
204
  deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
@@ -2,7 +2,7 @@
2
2
  // Memory v2 — `recall` adapter for the `memory` source
3
3
  // ---------------------------------------------------------------------------
4
4
  //
5
- // When the v2 flag is on, the `memory` recall source reads from the v2
5
+ // When v2 is enabled, the `memory` recall source reads from the v2
6
6
  // concept-page subsystem (under `<workspace>/memory/concepts/`) instead of
7
7
  // the legacy graph. Two retrieval paths run in parallel and merge:
8
8
  //
@@ -26,8 +26,6 @@
26
26
  import { readdir, readFile, realpath, stat } from "node:fs/promises";
27
27
  import { extname, isAbsolute, join, relative } from "node:path";
28
28
 
29
- import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
30
- import type { AssistantConfig } from "../../../config/schema.js";
31
29
  import { getLogger } from "../../../util/logger.js";
32
30
  import { embedWithRetry } from "../../embed.js";
33
31
  import { generateSparseEmbedding } from "../../embedding-backend.js";
@@ -46,20 +44,6 @@ import type {
46
44
  RecallSearchResult,
47
45
  } from "../types.js";
48
46
 
49
- /**
50
- * True when both v2 gates are on. Single source of truth for whether v2 is
51
- * the active memory subsystem — used by recall sources (`memory`, `pkb`),
52
- * per-turn injectors, the indexer's v1 graph_extract suppression, and the
53
- * v1/v2 maintenance scheduler. The historical name retains "Read" but the
54
- * predicate now gates both read and write paths.
55
- */
56
- export function isMemoryV2ReadActive(config: AssistantConfig): boolean {
57
- return (
58
- isAssistantFeatureFlagEnabled("memory-v2-enabled", config) &&
59
- config.memory.v2.enabled
60
- );
61
- }
62
-
63
47
  const log = getLogger("context-search-memory-v2-source");
64
48
 
65
49
  /**
@@ -9,7 +9,7 @@ import type {
9
9
  RecallSearchContext,
10
10
  RecallSearchResult,
11
11
  } from "../types.js";
12
- import { isMemoryV2ReadActive, searchMemoryV2Source } from "./memory-v2.js";
12
+ import { searchMemoryV2Source } from "./memory-v2.js";
13
13
 
14
14
  const log = getLogger("context-search-memory-source");
15
15
 
@@ -23,7 +23,7 @@ export async function searchMemorySource(
23
23
  return { evidence: [] };
24
24
  }
25
25
 
26
- if (isMemoryV2ReadActive(context.config)) {
26
+ if (context.config.memory.v2.enabled) {
27
27
  return searchMemoryV2Source(query, context, normalizedLimit);
28
28
  }
29
29
 
@@ -15,7 +15,6 @@ import type {
15
15
  RecallSearchContext,
16
16
  RecallSearchResult,
17
17
  } from "../types.js";
18
- import { isMemoryV2ReadActive } from "./memory-v2.js";
19
18
 
20
19
  const log = getLogger("context-search-pkb-source");
21
20
 
@@ -76,7 +75,7 @@ export async function searchPkbSource(
76
75
  context: RecallSearchContext,
77
76
  limit: number,
78
77
  ): Promise<RecallSearchResult> {
79
- if (isMemoryV2ReadActive(context.config)) {
78
+ if (context.config.memory.v2.enabled) {
80
79
  return { evidence: [] };
81
80
  }
82
81
 
@@ -139,7 +138,7 @@ export async function searchPkbSource(
139
138
  export function readPkbContextEvidence(
140
139
  context: RecallSearchContext,
141
140
  ): RecallEvidence[] {
142
- if (isMemoryV2ReadActive(context.config)) {
141
+ if (context.config.memory.v2.enabled) {
143
142
  return [];
144
143
  }
145
144