@vellumai/assistant 0.7.3 → 0.8.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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -13,8 +13,8 @@
13
13
 
14
14
  import { answerCall } from "../calls/call-domain.js";
15
15
  import { findContactChannel } from "../contacts/contact-store.js";
16
+ import { upsertContactChannel } from "../contacts/contacts-write.js";
16
17
  import { findConversation } from "../daemon/conversation-store.js";
17
- import { emitFeedEvent } from "../home/emit-feed-event.js";
18
18
  import {
19
19
  type CanonicalGuardianRequest,
20
20
  getCanonicalGuardianRequest,
@@ -192,28 +192,15 @@ const pendingInteractionResolver: GuardianRequestResolver = {
192
192
  return { ok: false, reason: "pending_interaction_not_found" };
193
193
  }
194
194
 
195
- // Resolve the interaction: remove from tracker and get the session.
196
- const resolved = pendingInteractions.resolve(request.id);
197
- if (!resolved) {
198
- // Race condition: interaction was consumed between get() and resolve().
199
- log.warn(
200
- {
201
- event: "resolver_tool_approval_resolve_race",
202
- requestId: request.id,
203
- },
204
- "Tool approval resolver: pending interaction consumed between lookup and resolve",
205
- );
206
- return { ok: false, reason: "pending_interaction_race" };
207
- }
208
-
209
195
  // Map action to the permission system's UserDecision type and notify session.
196
+ // resolveConfirmation() owns pendingInteractions deregistration.
210
197
  const userDecision: UserDecision =
211
198
  decision.action === "reject" ? "deny" : "allow";
212
- const conversation = findConversation(resolved.conversationId);
199
+ const conversation = findConversation(interaction.conversationId);
213
200
  if (!conversation) {
214
201
  return {
215
202
  ok: false,
216
- reason: `conversation_not_found: ${resolved.conversationId}`,
203
+ reason: `conversation_not_found: ${interaction.conversationId}`,
217
204
  };
218
205
  }
219
206
  conversation.handleConfirmationResponse(
@@ -225,21 +212,6 @@ const pendingInteractionResolver: GuardianRequestResolver = {
225
212
  ctx.emissionContext,
226
213
  );
227
214
 
228
- const approved = decision.action !== "reject";
229
- void emitFeedEvent({
230
- source: "assistant",
231
- title: approved ? "Tool Request Approved" : "Tool Request Denied",
232
- summary: `${approved ? "Approved" : "Denied"} access to ${request.toolName ?? "unknown tool"}.`,
233
- dedupKey: `guardian-approval:${request.id}`,
234
- urgency: approved ? undefined : "medium",
235
- detailPanel: { kind: "toolPermission" },
236
- }).catch((err) => {
237
- log.warn(
238
- { err, requestId: request.id },
239
- "Failed to emit guardian approval feed event",
240
- );
241
- });
242
-
243
215
  log.info(
244
216
  {
245
217
  event: "resolver_tool_approval_applied",
@@ -486,20 +458,6 @@ const accessRequestResolver: GuardianRequestResolver = {
486
458
  }
487
459
  }
488
460
 
489
- void emitFeedEvent({
490
- source: "assistant",
491
- title: "Access Request Denied",
492
- summary: `Denied access request.`,
493
- dedupKey: `guardian-access:${request.id}`,
494
- urgency: "medium",
495
- detailPanel: { kind: "permissionChat" },
496
- }).catch((err) => {
497
- log.warn(
498
- { err, requestId: request.id },
499
- "Failed to emit access request feed event",
500
- );
501
- });
502
-
503
461
  return {
504
462
  ok: true,
505
463
  applied: true,
@@ -515,6 +473,21 @@ const accessRequestResolver: GuardianRequestResolver = {
515
473
  // a verification session. The caller is already on the line and the
516
474
  // relay server's in-call wait loop will detect the approved status.
517
475
  if (channel === "phone") {
476
+ try {
477
+ upsertContactChannel({
478
+ sourceChannel: "phone",
479
+ externalUserId: requesterExternalUserId,
480
+ externalChatId: requesterChatId,
481
+ status: "active",
482
+ policy: "allow",
483
+ });
484
+ } catch (err) {
485
+ log.error(
486
+ { err, requesterExternalUserId },
487
+ "Access request resolver: failed to activate voice caller as trusted contact",
488
+ );
489
+ }
490
+
518
491
  log.info(
519
492
  {
520
493
  event: "resolver_access_request_voice_approved",
@@ -525,20 +498,6 @@ const accessRequestResolver: GuardianRequestResolver = {
525
498
  "Access request resolver: voice approval — direct trusted-contact activation (no verification session)",
526
499
  );
527
500
 
528
- void emitFeedEvent({
529
- source: "assistant",
530
- title: "Access Request Approved",
531
- summary: `Granted access request.`,
532
- dedupKey: `guardian-access:${request.id}`,
533
- urgency: undefined,
534
- detailPanel: { kind: "permissionChat" },
535
- }).catch((err) => {
536
- log.warn(
537
- { err, requestId: request.id },
538
- "Failed to emit access request feed event",
539
- );
540
- });
541
-
542
501
  return {
543
502
  ok: true,
544
503
  applied: true,
@@ -766,20 +725,6 @@ const accessRequestResolver: GuardianRequestResolver = {
766
725
  ? `Access approved for ${requesterLabel}. Give them this verification code: \`${session.secret}\`. The code expires in 10 minutes.`
767
726
  : `Access approved for ${requesterLabel}. Give them this verification code: \`${session.secret}\`. The code expires in 10 minutes. I could not notify them automatically, so please tell them to send the code manually.`;
768
727
 
769
- void emitFeedEvent({
770
- source: "assistant",
771
- title: "Access Request Approved",
772
- summary: `Granted access request.`,
773
- dedupKey: `guardian-access:${request.id}`,
774
- urgency: undefined,
775
- detailPanel: { kind: "permissionChat" },
776
- }).catch((err) => {
777
- log.warn(
778
- { err, requestId: request.id },
779
- "Failed to emit access request feed event",
780
- );
781
- });
782
-
783
728
  return {
784
729
  ok: true,
785
730
  applied: true,
@@ -853,20 +798,6 @@ const toolGrantRequestResolver: GuardianRequestResolver = {
853
798
  }
854
799
  }
855
800
 
856
- void emitFeedEvent({
857
- source: "assistant",
858
- title: "Tool Grant Denied",
859
- summary: `Denied grant request for ${request.toolName ?? "unknown tool"}.`,
860
- dedupKey: `guardian-grant:${request.id}`,
861
- urgency: "medium",
862
- detailPanel: { kind: "toolPermission" },
863
- }).catch((err) => {
864
- log.warn(
865
- { err, requestId: request.id },
866
- "Failed to emit tool grant denial feed event",
867
- );
868
- });
869
-
870
801
  return { ok: true, applied: true };
871
802
  }
872
803
 
@@ -964,20 +895,6 @@ const toolGrantRequestResolver: GuardianRequestResolver = {
964
895
  }
965
896
  }
966
897
 
967
- void emitFeedEvent({
968
- source: "assistant",
969
- title: "Tool Grant Approved",
970
- summary: `Approved grant request for ${request.toolName ?? "unknown tool"}.`,
971
- dedupKey: `guardian-grant:${request.id}`,
972
- urgency: undefined,
973
- detailPanel: { kind: "toolPermission" },
974
- }).catch((err) => {
975
- log.warn(
976
- { err, requestId: request.id },
977
- "Failed to emit tool grant approval feed event",
978
- );
979
- });
980
-
981
898
  return { ok: true, applied: true, grantMinted: false };
982
899
  },
983
900
  };
@@ -1,14 +1,7 @@
1
1
  import { getConfig } from "../config/loader.js";
2
2
 
3
3
  // Emergency/high-risk numbers that should never be called
4
- const DENIED_NUMBERS = new Set([
5
- "911",
6
- "112",
7
- "999",
8
- "000",
9
- "110",
10
- "119",
11
- ]);
4
+ const DENIED_NUMBERS = new Set(["911", "112", "999", "000", "110", "119"]);
12
5
 
13
6
  /**
14
7
  * Check whether a phone number is a denied emergency number.
@@ -75,3 +68,7 @@ export function getGuardianWaitUpdateSteadyMaxIntervalMs(): number {
75
68
  export function getSilenceTimeoutMs(): number {
76
69
  return 30 * 1000; // 30 seconds
77
70
  }
71
+
72
+ export function getEndCallListenWindowMs(): number {
73
+ return 15 * 1000;
74
+ }
@@ -26,6 +26,7 @@ import type { TtsProvider, TtsProviderId } from "../tts/types.js";
26
26
  import { getLogger } from "../util/logger.js";
27
27
  import { createStreamingEntry } from "./audio-store.js";
28
28
  import {
29
+ getEndCallListenWindowMs,
29
30
  getMaxCallDurationMs,
30
31
  getSilenceTimeoutMs,
31
32
  getUserConsultationTimeoutMs,
@@ -37,6 +38,7 @@ import {
37
38
  registerCallController,
38
39
  unregisterCallController,
39
40
  } from "./call-state.js";
41
+ import { isTerminalState } from "./call-state-machine.js";
40
42
  import {
41
43
  createPendingQuestion,
42
44
  expirePendingQuestions,
@@ -93,6 +95,7 @@ export class CallController {
93
95
  private currentTurnPromise: Promise<void> | null = null;
94
96
  private destroyed = false;
95
97
  private silenceTimer: ReturnType<typeof setTimeout> | null = null;
98
+ private endCallListenTimer: ReturnType<typeof setTimeout> | null = null;
96
99
  private durationTimer: ReturnType<typeof setTimeout> | null = null;
97
100
  private durationWarningTimer: ReturnType<typeof setTimeout> | null = null;
98
101
  /**
@@ -244,6 +247,8 @@ export class CallController {
244
247
  transcript: string,
245
248
  speaker?: PromptSpeakerContext,
246
249
  ): Promise<void> {
250
+ this.cancelPendingEndCall();
251
+
247
252
  const interruptedInFlight =
248
253
  this.state === "processing" || this.state === "speaking";
249
254
  // If we're already processing or speaking, abort the in-flight generation
@@ -295,6 +300,8 @@ export class CallController {
295
300
  return false;
296
301
  }
297
302
 
303
+ this.cancelPendingEndCall();
304
+
298
305
  // Clear the consultation timeout and record
299
306
  clearTimeout(this.pendingGuardianInput.timer);
300
307
  this.pendingGuardianInput = null;
@@ -326,6 +333,8 @@ export class CallController {
326
333
  * position once the current turn completes.
327
334
  */
328
335
  async handleUserInstruction(instructionText: string): Promise<void> {
336
+ this.cancelPendingEndCall();
337
+
329
338
  recordCallEvent(this.callSessionId, "user_instruction_relayed", {
330
339
  instruction: instructionText,
331
340
  });
@@ -408,6 +417,7 @@ export class CallController {
408
417
  destroy(): void {
409
418
  this.destroyed = true;
410
419
  if (this.silenceTimer) clearTimeout(this.silenceTimer);
420
+ if (this.endCallListenTimer) clearTimeout(this.endCallListenTimer);
411
421
  if (this.durationTimer) clearTimeout(this.durationTimer);
412
422
  if (this.durationWarningTimer) clearTimeout(this.durationWarningTimer);
413
423
  if (this.pendingGuardianInput) {
@@ -419,6 +429,7 @@ export class CallController {
419
429
  this.durationEndTimer = null;
420
430
  }
421
431
  this.pendingInstructions = [];
432
+ this.endCallListenTimer = null;
422
433
  this.llmRunVersion++;
423
434
  this.abortCurrentTurn();
424
435
  if (this.activeSynthesisAbort) {
@@ -1075,73 +1086,7 @@ export class CallController {
1075
1086
 
1076
1087
  // Check for END_CALL marker
1077
1088
  if (responseText.includes(END_CALL_MARKER)) {
1078
- // Clear any pending consultation before completing the call.
1079
- // Without this, the consultation timeout can fire on an already-ended
1080
- // call, overwriting 'completed' status back to 'in_progress' and
1081
- // starting a new LLM turn on a dead conversation. Similarly, a late
1082
- // handleUserAnswer could be accepted since pendingGuardianInput is
1083
- // still non-null.
1084
- if (this.pendingGuardianInput) {
1085
- clearTimeout(this.pendingGuardianInput.timer);
1086
-
1087
- // Expire store-side consultation records so clients don't observe
1088
- // a completed call with a dangling pendingQuestion, and guardian
1089
- // replies are cleanly rejected instead of hitting answerCall failures.
1090
- expirePendingQuestions(this.callSessionId);
1091
- const previousRequest = getPendingCanonicalRequestByCallSessionId(
1092
- this.callSessionId,
1093
- );
1094
- if (previousRequest) {
1095
- expireCanonicalGuardianRequest(previousRequest.id);
1096
- }
1097
-
1098
- this.pendingGuardianInput = null;
1099
- }
1100
-
1101
- const currentSession = getCallSession(this.callSessionId);
1102
- const shouldNotifyCompletion = currentSession
1103
- ? currentSession.status !== "completed" &&
1104
- currentSession.status !== "failed" &&
1105
- currentSession.status !== "cancelled"
1106
- : false;
1107
-
1108
- this.transport.endSession("Call completed");
1109
- updateCallSession(this.callSessionId, {
1110
- status: "completed",
1111
- endedAt: Date.now(),
1112
- });
1113
- recordCallEvent(this.callSessionId, "call_ended", {
1114
- reason: "completed",
1115
- });
1116
-
1117
- // Notify the voice conversation
1118
- if (shouldNotifyCompletion && currentSession) {
1119
- finalizeCall(this.callSessionId, currentSession.conversationId);
1120
- }
1121
-
1122
- // Post a pointer message in the initiating conversation
1123
- if (currentSession?.initiatedFromConversationId) {
1124
- const durationMs = currentSession.startedAt
1125
- ? Date.now() - currentSession.startedAt
1126
- : 0;
1127
- addPointerMessage(
1128
- currentSession.initiatedFromConversationId,
1129
- "completed",
1130
- currentSession.toNumber,
1131
- {
1132
- duration: durationMs > 0 ? formatDuration(durationMs) : undefined,
1133
- },
1134
- ).catch((err) => {
1135
- log.warn(
1136
- {
1137
- conversationId: currentSession.initiatedFromConversationId,
1138
- err,
1139
- },
1140
- "Skipping pointer write — origin conversation may no longer exist",
1141
- );
1142
- });
1143
- }
1144
- this.state = "idle";
1089
+ this.scheduleEndCallAfterListenWindow();
1145
1090
  return;
1146
1091
  }
1147
1092
 
@@ -1153,6 +1098,124 @@ export class CallController {
1153
1098
  this.flushPendingInstructions();
1154
1099
  }
1155
1100
 
1101
+ private scheduleEndCallAfterListenWindow(): void {
1102
+ const currentSession = getCallSession(this.callSessionId);
1103
+ if (currentSession && isTerminalState(currentSession.status)) {
1104
+ this.state = "idle";
1105
+ this.currentTurnHandle = null;
1106
+ return;
1107
+ }
1108
+
1109
+ const clearedPendingGuardianInput =
1110
+ this.clearPendingGuardianInputForCallEnd();
1111
+ this.state = "idle";
1112
+ this.currentTurnHandle = null;
1113
+
1114
+ if (this.endCallListenTimer) {
1115
+ clearTimeout(this.endCallListenTimer);
1116
+ this.endCallListenTimer = null;
1117
+ }
1118
+
1119
+ const listenWindowMs = getEndCallListenWindowMs();
1120
+ const callContinues =
1121
+ this.pendingInstructions.length > 0 || listenWindowMs > 0;
1122
+ if (clearedPendingGuardianInput && callContinues) {
1123
+ updateCallSession(this.callSessionId, { status: "in_progress" });
1124
+ }
1125
+
1126
+ if (this.pendingInstructions.length > 0) {
1127
+ this.flushPendingInstructions();
1128
+ return;
1129
+ }
1130
+
1131
+ if (listenWindowMs <= 0) {
1132
+ this.completeCallFromEndMarker();
1133
+ return;
1134
+ }
1135
+
1136
+ this.resetSilenceTimer();
1137
+ this.endCallListenTimer = setTimeout(() => {
1138
+ this.endCallListenTimer = null;
1139
+ this.completeCallFromEndMarker();
1140
+ }, listenWindowMs);
1141
+ }
1142
+
1143
+ private cancelPendingEndCall(): void {
1144
+ if (!this.endCallListenTimer) return;
1145
+ clearTimeout(this.endCallListenTimer);
1146
+ this.endCallListenTimer = null;
1147
+ }
1148
+
1149
+ private clearPendingGuardianInputForCallEnd(): boolean {
1150
+ if (!this.pendingGuardianInput) return false;
1151
+
1152
+ clearTimeout(this.pendingGuardianInput.timer);
1153
+
1154
+ // Expire store-side consultation records so clients don't observe
1155
+ // a completed call with a dangling pendingQuestion, and guardian
1156
+ // replies are cleanly rejected instead of hitting answerCall failures.
1157
+ expirePendingQuestions(this.callSessionId);
1158
+ const previousRequest = getPendingCanonicalRequestByCallSessionId(
1159
+ this.callSessionId,
1160
+ );
1161
+ if (previousRequest) {
1162
+ expireCanonicalGuardianRequest(previousRequest.id);
1163
+ }
1164
+
1165
+ this.pendingGuardianInput = null;
1166
+ return true;
1167
+ }
1168
+
1169
+ private completeCallFromEndMarker(): void {
1170
+ if (this.destroyed) return;
1171
+
1172
+ const currentSession = getCallSession(this.callSessionId);
1173
+ if (currentSession && isTerminalState(currentSession.status)) {
1174
+ this.state = "idle";
1175
+ return;
1176
+ }
1177
+
1178
+ const shouldNotifyCompletion = !!currentSession;
1179
+
1180
+ this.transport.endSession("Call completed");
1181
+ updateCallSession(this.callSessionId, {
1182
+ status: "completed",
1183
+ endedAt: Date.now(),
1184
+ });
1185
+ recordCallEvent(this.callSessionId, "call_ended", {
1186
+ reason: "completed",
1187
+ });
1188
+
1189
+ // Notify the voice conversation
1190
+ if (shouldNotifyCompletion && currentSession) {
1191
+ finalizeCall(this.callSessionId, currentSession.conversationId);
1192
+ }
1193
+
1194
+ // Post a pointer message in the initiating conversation
1195
+ if (currentSession?.initiatedFromConversationId) {
1196
+ const durationMs = currentSession.startedAt
1197
+ ? Date.now() - currentSession.startedAt
1198
+ : 0;
1199
+ addPointerMessage(
1200
+ currentSession.initiatedFromConversationId,
1201
+ "completed",
1202
+ currentSession.toNumber,
1203
+ {
1204
+ duration: durationMs > 0 ? formatDuration(durationMs) : undefined,
1205
+ },
1206
+ ).catch((err) => {
1207
+ log.warn(
1208
+ {
1209
+ conversationId: currentSession.initiatedFromConversationId,
1210
+ err,
1211
+ },
1212
+ "Skipping pointer write — origin conversation may no longer exist",
1213
+ );
1214
+ });
1215
+ }
1216
+ this.state = "idle";
1217
+ }
1218
+
1156
1219
  private isExpectedAbortError(err: unknown): boolean {
1157
1220
  if (!(err instanceof Error)) return false;
1158
1221
  return err.name === "AbortError" || err.name === "APIUserAbortError";
@@ -66,6 +66,8 @@ import {
66
66
  } from "./speaker-identification.js";
67
67
 
68
68
  const log = getLogger("relay-server");
69
+ const UUID_SHAPED_NAME =
70
+ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
69
71
 
70
72
  // ── ConversationRelay message types ──────────────────────────────────
71
73
 
@@ -610,6 +612,12 @@ export class RelayConnection {
610
612
  );
611
613
  this.startNameCapture(outcome.assistantId, outcome.fromNumber);
612
614
  return;
615
+ case "unverified_caller":
616
+ await this.handleUnverifiedCaller(
617
+ outcome.displayName,
618
+ outcome.isGuardian,
619
+ );
620
+ return;
613
621
  case "verification":
614
622
  if (this.controller && resolved.actorTrust.trustClass !== "unknown") {
615
623
  this.controller.setTrustContext(
@@ -667,6 +675,35 @@ export class RelayConnection {
667
675
  });
668
676
  }
669
677
 
678
+ /** Speak verification guidance to a known-but-unverified caller, then disconnect. */
679
+ private async handleUnverifiedCaller(
680
+ displayName: string,
681
+ isGuardian: boolean,
682
+ ): Promise<void> {
683
+ recordCallEvent(this.callSessionId, "inbound_acl_unverified_caller", {
684
+ callSessionId: this.callSessionId,
685
+ isGuardian,
686
+ });
687
+ this.connectionState = "disconnecting";
688
+ updateCallSession(this.callSessionId, {
689
+ status: "failed",
690
+ endedAt: Date.now(),
691
+ lastError: "Inbound voice ACL: caller channel unverified",
692
+ });
693
+ const action = isGuardian
694
+ ? `To verify, open your assistant's contacts page, click Verify next to the phone channel, ` +
695
+ `and follow the prompts. Then call back once the verification session is active.`
696
+ : `Please reach out to the account guardian to start a new verification session, ` +
697
+ `then call back once the verification session is active.`;
698
+ const message =
699
+ `This number is registered as ${displayName}'s phone but has not been verified yet. ` +
700
+ action;
701
+ await speakSystemPrompt(this, message);
702
+ setTimeout(() => {
703
+ this.endSession("Inbound voice ACL: caller channel unverified");
704
+ }, getTtsPlaybackDelayMs());
705
+ }
706
+
670
707
  /** Deny an inbound call with a TTS message and schedule disconnect. */
671
708
  private async denyInboundCall(
672
709
  from: string,
@@ -1615,7 +1652,11 @@ export class RelayConnection {
1615
1652
  private resolveAssistantLabel(): string | null {
1616
1653
  try {
1617
1654
  const name = getAssistantName();
1618
- return name?.trim() || null;
1655
+ const trimmedName = name?.trim();
1656
+ if (!trimmedName || UUID_SHAPED_NAME.test(trimmedName)) {
1657
+ return null;
1658
+ }
1659
+ return trimmedName;
1619
1660
  } catch {
1620
1661
  return null;
1621
1662
  }
@@ -56,6 +56,13 @@ type SetupOutcome =
56
56
  guardianName: string | null;
57
57
  }
58
58
  | { action: "name_capture"; assistantId: string; fromNumber: string }
59
+ | {
60
+ action: "unverified_caller";
61
+ assistantId: string;
62
+ fromNumber: string;
63
+ displayName: string;
64
+ isGuardian: boolean;
65
+ }
59
66
  | { action: "deny"; message: string; logReason: string };
60
67
 
61
68
  // ── Resolved context produced alongside the outcome ──────────────────
@@ -234,6 +241,35 @@ export function routeSetup(ctx: SetupContext): {
234
241
  };
235
242
  }
236
243
 
244
+ // Known caller whose channel hasn't passed verification yet —
245
+ // mirrors the gateway's pre-intercept (twilio-voice-webhook.ts) so
246
+ // calls slipping past it (e.g. canonicalization mismatch between
247
+ // gateway and assistant DBs) still get useful guidance instead of
248
+ // the "I don't recognize this number" name-capture script.
249
+ const unverifiedStatuses = new Set(["unverified", "pending"]);
250
+ const memberChannel = actorTrust.memberRecord?.channel;
251
+ if (memberChannel && unverifiedStatuses.has(memberChannel.status)) {
252
+ log.info(
253
+ {
254
+ callSessionId: ctx.callSessionId,
255
+ from: ctx.from,
256
+ channelId: memberChannel.id,
257
+ channelStatus: memberChannel.status,
258
+ },
259
+ "Inbound voice ACL: known but unverified caller — returning verification guidance",
260
+ );
261
+ return {
262
+ outcome: {
263
+ action: "unverified_caller",
264
+ assistantId,
265
+ fromNumber: ctx.from,
266
+ displayName: actorTrust.memberRecord!.contact.displayName,
267
+ isGuardian: actorTrust.memberRecord!.contact.role === "guardian",
268
+ },
269
+ resolved,
270
+ };
271
+ }
272
+
237
273
  // Unknown caller — name capture flow
238
274
  log.info(
239
275
  {
@@ -33,6 +33,7 @@ export type CallEventType =
33
33
  | "inbound_acl_name_capture_started"
34
34
  | "inbound_acl_name_captured"
35
35
  | "inbound_acl_name_capture_timeout"
36
+ | "inbound_acl_unverified_caller"
36
37
  | "inbound_acl_access_approved"
37
38
  | "inbound_acl_access_denied"
38
39
  | "inbound_acl_access_timeout"
@@ -233,7 +233,7 @@ function buildVoiceCallControlPrompt(opts: {
233
233
  );
234
234
  } else {
235
235
  lines.push(
236
- '7. If the latest user turn is "(call connected — deliver opening greeting)", this is an inbound call you are answering (not a call you initiated). Greet the caller warmly and ask how you can help. Introduce yourself once at the start using your assistant name if you know it (for example: "Hey there, this is Ava, Sam\'s assistant. How can I help?"). If your assistant name is not known, skip the name and just identify yourself as the guardian\'s assistant. Do NOT say "I\'m calling" or "I\'m calling on behalf of". Vary the wording; do not use a fixed template.',
236
+ '7. If the latest user turn is "(call connected — deliver opening greeting)", this is an inbound call you are answering (not a call you initiated). Greet the caller warmly and ask how you can help. Introduce yourself once at the start using your assistant name if you know it (for example: "Hey there, this is Ava, Sam\'s assistant. How can I help?"). If your assistant name is not known, skip the name and just identify yourself as the guardian\'s assistant. Never use a UUID-shaped internal assistant ID as your spoken name. Do NOT say "I\'m calling" or "I\'m calling on behalf of". Vary the wording; do not use a fixed template.',
237
237
  );
238
238
  }
239
239
  lines.push(
@@ -293,7 +293,11 @@ export async function startVoiceTurn(
293
293
  onMessageComplete: (msg) => {
294
294
  opts.onComplete?.();
295
295
  opts.callbacks?.message_complete?.(msg);
296
- if (msg.type === "message_complete" && msg.messageId) {
296
+ if (
297
+ msg.type === "message_complete" &&
298
+ msg.messageId &&
299
+ msg.source !== "aux"
300
+ ) {
297
301
  try {
298
302
  opts.callbacks?.persisted_assistant_message_id?.(msg.messageId);
299
303
  } catch (err) {
@@ -314,7 +318,8 @@ export async function startVoiceTurn(
314
318
 
315
319
  // Phone voice has no interactive permission/secret UI, so apply explicit
316
320
  // per-role policies by default. Local live voice opts into the normal
317
- // client approval path instead.
321
+ // client approval path instead. Side-effect double-defense is wired
322
+ // below at the conversation-configure point.
318
323
  const trustClass = opts.trustContext?.trustClass;
319
324
  const isGuardian = trustClass === "guardian";
320
325
  const approvalMode = opts.approvalMode ?? "phone-call";
@@ -386,7 +391,13 @@ export async function startVoiceTurn(
386
391
  }
387
392
  }
388
393
 
389
- // Configure conversation for this voice turn
394
+ // Non-guardian phone voice forces side-effect tools to prompt so the
395
+ // auto-deny handler below reliably sees a confirmation_request. Without
396
+ // this, a broad allow trust rule (e.g. wildcard bash) would let
397
+ // side-effect tools execute without ever emitting an event for the
398
+ // auto-deny / scoped-grant handler to intercept.
399
+ conversation.forcePromptSideEffects =
400
+ !isGuardian && !usesLocalInteractiveApprovals;
390
401
  conversation.setAssistantId(opts.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID);
391
402
  conversation.callSessionId = voiceSessionId;
392
403
  conversation.setTrustContext(opts.trustContext ?? null);
@@ -528,7 +539,14 @@ export async function startVoiceTurn(
528
539
  return;
529
540
  }
530
541
  } else if (msg.type === "secret_request") {
531
- // Voice has no secret-entry UI, so resolve immediately
542
+ if (usesLocalInteractiveApprovals) {
543
+ // Local live voice runs alongside the desktop client, which has a
544
+ // secret-entry UI (SecretPromptManager). Forward the broadcast and
545
+ // let the prompter's existing registration handle the response.
546
+ broadcastMessage(msg);
547
+ return;
548
+ }
549
+ // Phone voice has no secret-entry UI, so resolve immediately.
532
550
  log.info(
533
551
  { turnId, service: msg.service, field: msg.field },
534
552
  "Auto-resolving secret request for voice turn (no secret-entry UI)",
@@ -549,6 +567,7 @@ export async function startVoiceTurn(
549
567
  conversation.setAssistantId("self");
550
568
  conversation.setVoiceCallControlPrompt(null);
551
569
  conversation.callSessionId = undefined;
570
+ conversation.forcePromptSideEffects = false;
552
571
  // Reset the conversation's client callback to a no-op so the stale
553
572
  // closure doesn't intercept events from future turns on the same conversation.
554
573
  conversation.updateClient(() => {}, true);