@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
@@ -0,0 +1,292 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Mocks — must be set up before importing the module under test
5
+ // ---------------------------------------------------------------------------
6
+
7
+ let mockGuardian: { contact: unknown; channel: unknown } | null = null;
8
+ let mockActiveSession: Record<string, unknown> | null = null;
9
+ let mockSessionResult = {
10
+ sessionId: "sess-1",
11
+ secret: "123456",
12
+ challengeHash: "hash-1",
13
+ expiresAt: Date.now() + 600_000,
14
+ ttlSeconds: 600,
15
+ };
16
+
17
+ // Track calls manually to avoid TypeScript issues with mock() generics
18
+ let createOutboundSessionCalls: unknown[] = [];
19
+ let deliverChannelReplyCalls: unknown[][] = [];
20
+ let emitNotificationSignalCalls: unknown[] = [];
21
+ let messageIdCounter = 0;
22
+
23
+ mock.module("../../../contacts/contact-store.js", () => ({
24
+ findGuardianForChannel: () => mockGuardian,
25
+ }));
26
+
27
+ mock.module("../../channel-verification-service.js", () => ({
28
+ createOutboundSession: (params: unknown) => {
29
+ createOutboundSessionCalls.push(params);
30
+ return mockSessionResult;
31
+ },
32
+ findActiveSession: () => mockActiveSession,
33
+ }));
34
+
35
+ mock.module("../../gateway-client.js", () => ({
36
+ deliverChannelReply: (url: unknown, payload: unknown, token: unknown) => {
37
+ deliverChannelReplyCalls.push([url, payload, token]);
38
+ return Promise.resolve({ ok: true });
39
+ },
40
+ }));
41
+
42
+ mock.module("../../../notifications/emit-signal.js", () => ({
43
+ emitNotificationSignal: (params: unknown) => {
44
+ emitNotificationSignalCalls.push(params);
45
+ return Promise.resolve({
46
+ signalId: "sig-1",
47
+ deduplicated: false,
48
+ dispatched: true,
49
+ reason: "ok",
50
+ deliveryResults: [],
51
+ });
52
+ },
53
+ }));
54
+
55
+ mock.module("../../../util/logger.js", () => ({
56
+ getLogger: () => ({
57
+ debug: () => {},
58
+ info: () => {},
59
+ warn: () => {},
60
+ error: () => {},
61
+ }),
62
+ }));
63
+
64
+ // Import after mocks are installed
65
+ const { handleGuardianActivationIntercept } =
66
+ await import("./guardian-activation-intercept.js");
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Helpers
70
+ // ---------------------------------------------------------------------------
71
+
72
+ function makeParams(
73
+ overrides: Partial<
74
+ Parameters<typeof handleGuardianActivationIntercept>[0]
75
+ > = {},
76
+ ) {
77
+ messageIdCounter++;
78
+ return {
79
+ sourceChannel: "telegram" as const,
80
+ conversationExternalId: "chat-123",
81
+ rawSenderId: "user-42",
82
+ canonicalSenderId: "user-42",
83
+ actorDisplayName: "Alice",
84
+ actorUsername: "alice",
85
+ sourceMetadata: { commandIntent: { type: "start" } },
86
+ replyCallbackUrl: "https://gateway/reply",
87
+ mintBearerToken: () => "token-123",
88
+ assistantId: "self",
89
+ externalMessageId: `msg-${messageIdCounter}`,
90
+ ...overrides,
91
+ };
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Tests
96
+ // ---------------------------------------------------------------------------
97
+
98
+ describe("handleGuardianActivationIntercept", () => {
99
+ beforeEach(() => {
100
+ mockGuardian = null;
101
+ mockActiveSession = null;
102
+ mockSessionResult = {
103
+ sessionId: "sess-1",
104
+ secret: "123456",
105
+ challengeHash: "hash-1",
106
+ expiresAt: Date.now() + 600_000,
107
+ ttlSeconds: 600,
108
+ };
109
+ createOutboundSessionCalls = [];
110
+ deliverChannelReplyCalls = [];
111
+ emitNotificationSignalCalls = [];
112
+ });
113
+
114
+ afterEach(() => {
115
+ createOutboundSessionCalls = [];
116
+ deliverChannelReplyCalls = [];
117
+ emitNotificationSignalCalls = [];
118
+ });
119
+
120
+ test("bare /start with no guardian creates session and returns early", async () => {
121
+ const result = await handleGuardianActivationIntercept(makeParams());
122
+
123
+ expect(result).not.toBeNull();
124
+ const body = await result!.json();
125
+ expect(body).toEqual({ accepted: true, guardianActivation: true });
126
+
127
+ // Verify createOutboundSession was called with correct params
128
+ expect(createOutboundSessionCalls).toHaveLength(1);
129
+ expect(createOutboundSessionCalls[0]).toEqual({
130
+ channel: "telegram",
131
+ expectedExternalUserId: "user-42",
132
+ expectedChatId: "chat-123",
133
+ identityBindingStatus: "bound",
134
+ destinationAddress: "chat-123",
135
+ verificationPurpose: "guardian",
136
+ });
137
+
138
+ // Verify deliverChannelReply was called with the welcome/verify message
139
+ expect(deliverChannelReplyCalls).toHaveLength(1);
140
+ expect(deliverChannelReplyCalls[0][0]).toBe("https://gateway/reply");
141
+ expect(deliverChannelReplyCalls[0][1]).toEqual({
142
+ chatId: "chat-123",
143
+ text: "Welcome! To verify your identity as guardian, check your assistant app for a verification code and enter it here.",
144
+ assistantId: "self",
145
+ });
146
+
147
+ // Verify emitNotificationSignal was called with guardian.channel_activation
148
+ expect(emitNotificationSignalCalls).toHaveLength(1);
149
+ const signalArgs = emitNotificationSignalCalls[0] as Record<string, any>;
150
+ expect(signalArgs.sourceEventName).toBe("guardian.channel_activation");
151
+ expect(signalArgs.contextPayload.verificationCode).toBe("123456");
152
+ expect(signalArgs.contextPayload.sourceChannel).toBe("telegram");
153
+ expect(signalArgs.contextPayload.actorExternalId).toBe("user-42");
154
+ expect(signalArgs.contextPayload.sessionId).toBe("sess-1");
155
+ expect(signalArgs.dedupeKey).toBe("guardian-activation:sess-1");
156
+ });
157
+
158
+ test("bare /start with existing guardian returns null", async () => {
159
+ mockGuardian = {
160
+ contact: { id: "contact-1", role: "guardian" },
161
+ channel: { id: "ch-1", type: "telegram" },
162
+ };
163
+
164
+ const result = await handleGuardianActivationIntercept(makeParams());
165
+ expect(result).toBeNull();
166
+ expect(createOutboundSessionCalls).toHaveLength(0);
167
+ });
168
+
169
+ test("/start with payload returns null", async () => {
170
+ const result = await handleGuardianActivationIntercept(
171
+ makeParams({
172
+ sourceMetadata: {
173
+ commandIntent: { type: "start", payload: "gv_token" },
174
+ },
175
+ }),
176
+ );
177
+ expect(result).toBeNull();
178
+ expect(createOutboundSessionCalls).toHaveLength(0);
179
+ });
180
+
181
+ test("non-/start message returns null", async () => {
182
+ const result = await handleGuardianActivationIntercept(
183
+ makeParams({
184
+ sourceMetadata: { commandIntent: { type: "other" } },
185
+ }),
186
+ );
187
+ expect(result).toBeNull();
188
+ expect(createOutboundSessionCalls).toHaveLength(0);
189
+ });
190
+
191
+ test("no commandIntent returns null", async () => {
192
+ const result = await handleGuardianActivationIntercept(
193
+ makeParams({ sourceMetadata: {} }),
194
+ );
195
+ expect(result).toBeNull();
196
+
197
+ const result2 = await handleGuardianActivationIntercept(
198
+ makeParams({ sourceMetadata: undefined }),
199
+ );
200
+ expect(result2).toBeNull();
201
+ expect(createOutboundSessionCalls).toHaveLength(0);
202
+ });
203
+
204
+ test("non-telegram channel returns null", async () => {
205
+ const result = await handleGuardianActivationIntercept(
206
+ makeParams({ sourceChannel: "slack" as any }),
207
+ );
208
+ expect(result).toBeNull();
209
+ expect(createOutboundSessionCalls).toHaveLength(0);
210
+ });
211
+
212
+ test("missing sender ID returns null", async () => {
213
+ const result = await handleGuardianActivationIntercept(
214
+ makeParams({ rawSenderId: undefined }),
215
+ );
216
+ expect(result).toBeNull();
217
+ expect(createOutboundSessionCalls).toHaveLength(0);
218
+ });
219
+
220
+ test("existing active session from same sender sends 'already in progress' reply", async () => {
221
+ mockActiveSession = {
222
+ id: "existing-sess",
223
+ channel: "telegram",
224
+ status: "awaiting_response",
225
+ expectedExternalUserId: "user-42",
226
+ expectedChatId: "chat-123",
227
+ };
228
+
229
+ const result = await handleGuardianActivationIntercept(makeParams());
230
+
231
+ expect(result).not.toBeNull();
232
+ const body = await result!.json();
233
+ expect(body).toEqual({ accepted: true, guardianActivationPending: true });
234
+
235
+ // createOutboundSession should NOT be called
236
+ expect(createOutboundSessionCalls).toHaveLength(0);
237
+
238
+ // deliverChannelReply should be called with the "already in progress" message
239
+ expect(deliverChannelReplyCalls).toHaveLength(1);
240
+ expect(deliverChannelReplyCalls[0][1]).toEqual({
241
+ chatId: "chat-123",
242
+ text: "A verification is already in progress. Check your assistant app for the code and enter it here.",
243
+ assistantId: "self",
244
+ });
245
+
246
+ // emitNotificationSignal should NOT be called
247
+ expect(emitNotificationSignalCalls).toHaveLength(0);
248
+ });
249
+
250
+ test("existing active session from different sender allows superseding", async () => {
251
+ mockActiveSession = {
252
+ id: "existing-sess",
253
+ channel: "telegram",
254
+ status: "awaiting_response",
255
+ expectedExternalUserId: "user-OTHER",
256
+ expectedChatId: "chat-OTHER",
257
+ };
258
+
259
+ const result = await handleGuardianActivationIntercept(makeParams());
260
+
261
+ // Should proceed and create a new session (superseding the stale one)
262
+ expect(result).not.toBeNull();
263
+ const body = await result!.json();
264
+ expect(body).toEqual({ accepted: true, guardianActivation: true });
265
+ expect(createOutboundSessionCalls).toHaveLength(1);
266
+ expect(emitNotificationSignalCalls).toHaveLength(1);
267
+ });
268
+
269
+ test("duplicate webhook retry is silently deduped", async () => {
270
+ const params = makeParams({ externalMessageId: "dedup-test-msg" });
271
+
272
+ // First call should process normally
273
+ const result1 = await handleGuardianActivationIntercept(params);
274
+ expect(result1).not.toBeNull();
275
+ const body1 = await result1!.json();
276
+ expect(body1).toEqual({ accepted: true, guardianActivation: true });
277
+ expect(createOutboundSessionCalls).toHaveLength(1);
278
+ expect(deliverChannelReplyCalls).toHaveLength(1);
279
+ expect(emitNotificationSignalCalls).toHaveLength(1);
280
+
281
+ // Second call with same externalMessageId should be deduped
282
+ const result2 = await handleGuardianActivationIntercept(params);
283
+ expect(result2).not.toBeNull();
284
+ const body2 = await result2!.json();
285
+ expect(body2).toEqual({ accepted: true, guardianActivation: true });
286
+
287
+ // No additional session/reply/signal calls
288
+ expect(createOutboundSessionCalls).toHaveLength(1);
289
+ expect(deliverChannelReplyCalls).toHaveLength(1);
290
+ expect(emitNotificationSignalCalls).toHaveLength(1);
291
+ });
292
+ });
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Guardian activation intercept stage: when a bare /start arrives on a
3
+ * Telegram channel with no existing guardian, auto-initiate a verification
4
+ * session so the first user can claim the channel as guardian.
5
+ *
6
+ * This runs BEFORE ACL enforcement — a bare /start from an unknown user
7
+ * would otherwise be rejected. When the user subsequently enters the
8
+ * 6-digit code, the existing verification intercept validates it, creates
9
+ * the guardian binding, and sends a success reply.
10
+ */
11
+ import type { ChannelId } from "../../../channels/types.js";
12
+ import { findGuardianForChannel } from "../../../contacts/contact-store.js";
13
+ import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
14
+ import type { NotificationSourceChannel } from "../../../notifications/signal.js";
15
+ import { getLogger } from "../../../util/logger.js";
16
+ import {
17
+ createOutboundSession,
18
+ findActiveSession,
19
+ } from "../../channel-verification-service.js";
20
+ import { deliverChannelReply } from "../../gateway-client.js";
21
+
22
+ const log = getLogger("runtime-http");
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Public API
26
+ // ---------------------------------------------------------------------------
27
+
28
+ export interface GuardianActivationInterceptParams {
29
+ sourceChannel: ChannelId;
30
+ conversationExternalId: string;
31
+ rawSenderId: string | undefined;
32
+ canonicalSenderId: string | null;
33
+ actorDisplayName: string | undefined;
34
+ actorUsername: string | undefined;
35
+ sourceMetadata: Record<string, unknown> | undefined;
36
+ replyCallbackUrl: string | undefined;
37
+ mintBearerToken: () => string;
38
+ assistantId: string;
39
+ externalMessageId: string;
40
+ }
41
+
42
+ /**
43
+ * Lightweight dedup set for guardian activation intercepts.
44
+ * Prevents duplicate replies when Telegram retries the same webhook.
45
+ * Entries are evicted after DEDUP_TTL_MS to avoid unbounded growth.
46
+ */
47
+ const DEDUP_TTL_MS = 60_000;
48
+ const processedMessageIds = new Map<string, number>();
49
+
50
+ function isAlreadyProcessed(messageId: string): boolean {
51
+ const now = Date.now();
52
+ // Evict stale entries
53
+ for (const [key, ts] of processedMessageIds) {
54
+ if (now - ts > DEDUP_TTL_MS) processedMessageIds.delete(key);
55
+ }
56
+ return processedMessageIds.has(messageId);
57
+ }
58
+
59
+ function markProcessed(messageId: string): void {
60
+ processedMessageIds.set(messageId, Date.now());
61
+ }
62
+
63
+ export async function handleGuardianActivationIntercept(
64
+ params: GuardianActivationInterceptParams,
65
+ ): Promise<Response | null> {
66
+ const {
67
+ sourceChannel,
68
+ conversationExternalId,
69
+ rawSenderId,
70
+ actorDisplayName,
71
+ actorUsername,
72
+ sourceMetadata,
73
+ replyCallbackUrl,
74
+ mintBearerToken,
75
+ assistantId,
76
+ externalMessageId,
77
+ } = params;
78
+
79
+ // ── Extract commandIntent ──
80
+ const rawCommandIntent = sourceMetadata?.commandIntent;
81
+ const commandIntent =
82
+ rawCommandIntent &&
83
+ typeof rawCommandIntent === "object" &&
84
+ !Array.isArray(rawCommandIntent)
85
+ ? (rawCommandIntent as Record<string, unknown>)
86
+ : undefined;
87
+
88
+ // Only proceed for /start commands
89
+ if (!commandIntent || commandIntent.type !== "start") return null;
90
+
91
+ // If /start has a payload (e.g. gv_token, iv_token), let the existing
92
+ // bootstrap/invite handlers deal with it.
93
+ if (
94
+ typeof commandIntent.payload === "string" &&
95
+ commandIntent.payload.length > 0
96
+ ) {
97
+ return null;
98
+ }
99
+
100
+ // Only proceed for Telegram (can be extended later)
101
+ if (sourceChannel !== "telegram") return null;
102
+
103
+ // If a guardian already exists for this channel, continue to normal flow
104
+ if (findGuardianForChannel(sourceChannel)) return null;
105
+
106
+ // Can't bind a session without sender identity
107
+ if (!rawSenderId) return null;
108
+
109
+ // ── Webhook retry dedup ──
110
+ // The intercept runs before recordInbound, so use a lightweight in-memory
111
+ // dedup to prevent duplicate replies when Telegram retries the webhook.
112
+ // Only checked here; marked as processed after successful session creation
113
+ // so transient failures remain retryable.
114
+ if (isAlreadyProcessed(externalMessageId)) {
115
+ return Response.json({ accepted: true, guardianActivation: true });
116
+ }
117
+
118
+ // ── Idempotency: check for an existing active session from this sender ──
119
+ const existingSession = findActiveSession(sourceChannel);
120
+ if (existingSession) {
121
+ // Only block if the session belongs to the same sender. If a different
122
+ // user triggered the session, let this sender proceed (they'll supersede
123
+ // the stale session via createOutboundSession's revocation logic).
124
+ const sessionOwner =
125
+ existingSession.expectedExternalUserId ?? existingSession.expectedChatId;
126
+ if (
127
+ sessionOwner === rawSenderId ||
128
+ sessionOwner === conversationExternalId
129
+ ) {
130
+ if (replyCallbackUrl) {
131
+ deliverChannelReply(
132
+ replyCallbackUrl,
133
+ {
134
+ chatId: conversationExternalId,
135
+ text: "A verification is already in progress. Check your assistant app for the code and enter it here.",
136
+ assistantId,
137
+ },
138
+ mintBearerToken(),
139
+ ).catch((err) => {
140
+ log.error(
141
+ { err, sourceChannel, conversationExternalId },
142
+ "Failed to deliver guardian activation idempotency reply",
143
+ );
144
+ });
145
+ }
146
+ markProcessed(externalMessageId);
147
+ return Response.json({ accepted: true, guardianActivationPending: true });
148
+ }
149
+ }
150
+
151
+ // ── Create verification session ──
152
+ const sessionResult = createOutboundSession({
153
+ channel: sourceChannel,
154
+ expectedExternalUserId: rawSenderId,
155
+ expectedChatId: conversationExternalId,
156
+ identityBindingStatus: "bound",
157
+ destinationAddress: conversationExternalId,
158
+ verificationPurpose: "guardian",
159
+ });
160
+
161
+ // Mark as processed only after session creation succeeds so transient
162
+ // failures (e.g. temporary DB issues) remain retryable on the next webhook.
163
+ markProcessed(externalMessageId);
164
+
165
+ // ── Send deterministic Telegram reply ──
166
+ if (replyCallbackUrl) {
167
+ deliverChannelReply(
168
+ replyCallbackUrl,
169
+ {
170
+ chatId: conversationExternalId,
171
+ text: "Welcome! To verify your identity as guardian, check your assistant app for a verification code and enter it here.",
172
+ assistantId,
173
+ },
174
+ mintBearerToken(),
175
+ ).catch((err) => {
176
+ log.error(
177
+ { err, sourceChannel, conversationExternalId },
178
+ "Failed to deliver guardian activation welcome reply",
179
+ );
180
+ });
181
+ }
182
+
183
+ // ── Emit notification signal to deliver code to macOS app ──
184
+ void emitNotificationSignal({
185
+ sourceEventName: "guardian.channel_activation",
186
+ sourceChannel: sourceChannel as NotificationSourceChannel,
187
+ sourceContextId: `guardian-activation-${sourceChannel}-${rawSenderId}`,
188
+ attentionHints: {
189
+ requiresAction: true,
190
+ urgency: "high",
191
+ isAsyncBackground: false,
192
+ visibleInSourceNow: false,
193
+ },
194
+ contextPayload: {
195
+ verificationCode: sessionResult.secret,
196
+ sourceChannel,
197
+ actorExternalId: rawSenderId,
198
+ actorDisplayName: actorDisplayName ?? null,
199
+ actorUsername: actorUsername ?? null,
200
+ sessionId: sessionResult.sessionId,
201
+ expiresAt: sessionResult.expiresAt,
202
+ },
203
+ dedupeKey: `guardian-activation:${sessionResult.sessionId}`,
204
+ });
205
+
206
+ return Response.json({ accepted: true, guardianActivation: true });
207
+ }
@@ -570,16 +570,8 @@ function anthropicMessageSections(
570
570
  const role = asString(message.role) ?? "unknown";
571
571
  const content = message.content;
572
572
  const sections: LlmContextSection[] = [];
573
- const text = collectAnthropicMessageText(content);
574
- if (text) {
575
- sections.push({
576
- kind: "message",
577
- label,
578
- role,
579
- text,
580
- });
581
- }
582
573
 
574
+ // Collect reasoning sections first so they appear before the message text.
583
575
  for (const block of asRecordArray(content) ?? []) {
584
576
  const type = asString(block.type);
585
577
  if (type === "thinking" || type === "redacted_thinking") {
@@ -589,9 +581,21 @@ function anthropicMessageSections(
589
581
  role,
590
582
  text: collectAnthropicReasoningText(block),
591
583
  });
592
- continue;
593
584
  }
585
+ }
594
586
 
587
+ const text = collectAnthropicMessageText(content);
588
+ if (text) {
589
+ sections.push({
590
+ kind: "message",
591
+ label,
592
+ role,
593
+ text,
594
+ });
595
+ }
596
+
597
+ for (const block of asRecordArray(content) ?? []) {
598
+ const type = asString(block.type);
595
599
  if (isAnthropicToolUseType(type)) {
596
600
  sections.push({
597
601
  kind: "tool_use",