@vellumai/assistant 0.5.16 → 0.6.1

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 (592) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +69 -16
  3. package/Dockerfile +2 -5
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/knip.json +2 -1
  9. package/openapi.yaml +1198 -83
  10. package/package.json +5 -1
  11. package/src/__tests__/actor-token-service.test.ts +68 -0
  12. package/src/__tests__/agent-loop.test.ts +0 -32
  13. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  14. package/src/__tests__/anthropic-provider.test.ts +217 -98
  15. package/src/__tests__/app-compiler.test.ts +120 -0
  16. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  17. package/src/__tests__/app-executors.test.ts +47 -1
  18. package/src/__tests__/app-source-watcher.test.ts +159 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  20. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  21. package/src/__tests__/call-domain.test.ts +2 -6
  22. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  23. package/src/__tests__/call-recovery.test.ts +2 -6
  24. package/src/__tests__/call-routes-http.test.ts +2 -6
  25. package/src/__tests__/call-store.test.ts +2 -6
  26. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  27. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  28. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  29. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  30. package/src/__tests__/checker.test.ts +63 -9
  31. package/src/__tests__/clawhub.test.ts +54 -24
  32. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  33. package/src/__tests__/config-schema.test.ts +6 -1
  34. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  35. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  36. package/src/__tests__/contacts-tools.test.ts +31 -0
  37. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  38. package/src/__tests__/context-token-estimator.test.ts +175 -10
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
  40. package/src/__tests__/conversation-agent-loop.test.ts +13 -51
  41. package/src/__tests__/conversation-attachments.test.ts +2 -6
  42. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  43. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  44. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  45. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  46. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  47. package/src/__tests__/conversation-error.test.ts +33 -2
  48. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  49. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  50. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  53. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  54. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  55. package/src/__tests__/conversation-store.test.ts +2 -6
  56. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  57. package/src/__tests__/conversation-usage.test.ts +2 -6
  58. package/src/__tests__/conversation-wipe.test.ts +13 -414
  59. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  60. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  61. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  62. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  63. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  64. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  65. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  66. package/src/__tests__/date-context.test.ts +76 -210
  67. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  68. package/src/__tests__/file-list-tool.test.ts +219 -0
  69. package/src/__tests__/first-greeting.test.ts +1 -1
  70. package/src/__tests__/followup-tools.test.ts +2 -6
  71. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  72. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  73. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  74. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  75. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  76. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  77. package/src/__tests__/guardian-action-store.test.ts +2 -6
  78. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  79. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  80. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  81. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  82. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  83. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  84. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  85. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  86. package/src/__tests__/heartbeat-service.test.ts +180 -3
  87. package/src/__tests__/identity-routes.test.ts +328 -0
  88. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  89. package/src/__tests__/injection-block.test.ts +178 -0
  90. package/src/__tests__/install-meta.test.ts +506 -0
  91. package/src/__tests__/install-skill-routing.test.ts +293 -0
  92. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  93. package/src/__tests__/invite-routes-http.test.ts +2 -6
  94. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
  95. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  96. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  97. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  98. package/src/__tests__/llm-context-route-provider.test.ts +103 -6
  99. package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
  100. package/src/__tests__/llm-usage-store.test.ts +2 -6
  101. package/src/__tests__/log-export-workspace.test.ts +74 -111
  102. package/src/__tests__/managed-store.test.ts +38 -11
  103. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  104. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  105. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  106. package/src/__tests__/memory-recall-log-store.test.ts +134 -6
  107. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  108. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  109. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  110. package/src/__tests__/mock-fetch.ts +87 -0
  111. package/src/__tests__/non-member-access-request.test.ts +2 -6
  112. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  113. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  114. package/src/__tests__/oauth-cli.test.ts +364 -2
  115. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  116. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  117. package/src/__tests__/outlook-attachments.test.ts +301 -0
  118. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  119. package/src/__tests__/outlook-categories.test.ts +212 -0
  120. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  121. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  122. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  123. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  124. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  125. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  126. package/src/__tests__/outlook-trash.test.ts +77 -0
  127. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  128. package/src/__tests__/parser.test.ts +32 -0
  129. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  130. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  131. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  132. package/src/__tests__/permission-mode-store.test.ts +277 -0
  133. package/src/__tests__/permission-mode.test.ts +101 -0
  134. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  135. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  136. package/src/__tests__/playbook-execution.test.ts +76 -80
  137. package/src/__tests__/playbook-tools.test.ts +5 -7
  138. package/src/__tests__/profiler-routes.test.ts +502 -0
  139. package/src/__tests__/profiler-run-store.test.ts +441 -0
  140. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  141. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  142. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  143. package/src/__tests__/registry.test.ts +3 -3
  144. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  145. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  146. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  147. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  148. package/src/__tests__/schedule-store.test.ts +2 -6
  149. package/src/__tests__/schedule-tools.test.ts +2 -6
  150. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  151. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  152. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  153. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  154. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  155. package/src/__tests__/search-skills-unified.test.ts +422 -0
  156. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  157. package/src/__tests__/send-endpoint-busy.test.ts +44 -9
  158. package/src/__tests__/sequence-store.test.ts +2 -6
  159. package/src/__tests__/server-history-render.test.ts +2 -6
  160. package/src/__tests__/set-permission-mode.test.ts +274 -0
  161. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  162. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  163. package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
  164. package/src/__tests__/skill-memory.test.ts +2 -741
  165. package/src/__tests__/skills-uninstall.test.ts +2 -2
  166. package/src/__tests__/skills.test.ts +1 -1
  167. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  168. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  169. package/src/__tests__/subagent-detail.test.ts +84 -0
  170. package/src/__tests__/subagent-disposal.test.ts +308 -0
  171. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  172. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  173. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  174. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  175. package/src/__tests__/subagent-tools.test.ts +464 -4
  176. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  177. package/src/__tests__/task-compiler.test.ts +2 -6
  178. package/src/__tests__/task-management-tools.test.ts +2 -6
  179. package/src/__tests__/task-memory-cleanup.test.ts +185 -241
  180. package/src/__tests__/task-runner.test.ts +2 -6
  181. package/src/__tests__/task-scheduler.test.ts +2 -6
  182. package/src/__tests__/terminal-tools.test.ts +17 -27
  183. package/src/__tests__/test-preload.ts +7 -0
  184. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  185. package/src/__tests__/tool-executor.test.ts +4 -26
  186. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  187. package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
  188. package/src/__tests__/top-level-renderer.test.ts +10 -13
  189. package/src/__tests__/trust-store.test.ts +1 -1
  190. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  191. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
  192. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  193. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  194. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  195. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  196. package/src/__tests__/usage-routes.test.ts +2 -6
  197. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  198. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  199. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  200. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  201. package/src/__tests__/volume-security-guard.test.ts +2 -0
  202. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  203. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  204. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  205. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  206. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  207. package/src/__tests__/workspace-policy.test.ts +1 -1
  208. package/src/agent/attachments.ts +7 -2
  209. package/src/agent/image-optimize.ts +165 -0
  210. package/src/agent/loop.ts +7 -15
  211. package/src/approvals/guardian-request-resolvers.ts +24 -0
  212. package/src/avatar/traits-png-sync.ts +3 -3
  213. package/src/bundler/app-compiler.ts +179 -2
  214. package/src/bundler/package-resolver.ts +3 -5
  215. package/src/cli/__tests__/notifications.test.ts +1 -2
  216. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  217. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  218. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  219. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  220. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  221. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  222. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  223. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  224. package/src/cli/commands/avatar.ts +3 -3
  225. package/src/cli/commands/config.ts +26 -13
  226. package/src/cli/commands/conversations.ts +1 -8
  227. package/src/cli/commands/doctor.ts +2 -2
  228. package/src/cli/commands/email.ts +584 -835
  229. package/src/cli/commands/memory.ts +37 -84
  230. package/src/cli/commands/notifications.ts +7 -2
  231. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  232. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  233. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  234. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  235. package/src/cli/commands/oauth/connect.ts +25 -11
  236. package/src/cli/commands/oauth/mode.ts +7 -0
  237. package/src/cli/commands/oauth/shared.ts +39 -3
  238. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  239. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  240. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  241. package/src/cli/commands/platform/index.ts +16 -16
  242. package/src/cli/commands/routes.ts +396 -0
  243. package/src/cli/commands/skills.ts +218 -36
  244. package/src/cli/commands/trust.ts +2 -2
  245. package/src/cli/lib/daemon-credential-client.ts +2 -3
  246. package/src/cli/program.ts +2 -0
  247. package/src/cli.ts +1 -120
  248. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  249. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  250. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  251. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  252. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  253. package/src/config/bundled-skills/gmail/SKILL.md +4 -12
  254. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  255. package/src/config/bundled-skills/messaging/SKILL.md +17 -18
  256. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  257. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  258. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  259. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  260. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  261. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  262. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  263. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  264. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  265. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  266. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  267. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  268. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  269. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  270. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  271. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  272. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  273. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  274. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  275. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  276. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  277. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  278. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  279. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  280. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  281. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  282. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  283. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  284. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  285. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  286. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  287. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  288. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  289. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  290. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  291. package/src/config/bundled-skills/slack/SKILL.md +3 -7
  292. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  293. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  294. package/src/config/bundled-tool-registry.ts +56 -4
  295. package/src/config/env-registry.ts +78 -8
  296. package/src/config/feature-flag-registry.json +38 -125
  297. package/src/config/schema.ts +8 -0
  298. package/src/config/schemas/filing.ts +51 -0
  299. package/src/config/schemas/heartbeat.ts +15 -12
  300. package/src/config/schemas/memory-lifecycle.ts +12 -0
  301. package/src/config/schemas/platform.ts +8 -0
  302. package/src/config/schemas/security.ts +14 -0
  303. package/src/config/schemas/timeouts.ts +1 -1
  304. package/src/config/skills.ts +18 -7
  305. package/src/context/token-estimator.ts +25 -18
  306. package/src/context/window-manager.ts +6 -2
  307. package/src/credential-execution/process-manager.ts +3 -1
  308. package/src/daemon/app-source-watcher.ts +93 -0
  309. package/src/daemon/config-watcher.ts +79 -1
  310. package/src/daemon/context-overflow-reducer.ts +46 -2
  311. package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
  312. package/src/daemon/conversation-agent-loop.ts +236 -108
  313. package/src/daemon/conversation-error.ts +31 -8
  314. package/src/daemon/conversation-history.ts +4 -19
  315. package/src/daemon/conversation-lifecycle.ts +36 -9
  316. package/src/daemon/conversation-media-retry.ts +85 -7
  317. package/src/daemon/conversation-notifiers.ts +4 -1
  318. package/src/daemon/conversation-process.ts +13 -7
  319. package/src/daemon/conversation-runtime-assembly.ts +305 -306
  320. package/src/daemon/conversation-tool-setup.ts +44 -14
  321. package/src/daemon/conversation-workspace.ts +1 -2
  322. package/src/daemon/conversation.ts +59 -2
  323. package/src/daemon/daemon-control.ts +8 -2
  324. package/src/daemon/date-context.ts +26 -53
  325. package/src/daemon/first-greeting.ts +1 -1
  326. package/src/daemon/handlers/conversations.ts +4 -7
  327. package/src/daemon/handlers/shared.test.ts +143 -0
  328. package/src/daemon/handlers/shared.ts +85 -17
  329. package/src/daemon/handlers/skills.ts +416 -209
  330. package/src/daemon/lifecycle.ts +212 -131
  331. package/src/daemon/main.ts +5 -1
  332. package/src/daemon/message-types/conversations.ts +29 -7
  333. package/src/daemon/message-types/messages.ts +12 -2
  334. package/src/daemon/message-types/schedules.ts +1 -0
  335. package/src/daemon/message-types/settings.ts +6 -0
  336. package/src/daemon/message-types/skills.ts +97 -36
  337. package/src/daemon/profiler-run-store.ts +557 -0
  338. package/src/daemon/providers-setup.ts +5 -0
  339. package/src/daemon/server.ts +100 -11
  340. package/src/daemon/shutdown-handlers.ts +5 -0
  341. package/src/daemon/tool-side-effects.ts +50 -8
  342. package/src/export/transcript-formatter.ts +148 -0
  343. package/src/filing/filing-service.ts +228 -0
  344. package/src/heartbeat/heartbeat-service.ts +97 -7
  345. package/src/hooks/cli.ts +2 -2
  346. package/src/hooks/runner.ts +15 -38
  347. package/src/inbound/platform-callback-registration.ts +14 -14
  348. package/src/mcp/client.ts +6 -0
  349. package/src/mcp/mcp-oauth-provider.ts +149 -27
  350. package/src/memory/admin.ts +42 -75
  351. package/src/memory/app-store.ts +69 -0
  352. package/src/memory/conversation-bootstrap.ts +3 -1
  353. package/src/memory/conversation-crud.ts +211 -288
  354. package/src/memory/conversation-group-migration.ts +157 -0
  355. package/src/memory/conversation-queries.ts +61 -13
  356. package/src/memory/conversation-title-service.ts +1 -0
  357. package/src/memory/db-init.ts +194 -361
  358. package/src/memory/embed.ts +73 -0
  359. package/src/memory/embedding-backend.ts +8 -14
  360. package/src/memory/embedding-runtime-manager.ts +12 -114
  361. package/src/memory/fingerprint.ts +2 -2
  362. package/src/memory/graph/bootstrap.ts +521 -0
  363. package/src/memory/graph/capability-seed.ts +449 -0
  364. package/src/memory/graph/consolidation.ts +725 -0
  365. package/src/memory/graph/conversation-graph-memory.ts +659 -0
  366. package/src/memory/graph/decay.test.ts +208 -0
  367. package/src/memory/graph/decay.ts +195 -0
  368. package/src/memory/graph/extraction-job.ts +74 -0
  369. package/src/memory/graph/extraction.test.ts +936 -0
  370. package/src/memory/graph/extraction.ts +1297 -0
  371. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  372. package/src/memory/graph/graph-search.ts +280 -0
  373. package/src/memory/graph/image-ref-utils.ts +29 -0
  374. package/src/memory/graph/injection.test.ts +513 -0
  375. package/src/memory/graph/injection.ts +469 -0
  376. package/src/memory/graph/inspect.ts +543 -0
  377. package/src/memory/graph/narrative.ts +267 -0
  378. package/src/memory/graph/pattern-scan.ts +269 -0
  379. package/src/memory/graph/retriever.ts +1111 -0
  380. package/src/memory/graph/scoring.test.ts +548 -0
  381. package/src/memory/graph/scoring.ts +232 -0
  382. package/src/memory/graph/serendipity.ts +65 -0
  383. package/src/memory/graph/store.test.ts +1098 -0
  384. package/src/memory/graph/store.ts +838 -0
  385. package/src/memory/graph/tool-handlers.ts +301 -0
  386. package/src/memory/graph/tools.ts +97 -0
  387. package/src/memory/graph/triggers.test.ts +487 -0
  388. package/src/memory/graph/triggers.ts +223 -0
  389. package/src/memory/graph/types.ts +295 -0
  390. package/src/memory/group-crud.ts +191 -0
  391. package/src/memory/indexer.ts +37 -19
  392. package/src/memory/job-handlers/cleanup.ts +32 -42
  393. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  394. package/src/memory/job-handlers/embedding.ts +5 -31
  395. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  396. package/src/memory/job-handlers/summarization.ts +32 -17
  397. package/src/memory/job-utils.ts +1 -1
  398. package/src/memory/jobs-store.ts +21 -31
  399. package/src/memory/jobs-worker.ts +180 -129
  400. package/src/memory/llm-request-log-store.ts +96 -12
  401. package/src/memory/memory-recall-log-store.ts +49 -5
  402. package/src/memory/message-content.ts +1 -0
  403. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  404. package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
  405. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  406. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  407. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  408. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  409. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  410. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  411. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  412. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  413. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  414. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  415. package/src/memory/migrations/index.ts +12 -0
  416. package/src/memory/migrations/registry.ts +16 -0
  417. package/src/memory/qdrant-client.ts +44 -17
  418. package/src/memory/schema/conversations.ts +14 -0
  419. package/src/memory/schema/index.ts +1 -0
  420. package/src/memory/schema/infrastructure.ts +8 -1
  421. package/src/memory/schema/memory-core.ts +0 -51
  422. package/src/memory/schema/memory-graph.ts +154 -0
  423. package/src/memory/search/semantic.ts +47 -91
  424. package/src/memory/task-memory-cleanup.ts +58 -61
  425. package/src/messaging/providers/outlook/adapter.ts +8 -1
  426. package/src/messaging/providers/outlook/client.ts +299 -0
  427. package/src/messaging/providers/outlook/types.ts +118 -0
  428. package/src/notifications/adapters/macos.ts +1 -0
  429. package/src/notifications/copy-composer.ts +95 -0
  430. package/src/notifications/decision-engine.ts +35 -0
  431. package/src/notifications/signal.ts +16 -0
  432. package/src/oauth/seed-providers.ts +2 -1
  433. package/src/permissions/checker.ts +36 -4
  434. package/src/permissions/defaults.ts +4 -4
  435. package/src/permissions/permission-mode-store.ts +180 -0
  436. package/src/permissions/permission-mode.ts +31 -0
  437. package/src/permissions/workspace-policy.ts +10 -1
  438. package/src/playbooks/playbook-compiler.ts +19 -18
  439. package/src/playbooks/types.ts +4 -3
  440. package/src/prompts/system-prompt.ts +62 -36
  441. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  442. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  443. package/src/prompts/templates/HEARTBEAT.md +3 -1
  444. package/src/prompts/templates/SOUL.md +25 -4
  445. package/src/prompts/templates/UPDATES.md +8 -0
  446. package/src/providers/anthropic/client.ts +136 -220
  447. package/src/providers/gemini/client.ts +1 -1
  448. package/src/providers/openai/client.ts +1 -1
  449. package/src/providers/registry.ts +1 -1
  450. package/src/providers/retry.ts +19 -3
  451. package/src/runtime/actor-trust-resolver.ts +5 -1
  452. package/src/runtime/auth/route-policy.ts +30 -0
  453. package/src/runtime/guardian-reply-router.ts +5 -1
  454. package/src/runtime/http-server.ts +55 -5
  455. package/src/runtime/http-types.ts +12 -1
  456. package/src/runtime/middleware/auth.ts +20 -0
  457. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  458. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  459. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  460. package/src/runtime/routes/app-management-routes.ts +1 -11
  461. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  462. package/src/runtime/routes/archive-utils.ts +29 -0
  463. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  464. package/src/runtime/routes/attachment-routes.ts +106 -16
  465. package/src/runtime/routes/avatar-routes.ts +2 -9
  466. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  467. package/src/runtime/routes/btw-routes.ts +22 -1
  468. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  469. package/src/runtime/routes/conversation-management-routes.ts +3 -14
  470. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  471. package/src/runtime/routes/conversation-routes.ts +264 -44
  472. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  473. package/src/runtime/routes/debug-routes.ts +1 -1
  474. package/src/runtime/routes/global-search-routes.ts +21 -19
  475. package/src/runtime/routes/group-routes.ts +207 -0
  476. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  477. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  478. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  479. package/src/runtime/routes/identity-routes.ts +53 -18
  480. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  481. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  482. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  483. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  484. package/src/runtime/routes/log-export-routes.ts +23 -275
  485. package/src/runtime/routes/memory-item-routes.test.ts +170 -247
  486. package/src/runtime/routes/memory-item-routes.ts +341 -388
  487. package/src/runtime/routes/migration-routes.ts +18 -7
  488. package/src/runtime/routes/profiler-routes.ts +350 -0
  489. package/src/runtime/routes/schedule-routes.ts +28 -11
  490. package/src/runtime/routes/settings-routes.ts +95 -8
  491. package/src/runtime/routes/skills-routes.ts +103 -37
  492. package/src/runtime/routes/subagents-routes.ts +28 -7
  493. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  494. package/src/runtime/routes/user-routes.ts +41 -0
  495. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  496. package/src/runtime/routes/workspace-routes.ts +0 -1
  497. package/src/schedule/schedule-store.ts +30 -0
  498. package/src/schedule/scheduler.ts +52 -18
  499. package/src/security/oauth2.ts +1 -1
  500. package/src/security/secure-keys.ts +4 -8
  501. package/src/shared/provider-env-vars.ts +19 -0
  502. package/src/skills/catalog-cache.ts +5 -0
  503. package/src/skills/catalog-install.ts +25 -16
  504. package/src/skills/clawhub.ts +134 -154
  505. package/src/skills/install-meta.ts +208 -0
  506. package/src/skills/managed-store.ts +29 -18
  507. package/src/skills/skill-memory.ts +12 -229
  508. package/src/skills/skillssh-registry.ts +19 -17
  509. package/src/subagent/index.ts +13 -3
  510. package/src/subagent/manager.ts +308 -29
  511. package/src/subagent/types.ts +68 -0
  512. package/src/tasks/task-runner.ts +7 -5
  513. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  514. package/src/tools/apps/executors.ts +29 -4
  515. package/src/tools/browser/runtime-check.ts +3 -1
  516. package/src/tools/filesystem/list.ts +93 -0
  517. package/src/tools/memory/register.ts +63 -46
  518. package/src/tools/permission-checker.ts +85 -1
  519. package/src/tools/registry.ts +4 -0
  520. package/src/tools/schedule/create.ts +3 -0
  521. package/src/tools/schedule/list.ts +1 -0
  522. package/src/tools/schedule/update.ts +6 -0
  523. package/src/tools/shared/filesystem/errors.ts +5 -0
  524. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  525. package/src/tools/shared/filesystem/image-read.ts +22 -85
  526. package/src/tools/shared/filesystem/types.ts +17 -0
  527. package/src/tools/shared/shell-output.ts +31 -2
  528. package/src/tools/subagent/abort.ts +12 -2
  529. package/src/tools/subagent/message.ts +9 -2
  530. package/src/tools/subagent/notify-parent.ts +79 -0
  531. package/src/tools/subagent/read.ts +29 -8
  532. package/src/tools/subagent/resolve.ts +21 -0
  533. package/src/tools/subagent/spawn.ts +2 -0
  534. package/src/tools/subagent/status.ts +11 -1
  535. package/src/tools/system/avatar-generator.ts +3 -3
  536. package/src/tools/system/register.ts +23 -0
  537. package/src/tools/system/set-permission-mode.ts +103 -0
  538. package/src/tools/terminal/parser.ts +30 -5
  539. package/src/tools/terminal/safe-env.ts +17 -1
  540. package/src/tools/tool-manifest.ts +9 -3
  541. package/src/tools/types.ts +2 -0
  542. package/src/util/browser.ts +25 -10
  543. package/src/util/bun-runtime.ts +172 -0
  544. package/src/util/logger.ts +1 -1
  545. package/src/util/platform.ts +50 -17
  546. package/src/watcher/providers/outlook-calendar.ts +343 -0
  547. package/src/watcher/providers/outlook.ts +198 -0
  548. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  549. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  550. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  551. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  552. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  553. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  554. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  555. package/src/workspace/migrations/registry.ts +10 -0
  556. package/src/workspace/top-level-renderer.ts +5 -9
  557. package/src/__tests__/cli-memory.test.ts +0 -372
  558. package/src/__tests__/clipboard.test.ts +0 -88
  559. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  560. package/src/__tests__/journal-context.test.ts +0 -268
  561. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  562. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  563. package/src/__tests__/memory-query-builder.test.ts +0 -59
  564. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  565. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  566. package/src/__tests__/memory-regressions.test.ts +0 -3696
  567. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  568. package/src/cli/cli-memory.ts +0 -176
  569. package/src/daemon/conversation-memory.ts +0 -207
  570. package/src/memory/conversation-starters-cadence.ts +0 -74
  571. package/src/memory/items-extractor.ts +0 -860
  572. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  573. package/src/memory/job-handlers/extraction.ts +0 -40
  574. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  575. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  576. package/src/memory/journal-memory.ts +0 -224
  577. package/src/memory/query-builder.ts +0 -47
  578. package/src/memory/query-expansion.ts +0 -83
  579. package/src/memory/retriever.test.ts +0 -1592
  580. package/src/memory/retriever.ts +0 -1331
  581. package/src/memory/search/formatting.test.ts +0 -140
  582. package/src/memory/search/formatting.ts +0 -262
  583. package/src/memory/search/mmr.ts +0 -139
  584. package/src/memory/search/ranking.ts +0 -15
  585. package/src/memory/search/staleness.ts +0 -40
  586. package/src/memory/search/tier-classifier.ts +0 -18
  587. package/src/memory/search/types.ts +0 -121
  588. package/src/prompts/journal-context.ts +0 -154
  589. package/src/tools/memory/definitions.ts +0 -69
  590. package/src/tools/memory/handlers.test.ts +0 -562
  591. package/src/tools/memory/handlers.ts +0 -434
  592. package/src/util/clipboard.ts +0 -34
