@vellumai/assistant 0.4.35 → 0.4.37

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 (239) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +44 -49
  3. package/README.md +32 -20
  4. package/docs/architecture/keychain-broker.md +186 -0
  5. package/docs/architecture/security.md +110 -116
  6. package/docs/runbook-trusted-contacts.md +2 -2
  7. package/docs/skills.md +25 -25
  8. package/package.json +5 -2
  9. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
  10. package/src/__tests__/actor-token-service.test.ts +1 -0
  11. package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
  13. package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
  14. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  15. package/src/__tests__/bundle-scanner.test.ts +1 -1
  16. package/src/__tests__/channel-guardian.test.ts +102 -102
  17. package/src/__tests__/channel-invite-transport.test.ts +155 -256
  18. package/src/__tests__/channel-readiness-routes.test.ts +336 -0
  19. package/src/__tests__/checker.test.ts +6 -6
  20. package/src/__tests__/chrome-cdp.test.ts +350 -0
  21. package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
  22. package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
  23. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
  24. package/src/__tests__/config-loader-migration.test.ts +85 -0
  25. package/src/__tests__/conversation-pairing.test.ts +370 -5
  26. package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
  27. package/src/__tests__/credential-broker-server-use.test.ts +1 -10
  28. package/src/__tests__/credential-security-e2e.test.ts +7 -1
  29. package/src/__tests__/credential-security-invariants.test.ts +14 -20
  30. package/src/__tests__/credential-vault-unit.test.ts +1 -11
  31. package/src/__tests__/credential-vault.test.ts +5 -19
  32. package/src/__tests__/credentials-cli.test.ts +814 -0
  33. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
  34. package/src/__tests__/email-invite-adapter.test.ts +78 -0
  35. package/src/__tests__/email-service-config-fallback.test.ts +102 -0
  36. package/src/__tests__/encrypted-store.test.ts +6 -6
  37. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  38. package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
  39. package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
  40. package/src/__tests__/guardian-outbound-http.test.ts +53 -47
  41. package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
  42. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
  43. package/src/__tests__/handlers-telegram-config.test.ts +8 -2
  44. package/src/__tests__/handlers-twitter-config.test.ts +2 -2
  45. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
  46. package/src/__tests__/ingress-reconcile.test.ts +6 -0
  47. package/src/__tests__/intent-routing.test.ts +23 -4
  48. package/src/__tests__/invite-routes-http.test.ts +12 -0
  49. package/src/__tests__/ipc-snapshot.test.ts +8 -2
  50. package/src/__tests__/keychain-broker-client.test.ts +543 -0
  51. package/src/__tests__/llm-usage-store.test.ts +344 -0
  52. package/src/__tests__/mcp-client-auth.test.ts +2 -2
  53. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  54. package/src/__tests__/migration-transport.test.ts +49 -0
  55. package/src/__tests__/notification-broadcaster.test.ts +205 -5
  56. package/src/__tests__/notification-deep-link.test.ts +365 -1
  57. package/src/__tests__/oauth-connect-handler.test.ts +2 -2
  58. package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
  59. package/src/__tests__/proxy-approval-callback.test.ts +1 -1
  60. package/src/__tests__/recording-handler.test.ts +1 -1
  61. package/src/__tests__/recording-intent-handler.test.ts +6 -1
  62. package/src/__tests__/recording-state-machine.test.ts +1 -1
  63. package/src/__tests__/relay-server.test.ts +9 -1
  64. package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
  65. package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
  66. package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
  67. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
  68. package/src/__tests__/secret-onetime-send.test.ts +8 -2
  69. package/src/__tests__/secure-keys.test.ts +175 -216
  70. package/src/__tests__/session-confirmation-signals.test.ts +1 -1
  71. package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
  72. package/src/__tests__/session-queue.test.ts +2 -1
  73. package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
  74. package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
  75. package/src/__tests__/skill-feature-flags.test.ts +12 -9
  76. package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
  77. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  78. package/src/__tests__/skills.test.ts +34 -4
  79. package/src/__tests__/slack-channel-config.test.ts +2 -2
  80. package/src/__tests__/system-prompt.test.ts +26 -4
  81. package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
  82. package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
  83. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  84. package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
  85. package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
  86. package/src/__tests__/twitter-auth-handler.test.ts +2 -2
  87. package/src/__tests__/twitter-oauth-client.test.ts +1 -1
  88. package/src/__tests__/usage-routes.test.ts +339 -0
  89. package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
  90. package/src/agent/loop.ts +3 -0
  91. package/src/amazon/checkout.ts +0 -1
  92. package/src/approvals/guardian-request-resolvers.ts +9 -1
  93. package/src/bundler/app-bundler.ts +28 -12
  94. package/src/bundler/bundle-scanner.ts +1 -1
  95. package/src/bundler/bundle-signer.ts +3 -3
  96. package/src/bundler/manifest.ts +1 -1
  97. package/src/bundler/signature-verifier.ts +3 -3
  98. package/src/channels/config.ts +1 -1
  99. package/src/cli/AGENTS.md +63 -0
  100. package/src/cli/__tests__/notifications.test.ts +470 -0
  101. package/src/cli/amazon.ts +344 -167
  102. package/src/cli/audit.ts +85 -0
  103. package/src/cli/autonomy.ts +369 -0
  104. package/src/cli/channels.ts +51 -0
  105. package/src/cli/completions.ts +208 -0
  106. package/src/cli/config.ts +220 -0
  107. package/src/cli/contacts.ts +471 -0
  108. package/src/cli/credentials.ts +564 -0
  109. package/src/cli/default-action.ts +14 -0
  110. package/src/cli/dev.ts +131 -0
  111. package/src/cli/doctor.ts +398 -0
  112. package/src/cli/email.ts +494 -0
  113. package/src/cli/influencer.ts +72 -0
  114. package/src/cli/integrations.ts +248 -57
  115. package/src/cli/keys.ts +114 -0
  116. package/src/cli/map.ts +46 -54
  117. package/src/cli/mcp.ts +111 -3
  118. package/src/cli/{config-commands.ts → memory.ts} +134 -245
  119. package/src/cli/notifications.ts +407 -0
  120. package/src/cli/program.ts +65 -0
  121. package/src/cli/reference.ts +48 -0
  122. package/src/cli/sequence.ts +154 -0
  123. package/src/cli/sessions.ts +262 -0
  124. package/src/cli/trust.ts +175 -0
  125. package/src/cli/twitter.ts +323 -106
  126. package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
  127. package/src/config/bundled-skills/amazon/SKILL.md +2 -2
  128. package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
  129. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
  130. package/src/config/bundled-skills/contacts/SKILL.md +178 -10
  131. package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
  132. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
  133. package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
  134. package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
  135. package/src/config/bundled-tool-registry.ts +2 -0
  136. package/src/config/core-schema.ts +7 -0
  137. package/src/config/feature-flag-registry.json +16 -0
  138. package/src/config/loader.ts +26 -0
  139. package/src/config/schema.ts +4 -0
  140. package/src/config/skill-state.ts +0 -13
  141. package/src/config/system-prompt.ts +27 -0
  142. package/src/contacts/contact-store.ts +25 -0
  143. package/src/daemon/computer-use-session.ts +1 -1
  144. package/src/daemon/handlers/apps.ts +1 -0
  145. package/src/daemon/handlers/config-channels.ts +3 -3
  146. package/src/daemon/handlers/config-dispatch.ts +29 -0
  147. package/src/daemon/handlers/config-inbox.ts +4 -3
  148. package/src/daemon/handlers/config.ts +3 -43
  149. package/src/daemon/handlers/contacts.ts +34 -0
  150. package/src/daemon/handlers/index.ts +17 -3
  151. package/src/daemon/handlers/session-user-message.ts +7 -0
  152. package/src/daemon/handlers/sessions.ts +21 -2
  153. package/src/daemon/handlers/shared.ts +17 -0
  154. package/src/daemon/ipc-contract/apps.ts +2 -0
  155. package/src/daemon/ipc-contract/computer-use.ts +9 -0
  156. package/src/daemon/ipc-contract/contacts.ts +3 -3
  157. package/src/daemon/ipc-contract/inbox.ts +2 -0
  158. package/src/daemon/ipc-contract/messages.ts +4 -0
  159. package/src/daemon/ipc-contract/sessions.ts +8 -0
  160. package/src/daemon/ipc-contract-inventory.json +1 -0
  161. package/src/daemon/lifecycle.ts +0 -5
  162. package/src/daemon/ride-shotgun-handler.ts +139 -25
  163. package/src/daemon/session-agent-loop-handlers.ts +100 -0
  164. package/src/daemon/session-agent-loop.ts +72 -0
  165. package/src/daemon/session-tool-setup.ts +7 -0
  166. package/src/daemon/session.ts +23 -1
  167. package/src/daemon/tool-side-effects.ts +39 -1
  168. package/src/email/service.ts +59 -2
  169. package/src/index.ts +2 -60
  170. package/src/mcp/mcp-oauth-provider.ts +90 -8
  171. package/src/media/app-icon-generator.ts +86 -0
  172. package/src/memory/db-init.ts +11 -0
  173. package/src/memory/llm-usage-store.ts +186 -0
  174. package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
  175. package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
  176. package/src/memory/migrations/index.ts +2 -0
  177. package/src/memory/schema-migration.ts +1 -0
  178. package/src/memory/shared-app-links-store.ts +1 -1
  179. package/src/messaging/registry.ts +27 -0
  180. package/src/notifications/README.md +79 -70
  181. package/src/notifications/broadcaster.ts +2 -1
  182. package/src/notifications/conversation-pairing.ts +147 -13
  183. package/src/notifications/copy-composer.ts +7 -3
  184. package/src/notifications/destination-resolver.ts +14 -1
  185. package/src/notifications/emit-signal.ts +3 -2
  186. package/src/notifications/signal.ts +105 -1
  187. package/src/notifications/types.ts +16 -0
  188. package/src/permissions/checker.ts +29 -3
  189. package/src/permissions/prompter.ts +11 -3
  190. package/src/runtime/access-request-helper.ts +2 -1
  191. package/src/runtime/auth/route-policy.ts +7 -1
  192. package/src/runtime/channel-invite-transport.ts +40 -63
  193. package/src/runtime/channel-invite-transports/email.ts +13 -39
  194. package/src/runtime/channel-invite-transports/slack.ts +5 -34
  195. package/src/runtime/channel-invite-transports/sms.ts +8 -29
  196. package/src/runtime/channel-invite-transports/telegram.ts +69 -28
  197. package/src/runtime/channel-invite-transports/voice.ts +0 -7
  198. package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
  199. package/src/runtime/channel-readiness-service.ts +202 -45
  200. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
  201. package/src/runtime/guardian-outbound-actions.ts +8 -5
  202. package/src/runtime/http-server.ts +2 -0
  203. package/src/runtime/invite-instruction-generator.ts +178 -0
  204. package/src/runtime/invite-service.ts +22 -25
  205. package/src/runtime/migrations/migration-transport.ts +13 -0
  206. package/src/runtime/routes/app-routes.ts +1 -1
  207. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
  208. package/src/runtime/routes/channel-readiness-routes.ts +30 -11
  209. package/src/runtime/routes/contact-routes.ts +54 -26
  210. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
  211. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
  212. package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
  213. package/src/runtime/routes/integration-routes.ts +1 -1
  214. package/src/runtime/routes/invite-routes.ts +1 -1
  215. package/src/runtime/routes/secret-routes.ts +31 -7
  216. package/src/runtime/routes/twilio-routes.ts +32 -1
  217. package/src/runtime/routes/usage-routes.ts +114 -0
  218. package/src/runtime/tool-grant-request-helper.ts +2 -1
  219. package/src/security/encrypted-store.ts +9 -5
  220. package/src/security/keychain-broker-client.ts +393 -0
  221. package/src/security/secure-keys.ts +106 -321
  222. package/src/tools/apps/executors.ts +73 -0
  223. package/src/tools/browser/auto-navigate.ts +15 -6
  224. package/src/tools/browser/chrome-cdp.ts +211 -0
  225. package/src/tools/browser/network-recorder.test.ts +83 -0
  226. package/src/tools/browser/network-recorder.ts +8 -7
  227. package/src/tools/browser/x-auto-navigate.ts +12 -6
  228. package/src/tools/credentials/policy-types.ts +24 -0
  229. package/src/tools/credentials/vault.ts +22 -27
  230. package/src/tools/network/script-proxy/session-manager.ts +47 -3
  231. package/src/tools/permission-checker.ts +1 -0
  232. package/src/tools/types.ts +2 -0
  233. package/src/tools/ui-surface/definitions.ts +1 -2
  234. package/src/tools/watch/watch-state.ts +2 -0
  235. package/src/__tests__/key-migration.test.ts +0 -240
  236. package/src/__tests__/keychain.test.ts +0 -286
  237. package/src/cli/core-commands.ts +0 -899
  238. package/src/security/keychain-to-encrypted-migration.ts +0 -66
  239. package/src/security/keychain.ts +0 -490
