@vellumai/assistant 0.4.49 → 0.4.50
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.
- package/ARCHITECTURE.md +24 -33
- package/README.md +3 -3
- package/docs/architecture/memory.md +180 -119
- package/package.json +2 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -0
- package/src/__tests__/credential-vault.test.ts +13 -1
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/invite-redemption-service.test.ts +65 -1
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +572 -5
- package/src/__tests__/oauth-store.test.ts +120 -6
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +46 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secure-keys.test.ts +7 -2
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/voice-invite-redemption.test.ts +32 -1
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +132 -0
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +43 -5
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +1 -1
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/doctor.ts +4 -3
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +31 -2
- package/src/cli/commands/oauth/connections.ts +431 -97
- package/src/cli/commands/oauth/providers.ts +15 -1
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +4 -10
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +61 -3
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +31 -3
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -0
- package/src/daemon/server.ts +12 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/instrument.ts +61 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +22 -3
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/oauth/byo-connection.test.ts +8 -1
- package/src/oauth/oauth-store.ts +113 -27
- package/src/oauth/seed-providers.ts +6 -0
- package/src/oauth/token-persistence.ts +11 -3
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/invite-redemption-service.ts +19 -1
- package/src/runtime/invite-service.ts +25 -0
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/conversation-routes.ts +9 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +2 -2
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +2 -1
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/secure-keys.ts +25 -3
- package/src/security/token-manager.ts +36 -36
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +1 -3
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/schedule/create.ts +8 -1
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
package/src/cli/commands/mcp.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
1
4
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
6
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
@@ -11,7 +14,7 @@ import {
|
|
|
11
14
|
deleteMcpOAuthCredentials,
|
|
12
15
|
McpOAuthProvider,
|
|
13
16
|
} from "../../mcp/mcp-oauth-provider.js";
|
|
14
|
-
import {
|
|
17
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
15
18
|
import { log } from "../logger.js";
|
|
16
19
|
|
|
17
20
|
export const HEALTH_CHECK_TIMEOUT_MS = 10_000;
|
|
@@ -51,6 +54,21 @@ export async function checkServerHealth(
|
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Write a signal file so the daemon's ConfigWatcher triggers an MCP reload.
|
|
59
|
+
* Used by `mcp reload`, `mcp auth`, and any operation that needs the daemon
|
|
60
|
+
* to reconnect MCP servers.
|
|
61
|
+
*/
|
|
62
|
+
function signalMcpReload(): void {
|
|
63
|
+
try {
|
|
64
|
+
const signalsDir = join(getWorkspaceDir(), "signals");
|
|
65
|
+
mkdirSync(signalsDir, { recursive: true });
|
|
66
|
+
writeFileSync(join(signalsDir, "mcp-reload"), "");
|
|
67
|
+
} catch {
|
|
68
|
+
// Best-effort — the daemon may not be running or the directory may not exist.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
54
72
|
export function registerMcpCommand(program: Command): void {
|
|
55
73
|
const mcp = program
|
|
56
74
|
.command("mcp")
|
|
@@ -67,8 +85,8 @@ server uses one of three transport types:
|
|
|
67
85
|
sse Remote server using Server-Sent Events
|
|
68
86
|
streamable-http Remote server using Streamable HTTP transport
|
|
69
87
|
|
|
70
|
-
|
|
71
|
-
|
|
88
|
+
MCP server configuration changes are detected automatically by the running
|
|
89
|
+
assistant. You can also run 'vellum mcp reload' to trigger a manual reload.
|
|
72
90
|
|
|
73
91
|
Examples:
|
|
74
92
|
$ assistant mcp list
|
|
@@ -251,61 +269,20 @@ Examples:
|
|
|
251
269
|
.addHelpText(
|
|
252
270
|
"after",
|
|
253
271
|
`
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
272
|
+
Signals the running assistant to disconnect and reconnect all MCP servers
|
|
273
|
+
using the current configuration from disk. Active sessions pick up new tools
|
|
274
|
+
on their next turn automatically. The assistant must be running.
|
|
257
275
|
|
|
258
276
|
Examples:
|
|
259
277
|
$ vellum mcp reload
|
|
260
278
|
$ vellum mcp reload # after editing config.json to add a new server
|
|
261
279
|
$ vellum mcp reload # after running "vellum mcp auth <server>"`,
|
|
262
280
|
)
|
|
263
|
-
.action(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
success: boolean;
|
|
269
|
-
serverCount?: number;
|
|
270
|
-
toolCount?: number;
|
|
271
|
-
servers?: {
|
|
272
|
-
id: string;
|
|
273
|
-
connected: boolean;
|
|
274
|
-
disabled?: boolean;
|
|
275
|
-
toolCount: number;
|
|
276
|
-
tools: string[];
|
|
277
|
-
}[];
|
|
278
|
-
error?: string;
|
|
279
|
-
};
|
|
280
|
-
if (response.success) {
|
|
281
|
-
log.info(
|
|
282
|
-
`MCP servers reloaded: ${response.serverCount} server(s), ${response.toolCount} tool(s)\n`,
|
|
283
|
-
);
|
|
284
|
-
if (response.servers && response.servers.length > 0) {
|
|
285
|
-
for (const server of response.servers) {
|
|
286
|
-
const status = server.disabled
|
|
287
|
-
? "⊘ Disabled"
|
|
288
|
-
: server.connected
|
|
289
|
-
? "\u2713 Connected"
|
|
290
|
-
: "\u2717 Not connected";
|
|
291
|
-
log.info(` ${server.id}`);
|
|
292
|
-
log.info(` Status: ${status}`);
|
|
293
|
-
log.info(
|
|
294
|
-
` Tools: ${server.toolCount > 0 ? server.tools.join(", ") : "(none)"}`,
|
|
295
|
-
);
|
|
296
|
-
log.info("");
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
log.error(`Failed to reload: ${response.error}`);
|
|
301
|
-
process.exitCode = 1;
|
|
302
|
-
}
|
|
303
|
-
} catch (err) {
|
|
304
|
-
log.error(
|
|
305
|
-
`Failed to send reload request: ${err instanceof Error ? err.message : err}`,
|
|
306
|
-
);
|
|
307
|
-
process.exitCode = 1;
|
|
308
|
-
}
|
|
281
|
+
.action(() => {
|
|
282
|
+
signalMcpReload();
|
|
283
|
+
log.info(
|
|
284
|
+
"MCP reload signal sent. The running assistant will reconnect servers shortly.",
|
|
285
|
+
);
|
|
309
286
|
});
|
|
310
287
|
|
|
311
288
|
mcp
|
|
@@ -422,7 +399,10 @@ Examples:
|
|
|
422
399
|
|
|
423
400
|
saveRawConfig(raw);
|
|
424
401
|
log.info(`Added MCP server "${name}" (${opts.transportType})`);
|
|
425
|
-
log.info(
|
|
402
|
+
log.info(
|
|
403
|
+
"The running assistant will pick up this change automatically. " +
|
|
404
|
+
"Or run 'vellum mcp reload' to apply now.",
|
|
405
|
+
);
|
|
426
406
|
},
|
|
427
407
|
);
|
|
428
408
|
|
|
@@ -444,8 +424,8 @@ OAuth flow. If the server already has valid cached tokens, the command succeeds
|
|
|
444
424
|
immediately without opening a browser. Tokens are cached locally for future use
|
|
445
425
|
by the assistant.
|
|
446
426
|
|
|
447
|
-
After successful authentication,
|
|
448
|
-
|
|
427
|
+
After successful authentication, the running assistant detects the change
|
|
428
|
+
automatically. You can also run 'vellum mcp reload' to apply immediately.
|
|
449
429
|
|
|
450
430
|
Examples:
|
|
451
431
|
$ assistant mcp auth my-server
|
|
@@ -603,7 +583,11 @@ Examples:
|
|
|
603
583
|
provider.stopCallbackServer();
|
|
604
584
|
|
|
605
585
|
log.info(`Authentication successful for "${name}".`);
|
|
606
|
-
log.info(
|
|
586
|
+
log.info(
|
|
587
|
+
"The running assistant will pick up this change automatically. " +
|
|
588
|
+
"Or run 'vellum mcp reload' to apply now.",
|
|
589
|
+
);
|
|
590
|
+
signalMcpReload();
|
|
607
591
|
process.exit(0);
|
|
608
592
|
});
|
|
609
593
|
|
|
@@ -621,8 +605,8 @@ any stored OAuth credentials (tokens, client info, discovery metadata) for
|
|
|
621
605
|
sse/streamable-http servers. If no OAuth credentials exist, the cleanup is
|
|
622
606
|
silently skipped.
|
|
623
607
|
|
|
624
|
-
After removal,
|
|
625
|
-
|
|
608
|
+
After removal, the running assistant detects the change automatically. You
|
|
609
|
+
can also run 'vellum mcp reload' to apply immediately.
|
|
626
610
|
|
|
627
611
|
Examples:
|
|
628
612
|
$ assistant mcp remove my-server
|
|
@@ -655,6 +639,9 @@ Examples:
|
|
|
655
639
|
delete servers[name];
|
|
656
640
|
saveRawConfig(raw);
|
|
657
641
|
log.info(`Removed MCP server "${name}".`);
|
|
658
|
-
log.info(
|
|
642
|
+
log.info(
|
|
643
|
+
"The running assistant will pick up this change automatically. " +
|
|
644
|
+
"Or run 'vellum mcp reload' to apply now.",
|
|
645
|
+
);
|
|
659
646
|
});
|
|
660
647
|
}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
dismissPendingConflicts,
|
|
5
4
|
getMemorySystemStatus,
|
|
6
5
|
queryMemory,
|
|
7
6
|
requestMemoryBackfill,
|
|
8
7
|
requestMemoryCleanup,
|
|
9
8
|
requestMemoryRebuildIndex,
|
|
10
9
|
} from "../../memory/admin.js";
|
|
11
|
-
import { listPendingConflictDetails } from "../../memory/conflict-store.js";
|
|
12
10
|
import { listConversations } from "../../memory/conversation-queries.js";
|
|
13
|
-
import { rawGet } from "../../memory/db.js";
|
|
14
11
|
import { initializeDb } from "../db.js";
|
|
15
12
|
import { log } from "../logger.js";
|
|
16
13
|
|
|
17
|
-
const SHORT_HASH_LENGTH = 8;
|
|
18
|
-
|
|
19
14
|
export function registerMemoryCommand(program: Command): void {
|
|
20
15
|
const memory = program
|
|
21
16
|
.command("memory")
|
|
@@ -24,23 +19,20 @@ export function registerMemoryCommand(program: Command): void {
|
|
|
24
19
|
memory.addHelpText(
|
|
25
20
|
"after",
|
|
26
21
|
`
|
|
27
|
-
The memory subsystem indexes conversation segments
|
|
28
|
-
and vector embeddings for semantic recall
|
|
29
|
-
|
|
30
|
-
"pending_clarification" status until explicitly dismissed or resolved.
|
|
22
|
+
The memory subsystem indexes conversation segments using hybrid search (dense
|
|
23
|
+
and sparse vector embeddings) for semantic recall, with tier classification
|
|
24
|
+
to prioritize high-value memories.
|
|
31
25
|
|
|
32
26
|
Key concepts:
|
|
33
27
|
segments Chunks of conversation text extracted for indexing
|
|
34
28
|
items Distilled facts/statements derived from segments
|
|
35
29
|
summaries Compressed representations of conversation history
|
|
36
30
|
embeddings Vector representations used for semantic similarity search
|
|
37
|
-
conflicts Pairs of contradictory statements awaiting resolution
|
|
38
31
|
|
|
39
32
|
Examples:
|
|
40
33
|
$ assistant memory status
|
|
41
34
|
$ assistant memory query "What is the project deadline?"
|
|
42
|
-
$ assistant memory backfill
|
|
43
|
-
$ assistant memory dismiss-conflicts --all`,
|
|
35
|
+
$ assistant memory backfill`,
|
|
44
36
|
);
|
|
45
37
|
|
|
46
38
|
memory
|
|
@@ -59,10 +51,7 @@ Fields shown:
|
|
|
59
51
|
items Total distilled fact items stored
|
|
60
52
|
summaries Total compressed conversation summaries
|
|
61
53
|
embeddings Total vector embeddings computed
|
|
62
|
-
|
|
63
|
-
resolved conflicts Conflicts that have been dismissed or resolved
|
|
64
|
-
oldest pending age How long the oldest unresolved conflict has been waiting
|
|
65
|
-
cleanup backlogs Number of resolved conflicts and superseded items pending cleanup
|
|
54
|
+
cleanup backlogs Number of superseded items pending cleanup
|
|
66
55
|
cleanup throughput Number of cleanup operations completed in the last 24 hours
|
|
67
56
|
jobs Status of background jobs (backfill, cleanup, rebuild-index)
|
|
68
57
|
|
|
@@ -84,29 +73,9 @@ Examples:
|
|
|
84
73
|
log.info(`Items: ${status.counts.items.toLocaleString()}`);
|
|
85
74
|
log.info(`Summaries: ${status.counts.summaries.toLocaleString()}`);
|
|
86
75
|
log.info(`Embeddings: ${status.counts.embeddings.toLocaleString()}`);
|
|
87
|
-
log.info(
|
|
88
|
-
`Pending conflicts: ${status.conflicts.pending.toLocaleString()}`,
|
|
89
|
-
);
|
|
90
|
-
log.info(
|
|
91
|
-
`Resolved conflicts: ${status.conflicts.resolved.toLocaleString()}`,
|
|
92
|
-
);
|
|
93
|
-
if (status.conflicts.oldestPendingAgeMs != null) {
|
|
94
|
-
const oldestMinutes = Math.floor(
|
|
95
|
-
status.conflicts.oldestPendingAgeMs / 60_000,
|
|
96
|
-
);
|
|
97
|
-
log.info(`Oldest pending conflict age: ${oldestMinutes} min`);
|
|
98
|
-
} else {
|
|
99
|
-
log.info("Oldest pending conflict age: n/a");
|
|
100
|
-
}
|
|
101
|
-
log.info(
|
|
102
|
-
`Cleanup backlog (resolved conflicts): ${status.cleanup.resolvedBacklog.toLocaleString()}`,
|
|
103
|
-
);
|
|
104
76
|
log.info(
|
|
105
77
|
`Cleanup backlog (superseded items): ${status.cleanup.supersededBacklog.toLocaleString()}`,
|
|
106
78
|
);
|
|
107
|
-
log.info(
|
|
108
|
-
`Cleanup throughput 24h (resolved conflicts): ${status.cleanup.resolvedCompleted24h.toLocaleString()}`,
|
|
109
|
-
);
|
|
110
79
|
log.info(
|
|
111
80
|
`Cleanup throughput 24h (superseded items): ${status.cleanup.supersededCompleted24h.toLocaleString()}`,
|
|
112
81
|
);
|
|
@@ -123,8 +92,8 @@ Examples:
|
|
|
123
92
|
.addHelpText(
|
|
124
93
|
"after",
|
|
125
94
|
`
|
|
126
|
-
Queues a background job to index unprocessed conversation segments into
|
|
127
|
-
|
|
95
|
+
Queues a background job to index unprocessed conversation segments into
|
|
96
|
+
vector embeddings. The job resumes from where the last backfill left off,
|
|
128
97
|
processing only new or unindexed segments.
|
|
129
98
|
|
|
130
99
|
The --force flag restarts the backfill from the very beginning, reprocessing
|
|
@@ -144,7 +113,7 @@ Examples:
|
|
|
144
113
|
memory
|
|
145
114
|
.command("cleanup")
|
|
146
115
|
.description(
|
|
147
|
-
"Queue cleanup jobs for
|
|
116
|
+
"Queue cleanup jobs for stale superseded items",
|
|
148
117
|
)
|
|
149
118
|
.option(
|
|
150
119
|
"--retention-ms <ms>",
|
|
@@ -153,11 +122,8 @@ Examples:
|
|
|
153
122
|
.addHelpText(
|
|
154
123
|
"after",
|
|
155
124
|
`
|
|
156
|
-
Queues
|
|
157
|
-
|
|
158
|
-
dismissed or resolved past the retention threshold.
|
|
159
|
-
2. Stale superseded items cleanup — removes memory items that have been
|
|
160
|
-
superseded by newer, corrected facts past the retention threshold.
|
|
125
|
+
Queues a background cleanup job to remove memory items that have been
|
|
126
|
+
superseded by newer, corrected facts past the retention threshold.
|
|
161
127
|
|
|
162
128
|
The optional --retention-ms flag sets the minimum age (in milliseconds) a
|
|
163
129
|
record must have before it is eligible for cleanup. If omitted, the system
|
|
@@ -175,9 +141,6 @@ Examples:
|
|
|
175
141
|
const jobs = requestMemoryCleanup(
|
|
176
142
|
Number.isFinite(retentionMs) ? retentionMs : undefined,
|
|
177
143
|
);
|
|
178
|
-
log.info(
|
|
179
|
-
`Queued cleanup_resolved_conflicts job: ${jobs.resolvedConflictsJobId}`,
|
|
180
|
-
);
|
|
181
144
|
log.info(
|
|
182
145
|
`Queued cleanup_stale_superseded_items job: ${jobs.staleSupersededItemsJobId}`,
|
|
183
146
|
);
|
|
@@ -195,8 +158,8 @@ Examples:
|
|
|
195
158
|
Arguments:
|
|
196
159
|
text The recall query string used to search memory (e.g. "What is the
|
|
197
160
|
project deadline?"). Matched against indexed segments using the full
|
|
198
|
-
recall pipeline:
|
|
199
|
-
(time-weighted)
|
|
161
|
+
recall pipeline: semantic (dense + sparse vector similarity) and recency
|
|
162
|
+
(time-weighted).
|
|
200
163
|
|
|
201
164
|
Runs the complete memory recall pipeline and displays hit counts for each
|
|
202
165
|
retrieval strategy, the total injected token count, query latency, and the
|
|
@@ -221,10 +184,8 @@ Examples:
|
|
|
221
184
|
if (result.degraded) {
|
|
222
185
|
log.info(`Memory degraded: ${result.reason ?? "unknown reason"}`);
|
|
223
186
|
}
|
|
224
|
-
log.info(`Lexical hits: ${result.lexicalHits}`);
|
|
225
187
|
log.info(`Semantic hits: ${result.semanticHits}`);
|
|
226
188
|
log.info(`Recency hits: ${result.recencyHits}`);
|
|
227
|
-
log.info(`Entity hits: ${result.entityHits}`);
|
|
228
189
|
log.info(`Injected tokens: ${result.injectedTokens}`);
|
|
229
190
|
log.info(`Latency: ${result.latencyMs}ms`);
|
|
230
191
|
if (result.injectedText.length > 0) {
|
|
@@ -237,13 +198,13 @@ Examples:
|
|
|
237
198
|
|
|
238
199
|
memory
|
|
239
200
|
.command("rebuild-index")
|
|
240
|
-
.description("Queue a memory
|
|
201
|
+
.description("Queue a memory embedding index rebuild job")
|
|
241
202
|
.addHelpText(
|
|
242
203
|
"after",
|
|
243
204
|
`
|
|
244
|
-
Queues a background job that performs a full rebuild of
|
|
245
|
-
|
|
246
|
-
|
|
205
|
+
Queues a background job that performs a full rebuild of the vector embedding
|
|
206
|
+
index. All existing index data is dropped and reconstructed from the source
|
|
207
|
+
memory items.
|
|
247
208
|
|
|
248
209
|
This is useful after schema changes, embedding model upgrades, or if index
|
|
249
210
|
corruption is suspected. The rebuild runs asynchronously; use "assistant memory
|
|
@@ -258,114 +219,4 @@ Examples:
|
|
|
258
219
|
const jobId = requestMemoryRebuildIndex();
|
|
259
220
|
log.info(`Queued rebuild-index job: ${jobId}`);
|
|
260
221
|
});
|
|
261
|
-
|
|
262
|
-
memory
|
|
263
|
-
.command("dismiss-conflicts")
|
|
264
|
-
.description("Dismiss pending memory conflicts (all or matching a pattern)")
|
|
265
|
-
.option("-a, --all", "Dismiss all pending conflicts")
|
|
266
|
-
.option(
|
|
267
|
-
"-p, --pattern <regex>",
|
|
268
|
-
"Dismiss conflicts where either statement matches this regex",
|
|
269
|
-
)
|
|
270
|
-
.option("-s, --scope <id>", 'Memory scope (default: "default")')
|
|
271
|
-
.option("--dry-run", "Show what would be dismissed without making changes")
|
|
272
|
-
.addHelpText(
|
|
273
|
-
"after",
|
|
274
|
-
`
|
|
275
|
-
Two modes of operation:
|
|
276
|
-
--all Dismiss every pending conflict in the scope
|
|
277
|
-
--pattern <regex> Dismiss only conflicts where either the existing or
|
|
278
|
-
candidate statement matches the given regex (case-insensitive)
|
|
279
|
-
|
|
280
|
-
At least one of --all or --pattern must be provided. If both are given,
|
|
281
|
-
--all takes priority and all pending conflicts are dismissed.
|
|
282
|
-
|
|
283
|
-
The --scope flag targets a specific memory scope. Defaults to "default" if
|
|
284
|
-
omitted. The --dry-run flag previews which conflicts would be dismissed
|
|
285
|
-
without actually modifying any records.
|
|
286
|
-
|
|
287
|
-
Examples:
|
|
288
|
-
$ assistant memory dismiss-conflicts --all
|
|
289
|
-
$ assistant memory dismiss-conflicts --pattern "project deadline" --dry-run
|
|
290
|
-
$ assistant memory dismiss-conflicts --pattern "^preferred\\b" --scope work`,
|
|
291
|
-
)
|
|
292
|
-
.action(
|
|
293
|
-
(opts: {
|
|
294
|
-
all?: boolean;
|
|
295
|
-
pattern?: string;
|
|
296
|
-
scope?: string;
|
|
297
|
-
dryRun?: boolean;
|
|
298
|
-
}) => {
|
|
299
|
-
if (!opts.all && !opts.pattern) {
|
|
300
|
-
log.info("At least one of --all or --pattern must be provided.");
|
|
301
|
-
log.info("Use --dry-run to preview without making changes.");
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
initializeDb();
|
|
306
|
-
|
|
307
|
-
const pattern = opts.pattern
|
|
308
|
-
? new RegExp(opts.pattern, "i")
|
|
309
|
-
: undefined;
|
|
310
|
-
|
|
311
|
-
if (opts.dryRun) {
|
|
312
|
-
const scopeId = opts.scope ?? "default";
|
|
313
|
-
const totalPending =
|
|
314
|
-
rawGet<{ c: number }>(
|
|
315
|
-
`SELECT COUNT(*) AS c FROM memory_item_conflicts WHERE scope_id = ? AND status = 'pending_clarification'`,
|
|
316
|
-
scopeId,
|
|
317
|
-
)?.c ?? 0;
|
|
318
|
-
|
|
319
|
-
// Show a sample of conflicts (can't paginate without dismissing)
|
|
320
|
-
const sample = listPendingConflictDetails(scopeId, 1000);
|
|
321
|
-
let matchCount = 0;
|
|
322
|
-
for (const conflict of sample) {
|
|
323
|
-
const matches =
|
|
324
|
-
opts.all ||
|
|
325
|
-
(pattern &&
|
|
326
|
-
(pattern.test(conflict.existingStatement) ||
|
|
327
|
-
pattern.test(conflict.candidateStatement)));
|
|
328
|
-
if (!matches) continue;
|
|
329
|
-
matchCount++;
|
|
330
|
-
log.info(
|
|
331
|
-
` [${conflict.id.slice(0, SHORT_HASH_LENGTH)}] "${
|
|
332
|
-
conflict.existingStatement
|
|
333
|
-
}" vs "${conflict.candidateStatement}"`,
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (opts.all) {
|
|
338
|
-
// --all matches everything, so matchCount is just the sample size
|
|
339
|
-
log.info(
|
|
340
|
-
`\nDry run: ${totalPending} of ${totalPending} pending conflicts would be dismissed.`,
|
|
341
|
-
);
|
|
342
|
-
} else {
|
|
343
|
-
const moreNote =
|
|
344
|
-
totalPending > sample.length
|
|
345
|
-
? ` (showing first ${sample.length} of ${totalPending})`
|
|
346
|
-
: "";
|
|
347
|
-
log.info(
|
|
348
|
-
`\nDry run: ${matchCount} of ${totalPending} pending conflicts would be dismissed.${moreNote}`,
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const result = dismissPendingConflicts({
|
|
355
|
-
all: opts.all,
|
|
356
|
-
pattern,
|
|
357
|
-
scopeId: opts.scope,
|
|
358
|
-
});
|
|
359
|
-
for (const detail of result.details) {
|
|
360
|
-
log.info(
|
|
361
|
-
` Dismissed [${detail.id.slice(0, SHORT_HASH_LENGTH)}]: "${
|
|
362
|
-
detail.existingStatement
|
|
363
|
-
}" vs "${detail.candidateStatement}"`,
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
log.info(
|
|
367
|
-
`\nDismissed ${result.dismissed} conflicts. ${result.remaining} pending conflicts remain.`,
|
|
368
|
-
);
|
|
369
|
-
},
|
|
370
|
-
);
|
|
371
222
|
}
|
|
@@ -165,6 +165,10 @@ At least --id or --provider must be specified.`,
|
|
|
165
165
|
"--client-secret <secret>",
|
|
166
166
|
"OAuth client secret (stored in secure keychain)",
|
|
167
167
|
)
|
|
168
|
+
.option(
|
|
169
|
+
"--client-secret-credential-path <path>",
|
|
170
|
+
"Path to an existing client secret in the credential store (mutually exclusive with --client-secret)",
|
|
171
|
+
)
|
|
168
172
|
.addHelpText(
|
|
169
173
|
"after",
|
|
170
174
|
`
|
|
@@ -175,21 +179,46 @@ stored in the secure system keychain — not in the database.
|
|
|
175
179
|
When an existing app is matched and a --client-secret is provided, the stored
|
|
176
180
|
secret is updated. The app row itself is returned as-is.
|
|
177
181
|
|
|
182
|
+
You can supply the client secret directly via --client-secret, or reference an
|
|
183
|
+
existing credential in the store via --client-secret-credential-path. These two
|
|
184
|
+
options are mutually exclusive — providing both is an error.
|
|
185
|
+
|
|
178
186
|
Examples:
|
|
179
187
|
$ assistant oauth apps upsert --provider integration:gmail --client-id abc123
|
|
180
188
|
$ assistant oauth apps upsert --provider integration:slack --client-id def456 --client-secret s3cret
|
|
189
|
+
$ assistant oauth apps upsert --provider integration:slack --client-id def456 --client-secret-credential-path "custom/path"
|
|
181
190
|
$ assistant oauth apps upsert --provider integration:gmail --client-id abc123 --json`,
|
|
182
191
|
)
|
|
183
192
|
.action(
|
|
184
193
|
async (
|
|
185
|
-
opts: {
|
|
194
|
+
opts: {
|
|
195
|
+
provider: string;
|
|
196
|
+
clientId: string;
|
|
197
|
+
clientSecret?: string;
|
|
198
|
+
clientSecretCredentialPath?: string;
|
|
199
|
+
},
|
|
186
200
|
cmd: Command,
|
|
187
201
|
) => {
|
|
188
202
|
try {
|
|
203
|
+
if (opts.clientSecret && opts.clientSecretCredentialPath) {
|
|
204
|
+
writeOutput(cmd, {
|
|
205
|
+
ok: false,
|
|
206
|
+
error:
|
|
207
|
+
"Cannot provide both --client-secret and --client-secret-credential-path",
|
|
208
|
+
});
|
|
209
|
+
process.exitCode = 1;
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const clientSecretOpts = opts.clientSecret
|
|
214
|
+
? { clientSecretValue: opts.clientSecret }
|
|
215
|
+
: opts.clientSecretCredentialPath
|
|
216
|
+
? { clientSecretCredentialPath: opts.clientSecretCredentialPath }
|
|
217
|
+
: undefined;
|
|
189
218
|
const row = await upsertApp(
|
|
190
219
|
opts.provider,
|
|
191
220
|
opts.clientId,
|
|
192
|
-
|
|
221
|
+
clientSecretOpts,
|
|
193
222
|
);
|
|
194
223
|
|
|
195
224
|
if (!shouldOutputJson(cmd)) {
|