@vellumai/assistant 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +68 -15
  3. package/Dockerfile +2 -2
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +42 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  9. package/openapi.yaml +539 -4
  10. package/package.json +5 -1
  11. package/src/__tests__/anthropic-provider.test.ts +160 -95
  12. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  13. package/src/__tests__/app-executors.test.ts +47 -1
  14. package/src/__tests__/app-source-watcher.test.ts +159 -0
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/checker.test.ts +138 -172
  17. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  18. package/src/__tests__/config-schema.test.ts +5 -0
  19. package/src/__tests__/context-overflow-approval.test.ts +5 -5
  20. package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
  21. package/src/__tests__/conversation-agent-loop.test.ts +4 -51
  22. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  23. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  24. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  25. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  26. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  27. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  28. package/src/__tests__/conversation-wipe.test.ts +2 -6
  29. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  30. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  31. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  32. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  33. package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
  34. package/src/__tests__/date-context.test.ts +76 -210
  35. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  36. package/src/__tests__/file-list-tool.test.ts +219 -0
  37. package/src/__tests__/first-greeting.test.ts +1 -1
  38. package/src/__tests__/heartbeat-service.test.ts +180 -3
  39. package/src/__tests__/identity-routes.test.ts +328 -0
  40. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  41. package/src/__tests__/injection-block.test.ts +24 -0
  42. package/src/__tests__/inline-command-runner.test.ts +7 -5
  43. package/src/__tests__/install-skill-routing.test.ts +7 -6
  44. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
  45. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  46. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  47. package/src/__tests__/llm-context-route-provider.test.ts +101 -0
  48. package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
  49. package/src/__tests__/log-export-workspace.test.ts +257 -100
  50. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  51. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  52. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  53. package/src/__tests__/memory-recall-log-store.test.ts +132 -0
  54. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  55. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  56. package/src/__tests__/mock-fetch.ts +87 -0
  57. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  58. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  59. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  60. package/src/__tests__/onboarding-template-contract.test.ts +63 -14
  61. package/src/__tests__/parser.test.ts +32 -0
  62. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  63. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  64. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  65. package/src/__tests__/permission-mode-store.test.ts +277 -0
  66. package/src/__tests__/permission-mode.test.ts +101 -0
  67. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  68. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  69. package/src/__tests__/profiler-routes.test.ts +502 -0
  70. package/src/__tests__/profiler-run-store.test.ts +441 -0
  71. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  72. package/src/__tests__/registry.test.ts +1 -1
  73. package/src/__tests__/require-fresh-approval.test.ts +0 -2
  74. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  75. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  76. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  77. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  78. package/src/__tests__/search-skills-unified.test.ts +4 -3
  79. package/src/__tests__/send-endpoint-busy.test.ts +42 -3
  80. package/src/__tests__/set-permission-mode.test.ts +274 -0
  81. package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
  82. package/src/__tests__/skill-memory.test.ts +2 -783
  83. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  84. package/src/__tests__/subagent-detail.test.ts +84 -0
  85. package/src/__tests__/subagent-disposal.test.ts +308 -0
  86. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  87. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  88. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  89. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  90. package/src/__tests__/subagent-tools.test.ts +464 -4
  91. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  92. package/src/__tests__/task-memory-cleanup.test.ts +12 -12
  93. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  94. package/src/__tests__/terminal-tools.test.ts +16 -29
  95. package/src/__tests__/test-preload.ts +18 -0
  96. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  98. package/src/__tests__/tool-executor.test.ts +4 -27
  99. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  100. package/src/__tests__/top-level-renderer.test.ts +10 -13
  101. package/src/__tests__/transport-hints-queue.test.ts +77 -0
  102. package/src/__tests__/trust-store.test.ts +4 -4
  103. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
  104. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  105. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  106. package/src/__tests__/workspace-policy.test.ts +2 -7
  107. package/src/agent/loop.ts +6 -29
  108. package/src/approvals/guardian-request-resolvers.ts +24 -0
  109. package/src/avatar/traits-png-sync.ts +3 -3
  110. package/src/channels/types.ts +5 -0
  111. package/src/cli/__tests__/run-assistant-command.ts +56 -0
  112. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  113. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  114. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  115. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  116. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  117. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  118. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  119. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  120. package/src/cli/commands/conversations.ts +1 -8
  121. package/src/cli/commands/default-action.ts +68 -1
  122. package/src/cli/commands/email.ts +584 -835
  123. package/src/cli/commands/memory.ts +1 -34
  124. package/src/cli/commands/notifications.ts +7 -2
  125. package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
  126. package/src/cli/commands/oauth/connect.ts +25 -5
  127. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  128. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  129. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  130. package/src/cli/commands/routes.ts +396 -0
  131. package/src/cli/commands/skills.ts +130 -20
  132. package/src/cli/program.ts +11 -2
  133. package/src/cli.ts +1 -120
  134. package/src/config/assistant-feature-flags.ts +59 -55
  135. package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
  136. package/src/config/bundled-skills/gmail/SKILL.md +13 -8
  137. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  138. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  139. package/src/config/bundled-skills/messaging/SKILL.md +7 -0
  140. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  141. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  142. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  143. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  144. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  145. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  146. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  147. package/src/config/bundled-skills/slack/SKILL.md +2 -0
  148. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  149. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  150. package/src/config/env-registry.ts +63 -0
  151. package/src/config/feature-flag-registry.json +17 -1
  152. package/src/config/schema.ts +8 -0
  153. package/src/config/schemas/filing.ts +51 -0
  154. package/src/config/schemas/heartbeat.ts +15 -12
  155. package/src/config/schemas/memory-lifecycle.ts +12 -0
  156. package/src/config/schemas/security.ts +14 -0
  157. package/src/config/schemas/services.ts +8 -0
  158. package/src/credential-execution/approval-bridge.ts +0 -1
  159. package/src/credential-execution/managed-catalog.ts +3 -7
  160. package/src/daemon/app-source-watcher.ts +93 -0
  161. package/src/daemon/config-watcher.ts +85 -3
  162. package/src/daemon/context-overflow-approval.ts +0 -1
  163. package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
  164. package/src/daemon/conversation-agent-loop.ts +179 -65
  165. package/src/daemon/conversation-attachments.ts +0 -1
  166. package/src/daemon/conversation-history.ts +4 -19
  167. package/src/daemon/conversation-lifecycle.ts +8 -14
  168. package/src/daemon/conversation-messaging.ts +3 -0
  169. package/src/daemon/conversation-process.ts +30 -8
  170. package/src/daemon/conversation-queue-manager.ts +8 -0
  171. package/src/daemon/conversation-runtime-assembly.ts +359 -308
  172. package/src/daemon/conversation-surfaces.ts +65 -0
  173. package/src/daemon/conversation-tool-setup.ts +44 -17
  174. package/src/daemon/conversation-workspace.ts +1 -2
  175. package/src/daemon/conversation.ts +19 -3
  176. package/src/daemon/date-context.ts +26 -53
  177. package/src/daemon/first-greeting.ts +1 -1
  178. package/src/daemon/handlers/conversations.ts +5 -7
  179. package/src/daemon/handlers/shared.test.ts +143 -0
  180. package/src/daemon/handlers/shared.ts +70 -5
  181. package/src/daemon/handlers/skills.ts +11 -18
  182. package/src/daemon/lifecycle.ts +220 -158
  183. package/src/daemon/message-types/conversations.ts +29 -6
  184. package/src/daemon/message-types/messages.ts +9 -2
  185. package/src/daemon/message-types/notifications.ts +12 -0
  186. package/src/daemon/message-types/schedules.ts +1 -0
  187. package/src/daemon/message-types/settings.ts +18 -0
  188. package/src/daemon/profiler-run-store.ts +557 -0
  189. package/src/daemon/server.ts +87 -10
  190. package/src/daemon/shutdown-handlers.ts +5 -0
  191. package/src/daemon/tool-side-effects.ts +23 -3
  192. package/src/daemon/transport-hints.ts +33 -0
  193. package/src/export/transcript-formatter.ts +148 -0
  194. package/src/filing/filing-service.ts +228 -0
  195. package/src/heartbeat/heartbeat-service.ts +96 -7
  196. package/src/index.ts +1 -1
  197. package/src/mcp/client.ts +6 -0
  198. package/src/mcp/mcp-oauth-provider.ts +149 -27
  199. package/src/memory/admin.ts +33 -32
  200. package/src/memory/app-store.ts +69 -0
  201. package/src/memory/conversation-bootstrap.ts +1 -1
  202. package/src/memory/conversation-crud.ts +151 -117
  203. package/src/memory/conversation-directories.ts +39 -0
  204. package/src/memory/conversation-group-migration.ts +66 -6
  205. package/src/memory/conversation-queries.ts +58 -12
  206. package/src/memory/conversation-title-service.ts +1 -0
  207. package/src/memory/db-init.ts +182 -376
  208. package/src/memory/embedding-local.ts +1 -1
  209. package/src/memory/graph/bootstrap.ts +75 -66
  210. package/src/memory/graph/capability-seed.ts +167 -17
  211. package/src/memory/graph/consolidation.ts +38 -4
  212. package/src/memory/graph/conversation-graph-memory.ts +133 -104
  213. package/src/memory/graph/extraction-job.ts +9 -4
  214. package/src/memory/graph/extraction.ts +66 -23
  215. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  216. package/src/memory/graph/graph-search.ts +29 -15
  217. package/src/memory/graph/injection.ts +38 -8
  218. package/src/memory/graph/inspect.ts +12 -3
  219. package/src/memory/graph/retriever.ts +365 -262
  220. package/src/memory/graph/store.test.ts +48 -0
  221. package/src/memory/graph/store.ts +150 -11
  222. package/src/memory/graph/tool-handlers.ts +84 -209
  223. package/src/memory/graph/tools.ts +8 -52
  224. package/src/memory/graph/types.ts +24 -0
  225. package/src/memory/group-crud.ts +25 -9
  226. package/src/memory/job-handlers/cleanup.ts +44 -1
  227. package/src/memory/jobs-store.ts +70 -60
  228. package/src/memory/jobs-worker.ts +44 -28
  229. package/src/memory/llm-request-log-store.ts +96 -12
  230. package/src/memory/memory-recall-log-store.ts +49 -5
  231. package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
  232. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  233. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  234. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  235. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  236. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  237. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  238. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  239. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  240. package/src/memory/migrations/index.ts +8 -0
  241. package/src/memory/migrations/registry.ts +8 -0
  242. package/src/memory/schema/conversations.ts +14 -0
  243. package/src/memory/schema/infrastructure.ts +8 -1
  244. package/src/memory/schema/memory-core.ts +0 -51
  245. package/src/memory/schema/memory-graph.ts +15 -0
  246. package/src/memory/task-memory-cleanup.ts +30 -11
  247. package/src/messaging/provider.ts +1 -1
  248. package/src/notifications/broadcaster.ts +6 -0
  249. package/src/notifications/conversation-pairing.ts +12 -4
  250. package/src/notifications/copy-composer.ts +86 -0
  251. package/src/notifications/decision-engine.ts +35 -0
  252. package/src/notifications/emit-signal.ts +14 -0
  253. package/src/notifications/signal.ts +11 -0
  254. package/src/oauth/platform-connection.test.ts +2 -2
  255. package/src/oauth/seed-providers.ts +1 -0
  256. package/src/permissions/checker.ts +15 -4
  257. package/src/permissions/defaults.ts +7 -8
  258. package/src/permissions/permission-mode-store.ts +180 -0
  259. package/src/permissions/permission-mode.ts +31 -0
  260. package/src/permissions/prompter.ts +0 -2
  261. package/src/permissions/workspace-policy.ts +9 -0
  262. package/src/platform/client.ts +1 -1
  263. package/src/prompts/system-prompt.ts +59 -7
  264. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  265. package/src/prompts/templates/BOOTSTRAP.md +76 -162
  266. package/src/prompts/templates/HEARTBEAT.md +3 -1
  267. package/src/prompts/templates/SOUL.md +30 -9
  268. package/src/prompts/templates/UPDATES.md +8 -0
  269. package/src/providers/anthropic/client.ts +107 -219
  270. package/src/runtime/assistant-event-hub.ts +22 -0
  271. package/src/runtime/auth/route-policy.ts +23 -0
  272. package/src/runtime/auth/token-service.ts +8 -0
  273. package/src/runtime/http-server.ts +32 -2
  274. package/src/runtime/http-types.ts +12 -1
  275. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  276. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  277. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  278. package/src/runtime/routes/app-management-routes.ts +1 -11
  279. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  280. package/src/runtime/routes/archive-utils.ts +29 -0
  281. package/src/runtime/routes/avatar-routes.ts +2 -9
  282. package/src/runtime/routes/btw-routes.ts +14 -1
  283. package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
  284. package/src/runtime/routes/conversation-management-routes.ts +1 -14
  285. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  286. package/src/runtime/routes/conversation-routes.ts +270 -44
  287. package/src/runtime/routes/group-routes.ts +22 -8
  288. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  289. package/src/runtime/routes/identity-routes.ts +53 -18
  290. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  291. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  292. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  293. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  294. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  295. package/src/runtime/routes/log-export-routes.ts +41 -278
  296. package/src/runtime/routes/memory-item-routes.test.ts +168 -233
  297. package/src/runtime/routes/migration-routes.ts +18 -7
  298. package/src/runtime/routes/profiler-routes.ts +350 -0
  299. package/src/runtime/routes/schedule-routes.ts +27 -12
  300. package/src/runtime/routes/settings-routes.ts +95 -8
  301. package/src/runtime/routes/subagents-routes.ts +28 -7
  302. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  303. package/src/runtime/routes/user-routes.ts +41 -0
  304. package/src/runtime/routes/workspace-routes.ts +0 -1
  305. package/src/schedule/schedule-store.ts +30 -0
  306. package/src/schedule/scheduler.ts +45 -18
  307. package/src/skills/catalog-install.ts +10 -2
  308. package/src/skills/inline-command-runner.ts +12 -14
  309. package/src/skills/managed-store.ts +2 -2
  310. package/src/skills/skill-memory.ts +1 -293
  311. package/src/subagent/index.ts +13 -3
  312. package/src/subagent/manager.ts +308 -29
  313. package/src/subagent/types.ts +68 -0
  314. package/src/tasks/task-runner.ts +4 -4
  315. package/src/tools/apps/executors.ts +29 -4
  316. package/src/tools/filesystem/list.ts +93 -0
  317. package/src/tools/permission-checker.ts +78 -18
  318. package/src/tools/registry.ts +4 -0
  319. package/src/tools/schedule/create.ts +3 -0
  320. package/src/tools/schedule/list.ts +1 -0
  321. package/src/tools/schedule/update.ts +6 -0
  322. package/src/tools/secret-detection-handler.ts +0 -1
  323. package/src/tools/shared/filesystem/errors.ts +5 -0
  324. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  325. package/src/tools/shared/filesystem/types.ts +17 -0
  326. package/src/tools/shared/shell-output.ts +31 -2
  327. package/src/tools/skills/sandbox-runner.ts +3 -6
  328. package/src/tools/subagent/abort.ts +12 -2
  329. package/src/tools/subagent/message.ts +9 -2
  330. package/src/tools/subagent/notify-parent.ts +79 -0
  331. package/src/tools/subagent/read.ts +29 -8
  332. package/src/tools/subagent/resolve.ts +21 -0
  333. package/src/tools/subagent/spawn.ts +2 -0
  334. package/src/tools/subagent/status.ts +11 -1
  335. package/src/tools/system/avatar-generator.ts +3 -3
  336. package/src/tools/system/register.ts +23 -0
  337. package/src/tools/system/set-permission-mode.ts +103 -0
  338. package/src/tools/terminal/parser.ts +30 -5
  339. package/src/tools/terminal/safe-env.ts +16 -1
  340. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  341. package/src/tools/terminal/sandbox.ts +4 -1
  342. package/src/tools/terminal/shell.ts +3 -5
  343. package/src/tools/tool-manifest.ts +6 -0
  344. package/src/tools/types.ts +2 -3
  345. package/src/util/logger.ts +1 -1
  346. package/src/util/platform.ts +50 -17
  347. package/src/watcher/provider-types.ts +1 -1
  348. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  349. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  350. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  351. package/src/workspace/migrations/029-seed-pkb.ts +85 -0
  352. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  353. package/src/workspace/migrations/registry.ts +6 -0
  354. package/src/workspace/top-level-renderer.ts +5 -9
  355. package/src/__tests__/cli-memory.test.ts +0 -377
  356. package/src/__tests__/clipboard.test.ts +0 -88
  357. package/src/cli/cli-memory.ts +0 -179
  358. package/src/util/clipboard.ts +0 -34