@@ -22,7 +22,7 @@ interface FakeManagedSubagent {
22
22
  outputTokens: number;
23
23
  estimatedCost: number;
24
24
  };
25
- };
25
+ } | null;
26
26
  state: SubagentState;
27
27
  parentSendToClient: (msg: ServerMessage) => void;
28
28
  }
@@ -32,6 +32,7 @@ interface ManagerInternals {
32
32
  subagents: Map<string, FakeManagedSubagent>;
33
33
  parentToChildren: Map<string, Set<string>>;
34
34
  runSubagent: (subagentId: string, objective: string) => Promise<void>;
35
+ stopSweep: () => void;
35
36
  }
36
37
 
37
38
  function asInternals(manager: SubagentManager): ManagerInternals {
@@ -275,8 +276,8 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
275
276
 
276
277
  // Patch the fake conversation to simulate a successful agent loop.
277
278
  const managed = asInternals(manager).subagents.get(subagentId)!;
278
- managed.conversation.persistUserMessage = () => "msg-1";
279
- managed.conversation.runAgentLoop = async () => {};
279
+ managed.conversation!.persistUserMessage = () => "msg-1";
280
+ managed.conversation!.runAgentLoop = async () => {};
280
281
 
281
282
  const notifications: { parentConversationId: string; message: string }[] =
282
283
  [];
@@ -298,6 +299,8 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
298
299
  '[Subagent "Test subagent" completed]',
299
300
  );