@@ -1,12 +1,5 @@
1
1
  import type { Command } from "commander";
2
2
 
3
- import {
4
- API_KEY_PROVIDERS,
5
- getNestedValue,
6
- loadRawConfig,
7
- saveRawConfig,
8
- setNestedValue,
9
- } from "../config/loader.js";
10
3
  import {
11
4
  dismissPendingConflicts,
12
5
  getMemorySystemStatus,
@@ -18,255 +11,65 @@ import {
18
11
  import { listPendingConflictDetails } from "../memory/conflict-store.js";
19
12
  import { listConversations } from "../memory/conversation-store.js";
20
13
  import { initializeDb, rawGet } from "../memory/db.js";
21
- import {
22
- clearAllRules,
23
- getAllRules,
24
- removeRule,
25
- } from "../permissions/trust-store.js";
26
- import {
27
- deleteSecureKey,
28
- getSecureKey,
29
- setSecureKey,
30
- } from "../security/secure-keys.js";
31
14
  import { getCliLogger } from "../util/logger.js";
32
15
 
33
16
  const log = getCliLogger("cli");
34
17
 
35
18
  const SHORT_HASH_LENGTH = 8;
36
19
 
37
- export function registerConfigCommand(program: Command): void {
38
- const config = program.command("config").description("Manage configuration");
39
-
40
- config
41
- .command("set <key> <value>")
42
- .description(
43
- "Set a config value (supports dotted paths like apiKeys.anthropic)",
44
- )
45
- .action((key: string, value: string) => {
46
- const raw = loadRawConfig();
47
- // Try to parse as JSON for booleans/numbers, fall back to string
48
- let parsed: unknown = value;
49
- try {
50
- parsed = JSON.parse(value);
51
- } catch {
52
- // keep as string
53
- }
54
- setNestedValue(raw, key, parsed);
55
- saveRawConfig(raw);
56
- log.info(`Set ${key} = ${JSON.stringify(parsed)}`);
57
- });
58
-
59
- config
60
- .command("get <key>")
61
- .description("Get a config value (supports dotted paths)")
62
- .action((key: string) => {
63
- const raw = loadRawConfig();
64
- const value = getNestedValue(raw, key);
65
- if (value === undefined) {
66
- log.info(`(not set)`);
67
- } else {
68
- log.info(
69
- typeof value === "object"
70
- ? JSON.stringify(value, null, 2)
71
- : String(value),
72
- );
73
- }
74
- });
75
-
76
- config
77
- .command("list")
78
- .description("List all config values")
79
- .action(() => {
80
- const raw = loadRawConfig();
81
- if (Object.keys(raw).length === 0) {
82
- log.info("No configuration set");
83
- } else {
84
- log.info(JSON.stringify(raw, null, 2));
85
- }
86
- });
87
-
88
- config
89
- .command("validate-allowlist")
90
- .description("Validate regex patterns in secret-allowlist.json")
91
- .action(() => {
92
- const { validateAllowlistFile } =
93
- // eslint-disable-next-line @typescript-eslint/no-require-imports
94
- require("../security/secret-allowlist.js") as typeof import("../security/secret-allowlist.js");
95
- try {
96
- const errors = validateAllowlistFile();
97
- if (errors == null) {
98
- log.info("No secret-allowlist.json file found");
99
- return;
100
- }
101
- if (errors.length === 0) {
102
- log.info("All patterns in secret-allowlist.json are valid");
103
- return;
104
- }
105
- log.error(
106
- `Found ${errors.length} invalid pattern(s) in secret-allowlist.json:`,
107
- );
108
- for (const e of errors) {
109
- log.error(` [${e.index}] "${e.pattern}": ${e.message}`);
110
- }
111
- process.exit(1);
112
- } catch (err) {
113
- log.error(
114
- `Failed to read secret-allowlist.json: ${(err as Error).message}`,
115
- );
116
- process.exit(1);
117
- }
118
- });
119
- }
120
-
121
- export function registerKeysCommand(program: Command): void {
122
- const keys = program
123
- .command("keys")
124
- .description("Manage API keys in secure storage");
125
-
126
- keys
127
- .command("list")
128
- .description("List all stored API key names")
129
- .action(() => {
130
- const stored: string[] = [];
131
- for (const provider of API_KEY_PROVIDERS) {
132
- const value = getSecureKey(provider);
133
- if (value) stored.push(provider);
134
- }
135
- if (stored.length === 0) {
136
- log.info("No API keys stored");
137
- } else {
138
- for (const name of stored) {
139
- log.info(` ${name}`);
140
- }
141
- }
142
- });
143
-
144
- keys
145
- .command("set <provider> <key>")
146
- .description("Store an API key (e.g. vellum keys set anthropic sk-ant-...)")
147
- .action((provider: string, key: string) => {
148
- if (setSecureKey(provider, key)) {
149
- log.info(`Stored API key for "${provider}"`);
150
- } else {
151
- log.error(`Failed to store API key for "${provider}"`);
152
- process.exit(1);
153
- }
154
- });
155
-
156
- keys
157
- .command("delete <provider>")
158
- .description("Delete a stored API key")
159
- .action((provider: string) => {
160
- if (deleteSecureKey(provider)) {
161
- log.info(`Deleted API key for "${provider}"`);
162
- } else {
163
- log.error(`No API key found for "${provider}"`);
164
- process.exit(1);
165
- }
166
- });
167
- }
168
-
169
- export function registerTrustCommand(program: Command): void {
170
- const trust = program.command("trust").description("Manage trust rules");
171
-
172
- trust
173
- .command("list")
174
- .description("List all trust rules")
175
- .action(() => {
176
- const rules = getAllRules();
177
- if (rules.length === 0) {
178
- log.info("No trust rules");
179
- return;
180
- }
181
- const idW = 8;
182
- const toolW = 12;
183
- const patternW = 30;
184
- const scopeW = 20;
185
- const decW = 6;
186
- const priW = 4;
187
- log.info(
188
- "ID".padEnd(idW) +
189
- "Tool".padEnd(toolW) +
190
- "Pattern".padEnd(patternW) +
191
- "Scope".padEnd(scopeW) +
192
- "Dcn".padEnd(decW) +
193
- "Pri".padEnd(priW) +
194
- "Created",
195
- );
196
- log.info("-".repeat(idW + toolW + patternW + scopeW + decW + priW + 20));
197
- for (const r of rules) {
198
- const id = r.id.slice(0, SHORT_HASH_LENGTH);
199
- const created = new Date(r.createdAt).toISOString().slice(0, 10);
200
- log.info(
201
- id.padEnd(idW) +
202
- r.tool.padEnd(toolW) +
203
- r.pattern.slice(0, patternW - 2).padEnd(patternW) +
204
- r.scope.slice(0, scopeW - 2).padEnd(scopeW) +
205
- r.decision.slice(0, decW - 1).padEnd(decW) +
206
- String(r.priority).padEnd(priW) +
207
- created,
208
- );
209
- }
210
- });
211
-
212
- trust
213
- .command("remove <id>")
214
- .description("Remove a trust rule by ID (or prefix)")
215
- .action((id: string) => {
216
- const rules = getAllRules();
217
- const match = rules.find((r) => r.id.startsWith(id));
218
- if (!match) {
219
- log.error(`No rule found matching "${id}"`);
220
- process.exit(1);
221
- }
222
- try {
223
- removeRule(match.id);
224
- } catch (err) {
225
- log.error(err instanceof Error ? err.message : String(err));
226
- process.exit(1);
227
- }
228
- log.info(
229
- `Removed rule ${match.id.slice(0, SHORT_HASH_LENGTH)} (${match.tool}: ${
230
- match.pattern
231
- })`,
232
- );
233
- });
234
-
235
- trust
236
- .command("clear")
237
- .description("Remove all trust rules")
238
- .action(async () => {
239
- const rules = getAllRules();
240
- if (rules.length === 0) {
241
- log.info("No trust rules to clear");
242
- return;
243
- }
244
- const readline = await import("node:readline");
245
- const rl = readline.createInterface({
246
- input: process.stdin,
247
- output: process.stdout,
248
- });
249
- const answer = await new Promise<string>((resolve) => {
250
- rl.question(`Remove all ${rules.length} trust rules? (y/N) `, resolve);
251
- });
252
- rl.close();
253
- if (answer.toLowerCase() === "y") {
254
- clearAllRules();
255
- log.info(`Cleared ${rules.length} trust rules`);
256
- } else {
257
- log.info("Cancelled");
258
- }
259
- });
260
- }
261
-
262
20
  export function registerMemoryCommand(program: Command): void {
263
21
  const memory = program
264
22
  .command("memory")
265
23
  .description("Manage long-term memory indexing/retrieval");
266
24
 
25
+ memory.addHelpText(
26
+ "after",
27
+ `
28
+ The memory subsystem indexes conversation segments into full-text search (FTS)
29
+ and vector embeddings for semantic recall. When the assistant encounters new
30
+ information that contradicts a stored fact, a conflict is created and held in
31
+ "pending_clarification" status until explicitly dismissed or resolved.
32
+
33
+ Key concepts:
34
+ segments Chunks of conversation text extracted for indexing
35
+ items Distilled facts/statements derived from segments
36
+ summaries Compressed representations of conversation history
37
+ embeddings Vector representations used for semantic similarity search
38
+ conflicts Pairs of contradictory statements awaiting resolution
39
+
40
+ Examples:
41
+ $ vellum memory status
42
+ $ vellum memory query "What is the project deadline?"
43
+ $ vellum memory backfill
44
+ $ vellum memory dismiss-conflicts --all`,
45
+ );
46
+
267
47
  memory
268
48
  .command("status")
269
49
  .description("Show memory subsystem status")
50
+ .addHelpText(
51
+ "after",
52
+ `
53
+ Displays a comprehensive snapshot of the memory subsystem's health and counts.
54
+
55
+ Fields shown:
56
+ enabled/degraded Whether memory is active and whether it is running in a
57
+ degraded mode (e.g. missing embedding backend)
58
+ embedding backend The provider/model pair used for vector embeddings (or "none")
59
+ segments Total conversation segments indexed
60
+ items Total distilled fact items stored
61
+ summaries Total compressed conversation summaries
62
+ embeddings Total vector embeddings computed
63
+ pending conflicts Conflicts awaiting user resolution
64
+ resolved conflicts Conflicts that have been dismissed or resolved
65
+ oldest pending age How long the oldest unresolved conflict has been waiting
66
+ cleanup backlogs Number of resolved conflicts and superseded items pending cleanup
67
+ cleanup throughput Number of cleanup operations completed in the last 24 hours
68
+ jobs Status of background jobs (backfill, cleanup, rebuild-index)
69
+
70
+ Examples:
71
+ $ vellum memory status`,
72
+ )
270
73
  .action(() => {
271
74
  initializeDb();
272
75
  const status = getMemorySystemStatus();
@@ -318,6 +121,21 @@ export function registerMemoryCommand(program: Command): void {
318
121
  .command("backfill")
319
122
  .description("Queue a memory backfill job")
320
123
  .option("-f, --force", "Restart backfill from the beginning")
124
+ .addHelpText(
125
+ "after",
126
+ `
127
+ Queues a background job to index unprocessed conversation segments into FTS
128
+ and vector embeddings. The job resumes from where the last backfill left off,
129
+ processing only new or unindexed segments.
130
+
131
+ The --force flag restarts the backfill from the very beginning, reprocessing
132
+ all segments regardless of whether they have already been indexed. This is
133
+ useful after bulk imports or if the incremental state has become inconsistent.
134
+
135
+ Examples:
136
+ $ vellum memory backfill
137
+ $ vellum memory backfill --force`,
138
+ )
321
139
  .action((opts: { force?: boolean }) => {
322
140
  initializeDb();
323
141
  const jobId = requestMemoryBackfill(Boolean(opts?.force));
@@ -333,6 +151,23 @@ export function registerMemoryCommand(program: Command): void {
333
151
  "--retention-ms <ms>",
334
152
  "Optional retention threshold in milliseconds",
335
153
  )
154
+ .addHelpText(
155
+ "after",
156
+ `
157
+ Queues two background cleanup jobs:
158
+ 1. Resolved conflicts cleanup — removes conflict records that have been
159
+ dismissed or resolved past the retention threshold.
160
+ 2. Stale superseded items cleanup — removes memory items that have been
161
+ superseded by newer, corrected facts past the retention threshold.
162
+
163
+ The optional --retention-ms flag sets the minimum age (in milliseconds) a
164
+ record must have before it is eligible for cleanup. If omitted, the system
165
+ default retention period is used.
166
+
167
+ Examples:
168
+ $ vellum memory cleanup
169
+ $ vellum memory cleanup --retention-ms 86400000`,
170
+ )
336
171
  .action((opts: { retentionMs?: string }) => {
337
172
  initializeDb();
338
173
  const retentionMs = opts.retentionMs
@@ -355,6 +190,27 @@ export function registerMemoryCommand(program: Command): void {
355
190
  "Run a memory recall query and print the injected memory payload",
356
191
  )
357
192
  .option("-s, --session <id>", "Optional conversation/session ID")
193
+ .addHelpText(
194
+ "after",
195
+ `
196
+ Arguments:
197
+ text The recall query string used to search memory (e.g. "What is the
198
+ project deadline?"). Matched against indexed segments using the full
199
+ recall pipeline: lexical (FTS), semantic (vector similarity), recency
200
+ (time-weighted), and entity (named entity extraction).
201
+
202
+ Runs the complete memory recall pipeline and displays hit counts for each
203
+ retrieval strategy, the total injected token count, query latency, and the
204
+ assembled memory text that would be injected into context.
205
+
206
+ The optional --session flag provides a conversation/session ID for
207
+ context-aware recall. If omitted, the most recent conversation is used.
208
+
209
+ Examples:
210
+ $ vellum memory query "What is the project deadline?"
211
+ $ vellum memory query "preferred communication style" --session conv_abc123
212
+ $ vellum memory query "API rate limits"`,
213
+ )
358
214
  .action(async (text: string, opts?: { session?: string }) => {
359
215
  initializeDb();
360
216
  let sessionId = opts?.session;
@@ -383,6 +239,21 @@ export function registerMemoryCommand(program: Command): void {
383
239
  memory
384
240
  .command("rebuild-index")
385
241
  .description("Queue a memory FTS+embedding index rebuild job")
242
+ .addHelpText(
243
+ "after",
244
+ `
245
+ Queues a background job that performs a full rebuild of both the FTS (full-text
246
+ search) index and the vector embedding index. All existing index data is
247
+ dropped and reconstructed from the source memory items.
248
+
249
+ This is useful after schema changes, embedding model upgrades, or if index
250
+ corruption is suspected. The rebuild runs asynchronously; use "vellum memory
251
+ status" to monitor job progress.
252
+
253
+ Examples:
254
+ $ vellum memory rebuild-index
255
+ $ vellum memory status`,
256
+ )
386
257
  .action(() => {
387
258
  initializeDb();
388
259
  const jobId = requestMemoryRebuildIndex();
@@ -399,6 +270,26 @@ export function registerMemoryCommand(program: Command): void {
399
270
  )
400
271
  .option("-s, --scope <id>", 'Memory scope (default: "default")')
401
272
  .option("--dry-run", "Show what would be dismissed without making changes")
273
+ .addHelpText(
274
+ "after",
275
+ `
276
+ Two modes of operation:
277
+ --all Dismiss every pending conflict in the scope
278
+ --pattern <regex> Dismiss only conflicts where either the existing or
279
+ candidate statement matches the given regex (case-insensitive)
280
+
281
+ At least one of --all or --pattern must be provided. If both are given,
282
+ --all takes priority and all pending conflicts are dismissed.
283
+
284
+ The --scope flag targets a specific memory scope. Defaults to "default" if
285
+ omitted. The --dry-run flag previews which conflicts would be dismissed
286
+ without actually modifying any records.
287
+
288
+ Examples:
289
+ $ vellum memory dismiss-conflicts --all
290
+ $ vellum memory dismiss-conflicts --pattern "project deadline" --dry-run
291
+ $ vellum memory dismiss-conflicts --pattern "^preferred\\b" --scope work`,
292
+ )
402
293
  .action(
403
294
  (opts: {
404
295
  all?: boolean;
@@ -407,9 +298,7 @@ export function registerMemoryCommand(program: Command): void {
407
298
  dryRun?: boolean;
408
299
  }) => {
409
300
  if (!opts.all && !opts.pattern) {
410
- log.info(
411
- "Usage: vellum memory dismiss-conflicts --all OR --pattern <regex>",
412
- );
301
+ log.info("At least one of --all or --pattern must be provided.");
413
302
  log.info("Use --dry-run to preview without making changes.");
414
303
  return;
415
304
  }