@@ -0,0 +1,390 @@
1
+ import { describe, expect, mock, test } from "bun:test";
2
+
3
+ // Mock conversation-crud before importing tool executors that depend on it.
4
+ mock.module("../memory/conversation-crud.js", () => ({
5
+ getConversationType: () => "default",
6
+ setConversationOriginChannelIfUnset: () => {},
7
+ updateConversationContextWindow: () => {},
8
+ deleteMessageById: () => {},
9
+ updateConversationTitle: () => {},
10
+ updateConversationUsage: () => {},
11
+ addMessage: () => ({ id: "mock-msg-id" }),
12
+ getConversation: () => ({
13
+ id: "conv-1",
14
+ contextSummary: null,
15
+ contextCompactedMessageCount: 0,
16
+ totalInputTokens: 0,
17
+ totalOutputTokens: 0,
18
+ totalEstimatedCost: 0,
19
+ title: null,
20
+ }),
21
+ provenanceFromTrustContext: () => ({
22
+ source: "user",
23
+ trustContext: undefined,
24
+ }),
25
+ getConversationOriginInterface: () => null,
26
+ getConversationOriginChannel: () => null,
27
+ getMessages: () => null,
28
+ createConversation: () => ({ id: "mock-conv" }),
29
+ }));
30
+
31
+ import { isToolActiveForContext } from "../daemon/conversation-tool-setup.js";
32
+ import { getSubagentManager } from "../subagent/index.js";
33
+ import { SubagentManager } from "../subagent/manager.js";
34
+ import type { SubagentState } from "../subagent/types.js";
35
+ import {
36
+ executeSubagentNotifyParent,
37
+ notifyParentTool,
38
+ } from "../tools/subagent/notify-parent.js";
39
+
40
+ // ── Shared helpers ──────────────────────────────────────────────────
41
+
42
+ /**
43
+ * Inject a fake subagent into the singleton manager so tool executors
44
+ * can find it. Uses the same private-internals trick as the other tests.
45
+ */
46
+ function injectSubagent(
47
+ manager: SubagentManager,
48
+ subagentId: string,
49
+ parentConversationId: string,
50
+ status: SubagentState["status"] = "running",
51
+ overrides: Partial<SubagentState> = {},
52
+ ): SubagentState {
53
+ const internals = manager as unknown as {
54
+ subagents: Map<
55
+ string,
56
+ {
57
+ conversation: unknown;
58
+ state: SubagentState;
59
+ parentSendToClient: () => void;
60
+ }
61
+ >;
62
+ parentToChildren: Map<string, Set<string>>;
63
+ };
64
+ const state: SubagentState = {
65
+ config: {
66
+ id: subagentId,
67
+ parentConversationId,
68
+ label: "Test",
69
+ objective: "test",
70
+ },
71
+ status,
72
+ conversationId: `conv-${subagentId}`,
73
+ createdAt: Date.now(),
74
+ usage: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
75
+ ...overrides,
76
+ };
77
+ const fakeConversation = {
78
+ abort: () => {},
79
+ dispose: () => {},
80
+ messages: [],
81
+ sendToClient: () => {},
82
+ usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
83
+ enqueueMessage: () => ({ queued: false }),
84
+ persistUserMessage: () => "msg-1",
85
+ runAgentLoop: async () => {},
86
+ };
87
+ internals.subagents.set(subagentId, {
88
+ conversation: fakeConversation,
89
+ state,
90
+ parentSendToClient: () => {},
91
+ });
92
+ if (!internals.parentToChildren.has(parentConversationId)) {
93
+ internals.parentToChildren.set(parentConversationId, new Set());
94
+ }
95
+ internals.parentToChildren.get(parentConversationId)!.add(subagentId);
96
+ return state;
97
+ }
98
+
99
+ function makeContext(
100
+ conversationId: string,
101
+ extras: Record<string, unknown> = {},
102
+ ) {
103
+ return {
104
+ workingDir: "/tmp",
105
+ conversationId,
106
+ trustClass: "guardian" as const,
107
+ ...extras,
108
+ } as import("../tools/types.js").ToolContext;
109
+ }
110
+
111
+ // ── Tool definition ────────────────────────────────────────────────
112
+
113
+ describe("notify_parent tool definition", () => {
114
+ test("has correct core tool definition", () => {
115
+ const def = notifyParentTool.getDefinition();
116
+ const schema = def.input_schema as Record<string, unknown>;
117
+ expect(def.name).toBe("notify_parent");
118
+ expect(schema.required).toContain("message");
119
+ expect(
120
+ (schema.properties as Record<string, Record<string, unknown>>).urgency
121
+ .enum,
122
+ ).toEqual([
123
+ "info",
124
+ "important",
125
+ "blocked",
126
+ ]);
127
+ expect(notifyParentTool.category).toBe("orchestration");
128
+ });
129
+
130
+ test("is hidden from non-subagent context", () => {
131
+ const ctx = {
132
+ isSubagent: false,
133
+ preactivatedSkillIds: [],
134
+ skillProjectionState: new Map(),
135
+ skillProjectionCache: new Map(),
136
+ coreToolNames: new Set<string>(),
137
+ toolsDisabledDepth: 0,
138
+ } as unknown as import("../daemon/conversation-tool-setup.js").SkillProjectionContext;
139
+ expect(isToolActiveForContext("notify_parent", ctx)).toBe(false);
140
+ });
141
+
142
+ test("is hidden when isSubagent is undefined", () => {
143
+ const ctx = {
144
+ preactivatedSkillIds: [],
145
+ skillProjectionState: new Map(),
146
+ skillProjectionCache: new Map(),
147
+ coreToolNames: new Set<string>(),
148
+ toolsDisabledDepth: 0,
149
+ } as unknown as import("../daemon/conversation-tool-setup.js").SkillProjectionContext;
150
+ expect(isToolActiveForContext("notify_parent", ctx)).toBe(false);
151
+ });
152
+
153
+ test("is visible to subagent context", () => {
154
+ const ctx = {
155
+ isSubagent: true,
156
+ preactivatedSkillIds: [],
157
+ skillProjectionState: new Map(),
158
+ skillProjectionCache: new Map(),
159
+ coreToolNames: new Set<string>(),
160
+ toolsDisabledDepth: 0,
161
+ } as unknown as import("../daemon/conversation-tool-setup.js").SkillProjectionContext;
162
+ expect(isToolActiveForContext("notify_parent", ctx)).toBe(true);
163
+ });
164
+ });
165
+
166
+ // ── executeSubagentNotifyParent ────────────────────────────────────
167
+
168
+ describe("executeSubagentNotifyParent", () => {
169
+ test("rejects calls from non-subagent conversations", async () => {
170
+ const result = await executeSubagentNotifyParent(
171
+ { message: "Found something important" },
172
+ makeContext("not-a-subagent-conv"),
173
+ );
174
+ expect(result.isError).toBe(true);
175
+ expect(result.content).toContain("Could not notify parent");
176
+ expect(result.content).toContain("only available to subagents");
177
+ });
178
+
179
+ test("succeeds when called from a subagent conversation", async () => {
180
+ const manager = getSubagentManager();
181
+ const subagentId = "notify-sub-1";
182
+ const parentConversationId = "notify-parent-1";
183
+ injectSubagent(manager, subagentId, parentConversationId, "running");
184
+
185
+ // Wire up the onSubagentFinished callback.
186
+ let capturedMessage = "";
187
+ manager.onSubagentFinished = (
188
+ _parentId: string,
189
+ message: string,
190
+ ) => {
191
+ capturedMessage = message;
192
+ };
193
+
194
+ try {
195
+ const result = await executeSubagentNotifyParent(
196
+ { message: "Found key results", urgency: "important" },
197
+ makeContext(`conv-${subagentId}`),
198
+ );
199
+ expect(result.isError).toBe(false);
200
+ const parsed = JSON.parse(result.content);
201
+ expect(parsed.sent).toBe(true);
202
+ expect(parsed.urgency).toBe("important");
203
+ expect(capturedMessage).toContain("Found key results");
204
+ } finally {
205
+ manager.onSubagentFinished = undefined;
206
+ }
207
+ });
208
+
209
+ test("formats message with label and urgency", async () => {
210
+ const manager = getSubagentManager();
211
+ const subagentId = "notify-format-1";
212
+ const parentConversationId = "notify-format-parent";
213
+ injectSubagent(manager, subagentId, parentConversationId, "running", {
214
+ config: {
215
+ id: subagentId,
216
+ parentConversationId,
217
+ label: "Research Task",
218
+ objective: "research",
219
+ },
220
+ });
221
+
222
+ let capturedMessage = "";
223
+ manager.onSubagentFinished = (
224
+ _parentId: string,
225
+ message: string,
226
+ ) => {
227
+ capturedMessage = message;
228
+ };
229
+
230
+ try {
231
+ await executeSubagentNotifyParent(
232
+ { message: "Preliminary findings ready", urgency: "info" },
233
+ makeContext(`conv-${subagentId}`),
234
+ );
235
+ expect(capturedMessage).toBe(
236
+ '[Subagent "Research Task" — info] Preliminary findings ready',
237
+ );
238
+ } finally {
239
+ manager.onSubagentFinished = undefined;
240
+ }
241
+ });
242
+
243
+ test("returns error when message is empty", async () => {
244
+ const result = await executeSubagentNotifyParent(
245
+ { message: "" },
246
+ makeContext("some-conv"),
247
+ );
248
+ expect(result.isError).toBe(true);
249
+ expect(result.content).toContain('"message" is required');
250
+ });
251
+
252
+ test("returns error when message is missing", async () => {
253
+ const result = await executeSubagentNotifyParent(
254
+ {},
255
+ makeContext("some-conv"),
256
+ );
257
+ expect(result.isError).toBe(true);
258
+ expect(result.content).toContain('"message" is required');
259
+ });
260
+
261
+ test("defaults urgency to info when not provided", async () => {
262
+ const manager = getSubagentManager();
263
+ const subagentId = "notify-default-urg-1";
264
+ const parentConversationId = "notify-default-urg-parent";
265
+ injectSubagent(manager, subagentId, parentConversationId, "running");
266
+
267
+ manager.onSubagentFinished = () => {};
268
+
269
+ try {
270
+ const result = await executeSubagentNotifyParent(
271
+ { message: "Progress update" },
272
+ makeContext(`conv-${subagentId}`),
273
+ );
274
+ expect(result.isError).toBe(false);
275
+ const parsed = JSON.parse(result.content);
276
+ expect(parsed.urgency).toBe("info");
277
+ } finally {
278
+ manager.onSubagentFinished = undefined;
279
+ }
280
+ });
281
+
282
+ test("appends guidance hint for blocked urgency", async () => {
283
+ const manager = getSubagentManager();
284
+ const subagentId = "notify-blocked-1";
285
+ const parentConversationId = "notify-blocked-parent";
286
+ injectSubagent(manager, subagentId, parentConversationId, "running");
287
+
288
+ let capturedMessage = "";
289
+ manager.onSubagentFinished = (
290
+ _parentId: string,
291
+ message: string,
292
+ ) => {
293
+ capturedMessage = message;
294
+ };
295
+
296
+ try {
297
+ await executeSubagentNotifyParent(
298
+ { message: "Need API key to proceed", urgency: "blocked" },
299
+ makeContext(`conv-${subagentId}`),
300
+ );
301
+ expect(capturedMessage).toContain("Need API key to proceed");
302
+ expect(capturedMessage).toContain(
303
+ "Use subagent_message to send guidance to this subagent.",
304
+ );
305
+ } finally {
306
+ manager.onSubagentFinished = undefined;
307
+ }
308
+ });
309
+ });
310
+
311
+ // ── Manager-level tests ────────────────────────────────────────────
312
+
313
+ describe("SubagentManager.notifyParent", () => {
314
+ test("returns false for terminal subagents", () => {
315
+ const manager = getSubagentManager();
316
+
317
+ for (const terminalStatus of [
318
+ "completed",
319
+ "failed",
320
+ "aborted",
321
+ ] as const) {
322
+ const subagentId = `notify-terminal-${terminalStatus}`;
323
+ const parentConversationId = `notify-terminal-parent-${terminalStatus}`;
324
+ injectSubagent(
325
+ manager,
326
+ subagentId,
327
+ parentConversationId,
328
+ terminalStatus,
329
+ );
330
+
331
+ manager.onSubagentFinished = () => {};
332
+
333
+ try {
334
+ const result = manager.notifyParent(
335
+ `conv-${subagentId}`,
336
+ "Should not arrive",
337
+ "info",
338
+ );
339
+ expect(result).toBe(false);
340
+ } finally {
341
+ manager.onSubagentFinished = undefined;
342
+ }
343
+ }
344
+ });
345
+
346
+ test("returns false when onSubagentFinished is not wired", () => {
347
+ const manager = getSubagentManager();
348
+ const subagentId = "notify-no-callback-1";
349
+ const parentConversationId = "notify-no-callback-parent";
350
+ injectSubagent(manager, subagentId, parentConversationId, "running");
351
+
352
+ manager.onSubagentFinished = undefined;
353
+
354
+ const result = manager.notifyParent(
355
+ `conv-${subagentId}`,
356
+ "Test message",
357
+ "info",
358
+ );
359
+ expect(result).toBe(false);
360
+ });
361
+ });
362
+
363
+ describe("SubagentManager.getParentInfo", () => {
364
+ test("returns undefined for unknown conversationIds", () => {
365
+ const manager = getSubagentManager();
366
+ const result = manager.getParentInfo("nonexistent-conversation-id");
367
+ expect(result).toBeUndefined();
368
+ });
369
+
370
+ test("returns parent info for known subagent conversationId", () => {
371
+ const manager = getSubagentManager();
372
+ const subagentId = "parent-info-sub-1";
373
+ const parentConversationId = "parent-info-parent-1";
374
+ injectSubagent(manager, subagentId, parentConversationId, "running", {
375
+ config: {
376
+ id: subagentId,
377
+ parentConversationId,
378
+ label: "Info Lookup",
379
+ objective: "look things up",
380
+ },
381
+ });
382
+
383
+ const info = manager.getParentInfo(`conv-${subagentId}`);
384
+ expect(info).toBeDefined();
385
+ expect(info!.parentConversationId).toBe(parentConversationId);
386
+ expect(info!.subagentId).toBe(subagentId);
387
+ expect(info!.label).toBe("Info Lookup");
388
+ expect(typeof info!.parentSendToClient).toBe("function");
389
+ });
390
+ });
@@ -0,0 +1,108 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ mergeSkillIds,
5
+ SUBAGENT_ROLE_REGISTRY,
6
+ type SubagentRole,
7
+ } from "../subagent/index.js";
8
+
9
+ /** All roles defined in the SubagentRole union. */
10
+ const ALL_ROLES: SubagentRole[] = ["general", "researcher", "coder", "planner"];
11
+
12
+ describe("SUBAGENT_ROLE_REGISTRY", () => {
13
+ test("covers all values in the SubagentRole union", () => {
14
+ const registryKeys = Object.keys(SUBAGENT_ROLE_REGISTRY);
15
+ expect(registryKeys.sort()).toEqual([...ALL_ROLES].sort());
16
+ expect(registryKeys).toHaveLength(ALL_ROLES.length);
17
+ });
18
+
19
+ test("every role has a non-empty systemPromptPreamble", () => {
20
+ for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
21
+ expect(config.systemPromptPreamble.length).toBeGreaterThan(0);
22
+ }
23
+ });
24
+
25
+ test("general has allowedTools: undefined", () => {
26
+ expect(SUBAGENT_ROLE_REGISTRY.general.allowedTools).toBeUndefined();
27
+ });
28
+
29
+ test("all non-general roles have allowedTools as a non-empty array", () => {
30
+ for (const role of ALL_ROLES) {
31
+ if (role === "general") continue;
32
+ const config = SUBAGENT_ROLE_REGISTRY[role];
33
+ expect(Array.isArray(config.allowedTools)).toBe(true);
34
+ expect(config.allowedTools!.length).toBeGreaterThan(0);
35
+ }
36
+ });
37
+
38
+ test('every role with allowedTools includes "notify_parent"', () => {
39
+ for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
40
+ if (config.allowedTools !== undefined) {
41
+ expect(config.allowedTools).toContain("notify_parent");
42
+ }
43
+ }
44
+ });
45
+
46
+ test('no role includes "skill_execute" (replaced by core tools)', () => {
47
+ for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
48
+ if (config.allowedTools !== undefined) {
49
+ expect(config.allowedTools).not.toContain("skill_execute");
50
+ }
51
+ }
52
+ });
53
+
54
+ test('researcher includes "recall" for memory access', () => {
55
+ const tools = SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!;
56
+ expect(tools).toContain("recall");
57
+ });
58
+
59
+ test('coder includes "recall" for memory access', () => {
60
+ expect(SUBAGENT_ROLE_REGISTRY.coder.allowedTools!).toContain("recall");
61
+ });
62
+
63
+ test('planner includes "recall" for memory access', () => {
64
+ expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools!).toContain("recall");
65
+ });
66
+
67
+ test("no role references the old memory_recall tool name", () => {
68
+ for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
69
+ if (config.allowedTools !== undefined) {
70
+ expect(config.allowedTools).not.toContain("memory_recall");
71
+ }
72
+ }
73
+ });
74
+
75
+ test("every role has empty skillIds (no skill preactivation)", () => {
76
+ for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
77
+ expect(config.skillIds).toEqual([]);
78
+ }
79
+ });
80
+
81
+ test('researcher and planner include "file_list"', () => {
82
+ expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
83
+ "file_list",
84
+ );
85
+ expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools).toContain("file_list");
86
+ });
87
+ });
88
+
89
+ describe("mergeSkillIds", () => {
90
+ test("removes duplicates between role and config skill IDs", () => {
91
+ expect(mergeSkillIds(["a", "b"], ["b", "c"])).toEqual(["a", "b", "c"]);
92
+ });
93
+
94
+ test("returns only role skills when config is undefined", () => {
95
+ expect(mergeSkillIds(["subagent"], undefined)).toEqual(["subagent"]);
96
+ });
97
+
98
+ test("includes caller-provided extras alongside role skills", () => {
99
+ expect(mergeSkillIds(["subagent"], ["custom-skill"])).toEqual([
100
+ "subagent",
101
+ "custom-skill",
102
+ ]);
103
+ });
104
+
105
+ test("returns empty array when both inputs are empty", () => {
106
+ expect(mergeSkillIds([], undefined)).toEqual([]);
107
+ });
108
+ });
@@ -0,0 +1,71 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ isToolActiveForContext,
5
+ type SkillProjectionContext,
6
+ SUBAGENT_ONLY_TOOL_NAMES,
7
+ } from "../daemon/conversation-tool-setup.js";
8
+
9
+ const TEST_TOOL_NAME = "__test_subagent_only_tool__";
10
+
11
+ describe("subagent-only tool filtering", () => {
12
+ beforeEach(() => {
13
+ SUBAGENT_ONLY_TOOL_NAMES.add(TEST_TOOL_NAME);
14
+ });
15
+
16
+ afterEach(() => {
17
+ SUBAGENT_ONLY_TOOL_NAMES.delete(TEST_TOOL_NAME);
18
+ });
19
+
20
+ test("hides subagent-only tools from main conversations (isSubagent=false)", () => {
21
+ const ctx: SkillProjectionContext = {
22
+ skillProjectionState: new Map(),
23
+ skillProjectionCache: {},
24
+ coreToolNames: new Set(),
25
+ toolsDisabledDepth: 0,
26
+ hasNoClient: false,
27
+ isSubagent: false,
28
+ };
29
+
30
+ expect(isToolActiveForContext(TEST_TOOL_NAME, ctx)).toBe(false);
31
+ });
32
+
33
+ test("hides subagent-only tools when isSubagent is undefined", () => {
34
+ const ctx: SkillProjectionContext = {
35
+ skillProjectionState: new Map(),
36
+ skillProjectionCache: {},
37
+ coreToolNames: new Set(),
38
+ toolsDisabledDepth: 0,
39
+ hasNoClient: false,
40
+ };
41
+
42
+ expect(isToolActiveForContext(TEST_TOOL_NAME, ctx)).toBe(false);
43
+ });
44
+
45
+ test("shows subagent-only tools to subagent conversations (isSubagent=true)", () => {
46
+ const ctx: SkillProjectionContext = {
47
+ skillProjectionState: new Map(),
48
+ skillProjectionCache: {},
49
+ coreToolNames: new Set(),
50
+ toolsDisabledDepth: 0,
51
+ hasNoClient: true,
52
+ isSubagent: true,
53
+ };
54
+
55
+ expect(isToolActiveForContext(TEST_TOOL_NAME, ctx)).toBe(true);
56
+ });
57
+
58
+ test("does not affect regular tools when isSubagent is false", () => {
59
+ const ctx: SkillProjectionContext = {
60
+ skillProjectionState: new Map(),
61
+ skillProjectionCache: {},
62
+ coreToolNames: new Set(),
63
+ toolsDisabledDepth: 0,
64
+ hasNoClient: false,
65
+ isSubagent: false,
66
+ };
67
+
68
+ // A regular tool not in SUBAGENT_ONLY_TOOL_NAMES should still be active
69
+ expect(isToolActiveForContext("bash", ctx)).toBe(true);
70
+ });
71
+ });