300
301
  expect(notifications[0].message).toContain("subagent_read");
302
+
303
+ asInternals(manager).stopSweep();
301
304
  });
302
305
 
303
306
  test("failed subagent notifies parent with error and asks user before retry", async () => {
@@ -309,8 +312,8 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
309
312
  // Patch the fake conversation to simulate a failure.
310
313
  const managed = asInternals(manager).subagents.get(subagentId)!;
311
314
 
312
- managed.conversation.persistUserMessage = () => "msg-1";
313
- managed.conversation.runAgentLoop = async () => {
315
+ managed.conversation!.persistUserMessage = () => "msg-1";
316
+ managed.conversation!.runAgentLoop = async () => {
314
317
  throw new Error("API rate limit exceeded");
315
318
  };
316
319
 
@@ -333,6 +336,8 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
333
336
  expect(notifications[0].message).toContain("failed");
334
337
  expect(notifications[0].message).toContain("API rate limit exceeded");
335
338
  expect(notifications[0].message).toContain("Do NOT re-spawn");
339
+
340
+ asInternals(manager).stopSweep();
336
341
  });
337
342
 
338
343
  test("failed subagent does not notify if already aborted", async () => {
@@ -343,8 +348,8 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
343
348
 
344
349
  const managed = asInternals(manager).subagents.get(subagentId)!;
345
350
 
346
- managed.conversation.persistUserMessage = () => "msg-1";
347
- managed.conversation.runAgentLoop = async () => {
351
+ managed.conversation!.persistUserMessage = () => "msg-1";
352
+ managed.conversation!.runAgentLoop = async () => {
348
353
  throw new Error("Conversation aborted");
349
354
  };
350
355
 
@@ -358,6 +363,8 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
358
363
 
359
364
  // Should NOT notify — status was already terminal (aborted).
360
365
  expect(notifications).toHaveLength(0);
366
+
367
+ asInternals(manager).stopSweep();
361
368
  });
362
369
  });
363
370
 
@@ -422,9 +429,9 @@ describe("SubagentManager abort race guard", () => {
422
429
  // Patch conversation to simulate successful completion after abort.
423
430
  const managed = asInternals(manager).subagents.get(subagentId)!;
424
431
 
425
- managed.conversation.persistUserMessage = () => "msg-1";
426
- managed.conversation.runAgentLoop = async () => {};
427
- managed.conversation.messages = [
432
+ managed.conversation!.persistUserMessage = () => "msg-1";
433
+ managed.conversation!.runAgentLoop = async () => {};
434
+ managed.conversation!.messages = [
428
435
  { role: "assistant", content: [{ type: "text", text: "Done!" }] },
429
436
  ];
430
437
 
@@ -440,6 +447,8 @@ describe("SubagentManager abort race guard", () => {
440
447
  expect(notifications).toHaveLength(0);
441
448
  // Status should remain aborted, not overwritten to completed.
442
449
  expect(state.status).toBe("aborted");
450
+
451
+ asInternals(manager).stopSweep();
443
452
  });
444
453
  });
445
454
 
@@ -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
+ });