@vellumai/assistant 0.6.6 → 0.7.0

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 (1346) hide show
  1. package/AGENTS.md +20 -0
  2. package/ARCHITECTURE.md +45 -36
  3. package/Dockerfile +26 -6
  4. package/README.md +8 -10
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +14 -20
  6. package/bun.lock +306 -119
  7. package/docs/architecture/memory.md +1 -90
  8. package/docs/architecture/security.md +15 -30
  9. package/docs/credential-execution-service.md +7 -5
  10. package/docs/skills.md +10 -10
  11. package/docs/stt-provider-onboarding.md +17 -45
  12. package/examples/plugins/echo/bun.lock +25 -0
  13. package/knip.json +8 -22
  14. package/node_modules/@vellumai/ces-client/bun.lock +33 -0
  15. package/node_modules/@vellumai/ces-client/package.json +25 -0
  16. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +631 -0
  17. package/node_modules/@vellumai/ces-client/src/__tests__/package-boundary.test.ts +138 -0
  18. package/node_modules/@vellumai/ces-client/src/credential-rpc.ts +13 -0
  19. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +296 -0
  20. package/node_modules/@vellumai/ces-client/src/http-log-export.ts +111 -0
  21. package/node_modules/@vellumai/ces-client/src/index.ts +43 -0
  22. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +445 -0
  23. package/node_modules/@vellumai/credential-storage/src/__tests__/package-boundary.test.ts +32 -6
  24. package/node_modules/@vellumai/egress-proxy/src/__tests__/package-boundary.test.ts +32 -1
  25. package/node_modules/@vellumai/gateway-client/bun.lock +39 -0
  26. package/node_modules/@vellumai/gateway-client/package.json +23 -0
  27. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +343 -0
  28. package/node_modules/@vellumai/gateway-client/src/__tests__/package-boundary.test.ts +140 -0
  29. package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +422 -0
  30. package/node_modules/@vellumai/gateway-client/src/index.ts +35 -0
  31. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +331 -0
  32. package/node_modules/@vellumai/gateway-client/src/types.ts +131 -0
  33. package/node_modules/@vellumai/gateway-client/tsconfig.json +20 -0
  34. package/node_modules/@vellumai/{ces-contracts → service-contracts}/bun.lock +1 -1
  35. package/node_modules/@vellumai/{ces-contracts → service-contracts}/package.json +4 -2
  36. package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/contracts.test.ts +5 -1
  37. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +155 -0
  38. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +23 -0
  39. package/node_modules/@vellumai/service-contracts/src/index.ts +25 -0
  40. package/node_modules/@vellumai/{ces-contracts/src/index.ts → service-contracts/src/transport.ts} +6 -28
  41. package/node_modules/@vellumai/service-contracts/src/trust-rules.ts +116 -0
  42. package/node_modules/@vellumai/service-contracts/tsconfig.json +20 -0
  43. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +891 -0
  44. package/node_modules/@vellumai/skill-host-contracts/bun.lock +24 -0
  45. package/node_modules/@vellumai/skill-host-contracts/package.json +18 -0
  46. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +91 -0
  47. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +1348 -0
  48. package/node_modules/@vellumai/skill-host-contracts/src/index.ts +6 -0
  49. package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +11 -0
  50. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +32 -0
  51. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +333 -0
  52. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +444 -0
  53. package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +20 -0
  54. package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +12 -0
  55. package/openapi.yaml +2855 -556
  56. package/package.json +13 -7
  57. package/scripts/check-circular-deps.ts +80 -0
  58. package/scripts/generate-openapi.ts +24 -7
  59. package/{src/memory/graph/inspect.ts → scripts/memory-inspect.ts} +27 -27
  60. package/src/__tests__/access-request-decision.test.ts +2 -11
  61. package/src/__tests__/acp-session.test.ts +4 -150
  62. package/src/__tests__/actor-token-service.test.ts +17 -678
  63. package/src/__tests__/agent-loop-callsite-precedence.test.ts +2 -6
  64. package/src/__tests__/agent-loop-override-profile.test.ts +404 -0
  65. package/src/__tests__/agent-loop-thinking.test.ts +4 -4
  66. package/src/__tests__/agent-wake-override-profile.test.ts +261 -0
  67. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -1
  68. package/src/__tests__/anthropic-provider.test.ts +127 -15
  69. package/src/__tests__/app-routes-csp.test.ts +106 -55
  70. package/src/__tests__/approval-cascade.test.ts +3 -355
  71. package/src/__tests__/approval-conversation-turn.test.ts +3 -8
  72. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +1 -1
  73. package/src/__tests__/approval-primitive.test.ts +2 -1
  74. package/src/__tests__/approval-routes-http.test.ts +34 -451
  75. package/src/__tests__/assistant-events-sse-hardening.test.ts +73 -80
  76. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -3
  77. package/src/__tests__/attachment-upload-trusted-source.test.ts +139 -0
  78. package/src/__tests__/attachments-store.test.ts +46 -1
  79. package/src/__tests__/audit-log-rotation.test.ts +2 -1
  80. package/src/__tests__/auto-analysis-end-to-end.test.ts +8 -20
  81. package/src/__tests__/background-shell-bash.test.ts +227 -0
  82. package/src/__tests__/background-shell-host-bash.test.ts +474 -0
  83. package/src/__tests__/background-tool-registry.test.ts +145 -0
  84. package/src/__tests__/background-tool-routes.test.ts +175 -0
  85. package/src/__tests__/btw-routes.test.ts +147 -183
  86. package/src/__tests__/call-controller.test.ts +15 -2
  87. package/src/__tests__/call-conversation-messages.test.ts +2 -1
  88. package/src/__tests__/call-domain.test.ts +2 -2
  89. package/src/__tests__/call-pointer-messages.test.ts +11 -13
  90. package/src/__tests__/call-recovery.test.ts +2 -1
  91. package/src/__tests__/call-routes-http.test.ts +3 -14
  92. package/src/__tests__/call-store.test.ts +2 -1
  93. package/src/__tests__/cancel-resolves-conversation-key.test.ts +31 -62
  94. package/src/__tests__/canonical-guardian-store.test.ts +2 -2
  95. package/src/__tests__/catalog-files.test.ts +0 -26
  96. package/src/__tests__/ces-rpc-credential-backend.test.ts +1 -1
  97. package/src/__tests__/channel-approval-routes.test.ts +79 -49
  98. package/src/__tests__/channel-approval.test.ts +9 -7
  99. package/src/__tests__/channel-approvals.test.ts +9 -180
  100. package/src/__tests__/channel-delivery-store.test.ts +11 -10
  101. package/src/__tests__/channel-guardian.test.ts +14 -25
  102. package/src/__tests__/channel-readiness-service.test.ts +8 -6
  103. package/src/__tests__/channel-reply-delivery.test.ts +3 -19
  104. package/src/__tests__/channel-retry-sweep.test.ts +2 -5
  105. package/src/__tests__/checker.test.ts +274 -3921
  106. package/src/__tests__/circuit-breaker-pipeline.test.ts +1 -1
  107. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +208 -0
  108. package/src/__tests__/cli.test.ts +1 -38
  109. package/src/__tests__/compaction-events.test.ts +0 -1
  110. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  111. package/src/__tests__/compaction-strip-metadata-clear.test.ts +2 -2
  112. package/src/__tests__/compaction-timeout-recovery.test.ts +1 -1
  113. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -7
  114. package/src/__tests__/config-model-image-provider.test.ts +0 -1
  115. package/src/__tests__/config-schema-cmd.test.ts +1 -1
  116. package/src/__tests__/config-schema.test.ts +30 -221
  117. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -25
  118. package/src/__tests__/contact-store-user-file.test.ts +2 -1
  119. package/src/__tests__/contacts-tools.test.ts +56 -29
  120. package/src/__tests__/contacts-write.test.ts +6 -61
  121. package/src/__tests__/context-search-agent-protocol.test.ts +230 -0
  122. package/src/__tests__/context-search-agent-runner.test.ts +998 -0
  123. package/src/__tests__/context-search-conversations-source.test.ts +320 -0
  124. package/src/__tests__/context-search-fanout.test.ts +380 -0
  125. package/src/__tests__/context-search-memory-source.test.ts +311 -0
  126. package/src/__tests__/context-search-pkb-source.test.ts +444 -0
  127. package/src/__tests__/context-search-types.test.ts +95 -0
  128. package/src/__tests__/context-search-workspace-source.test.ts +545 -0
  129. package/src/__tests__/context-window-manager.test.ts +25 -0
  130. package/src/__tests__/conversation-abort-tool-results.test.ts +10 -1
  131. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +631 -0
  132. package/src/__tests__/conversation-agent-loop-overflow.test.ts +15 -2
  133. package/src/__tests__/conversation-agent-loop.test.ts +24 -2
  134. package/src/__tests__/conversation-analysis-routes.test.ts +60 -82
  135. package/src/__tests__/conversation-attachments.test.ts +9 -20
  136. package/src/__tests__/conversation-attention-store.test.ts +2 -1
  137. package/src/__tests__/conversation-attention-telegram.test.ts +4 -2
  138. package/src/__tests__/conversation-clear-safety.test.ts +53 -95
  139. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -39
  140. package/src/__tests__/conversation-crud-inference-profile.test.ts +54 -0
  141. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +63 -157
  142. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  143. package/src/__tests__/conversation-disk-view.test.ts +5 -4
  144. package/src/__tests__/conversation-fork-crud.test.ts +26 -55
  145. package/src/__tests__/conversation-fork-route.test.ts +5 -74
  146. package/src/__tests__/conversation-inference-profile-list.test.ts +128 -0
  147. package/src/__tests__/conversation-inference-profile-route.test.ts +216 -0
  148. package/src/__tests__/conversation-init.benchmark.test.ts +4 -81
  149. package/src/__tests__/conversation-key-store-disk-view.test.ts +2 -1
  150. package/src/__tests__/conversation-lifecycle.test.ts +0 -1
  151. package/src/__tests__/conversation-list-source.test.ts +2 -2
  152. package/src/__tests__/conversation-load-history-repair.test.ts +0 -1
  153. package/src/__tests__/conversation-pairing.test.ts +0 -1
  154. package/src/__tests__/conversation-pre-run-repair.test.ts +137 -297
  155. package/src/__tests__/conversation-process-callsite.test.ts +0 -1
  156. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -1
  157. package/src/__tests__/conversation-queue.test.ts +1 -33
  158. package/src/__tests__/conversation-routes-disk-view.test.ts +124 -97
  159. package/src/__tests__/conversation-routes-guardian-reply.test.ts +80 -55
  160. package/src/__tests__/conversation-routes-slash-commands.test.ts +83 -12
  161. package/src/__tests__/conversation-runtime-assembly.test.ts +41 -84
  162. package/src/__tests__/conversation-slash-commands.test.ts +6 -43
  163. package/src/__tests__/conversation-slash-queue.test.ts +0 -1
  164. package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
  165. package/src/__tests__/conversation-speed-override.test.ts +0 -1
  166. package/src/__tests__/conversation-starter-routes.test.ts +177 -55
  167. package/src/__tests__/conversation-starters-cadence.test.ts +2 -2
  168. package/src/__tests__/conversation-store.test.ts +2 -375
  169. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
  170. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +6 -6
  171. package/src/__tests__/conversation-unread-route.test.ts +1 -1
  172. package/src/__tests__/conversation-usage.test.ts +2 -1
  173. package/src/__tests__/conversation-wipe.test.ts +2 -103
  174. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -1
  175. package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
  176. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
  177. package/src/__tests__/conversations-defer-cli.test.ts +150 -0
  178. package/src/__tests__/credential-execution-admin-cli.test.ts +1 -1
  179. package/src/__tests__/credential-execution-api-key-propagation.test.ts +2 -2
  180. package/src/__tests__/credential-execution-approval-bridge.test.ts +22 -289
  181. package/src/__tests__/credential-execution-client.test.ts +1 -1
  182. package/src/__tests__/credential-execution-managed-contract.test.ts +1 -1
  183. package/src/__tests__/credential-security-invariants.test.ts +14 -0
  184. package/src/__tests__/credentials-cli.test.ts +45 -21
  185. package/src/__tests__/daemon-credential-client.test.ts +23 -108
  186. package/src/__tests__/db-acp-history.test.ts +284 -0
  187. package/src/__tests__/db-activation-state.test.ts +240 -0
  188. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +2 -1
  189. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +248 -0
  190. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +2 -1
  191. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +116 -0
  192. package/src/__tests__/db-rename-inference-profile-snake-case-migration.test.ts +132 -0
  193. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  194. package/src/__tests__/delete-propagation.test.ts +3 -2
  195. package/src/__tests__/deterministic-verification-control-plane.test.ts +39 -32
  196. package/src/__tests__/dm-backfill.test.ts +3 -2
  197. package/src/__tests__/edit-propagation.test.ts +5 -7
  198. package/src/__tests__/embedding-managed-proxy-selection.test.ts +1 -1
  199. package/src/__tests__/empty-response-pipeline.test.ts +1 -1
  200. package/src/__tests__/events-client-registration.test.ts +297 -0
  201. package/src/__tests__/file-write-tool.test.ts +2 -4
  202. package/src/__tests__/filing-service.test.ts +144 -17
  203. package/src/__tests__/followup-tools.test.ts +2 -1
  204. package/src/__tests__/gateway-client-managed-outbound.test.ts +8 -12
  205. package/src/__tests__/gateway-only-enforcement.test.ts +2 -6
  206. package/src/__tests__/gateway-only-guard.test.ts +4 -3
  207. package/src/__tests__/gemini-provider.test.ts +276 -10
  208. package/src/__tests__/graph-extraction-event-date.test.ts +30 -0
  209. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -1
  210. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -2
  211. package/src/__tests__/guardian-action-followup-store.test.ts +2 -1
  212. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +9 -9
  213. package/src/__tests__/guardian-action-late-reply.test.ts +2 -1
  214. package/src/__tests__/guardian-action-store.test.ts +2 -1
  215. package/src/__tests__/guardian-action-sweep.test.ts +9 -8
  216. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -1
  217. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +21 -118
  218. package/src/__tests__/guardian-dispatch.test.ts +14 -11
  219. package/src/__tests__/guardian-grant-minting.test.ts +9 -15
  220. package/src/__tests__/guardian-outbound-http.test.ts +71 -106
  221. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -2
  222. package/src/__tests__/guardian-routing-invariants.test.ts +34 -90
  223. package/src/__tests__/guardian-routing-state.test.ts +14 -22
  224. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -2
  225. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +253 -0
  226. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +8 -4
  227. package/src/__tests__/heartbeat-service.test.ts +39 -21
  228. package/src/__tests__/helpers/call-route-handler.ts +72 -0
  229. package/src/__tests__/helpers/channel-test-adapter.ts +161 -0
  230. package/src/__tests__/helpers/gateway-classify-mock.ts +67 -0
  231. package/src/__tests__/helpers/mock-logger.ts +36 -0
  232. package/src/__tests__/history-repair-pipeline.test.ts +1 -1
  233. package/src/__tests__/home-state-routes.test.ts +10 -31
  234. package/src/__tests__/host-browser-e2e-cloud.test.ts +2 -1
  235. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +12 -2
  236. package/src/__tests__/host-browser-routes.test.ts +36 -91
  237. package/src/__tests__/host-browser-ws-events-e2e.test.ts +10 -2
  238. package/src/__tests__/host-proxy-interface.test.ts +3 -3
  239. package/src/__tests__/host-shell-tool.test.ts +2 -4
  240. package/src/__tests__/host-transfer-pending-interactions.test.ts +160 -0
  241. package/src/__tests__/host-transfer-proxy.test.ts +733 -0
  242. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  243. package/src/__tests__/http-user-message-parity.test.ts +20 -11
  244. package/src/__tests__/inbound-invite-redemption.test.ts +3 -2
  245. package/src/__tests__/injector-chain.test.ts +16 -17
  246. package/src/__tests__/inline-skill-load-permissions.test.ts +41 -206
  247. package/src/__tests__/install-skill-routing.test.ts +1 -1
  248. package/src/__tests__/invite-redemption-service.test.ts +2 -1
  249. package/src/__tests__/invite-routes-http.test.ts +80 -12
  250. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -1
  251. package/src/__tests__/jobs-store-upsert-debounced.test.ts +2 -1
  252. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +157 -0
  253. package/src/__tests__/list-messages-attachments.test.ts +52 -55
  254. package/src/__tests__/list-messages-page-latest.test.ts +283 -0
  255. package/src/__tests__/list-messages-tool-merge.test.ts +16 -17
  256. package/src/__tests__/llm-call-pipeline.test.ts +7 -8
  257. package/src/__tests__/llm-context-normalization.test.ts +69 -4
  258. package/src/__tests__/llm-context-route-provider.test.ts +39 -113
  259. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -1
  260. package/src/__tests__/llm-resolver.test.ts +211 -0
  261. package/src/__tests__/llm-schema.test.ts +57 -1
  262. package/src/__tests__/llm-usage-store.test.ts +2 -1
  263. package/src/__tests__/log-export-workspace.test.ts +28 -17
  264. package/src/__tests__/mcp-abort-signal.test.ts +2 -3
  265. package/src/__tests__/mcp-client-auth.test.ts +2 -3
  266. package/src/__tests__/memory-admin-recall.test.ts +221 -0
  267. package/src/__tests__/memory-recall-log-store.test.ts +2 -1
  268. package/src/__tests__/memory-retrieval-pipeline.test.ts +6 -8
  269. package/src/__tests__/memory-upsert-concurrency.test.ts +2 -1
  270. package/src/__tests__/migration-cross-version-compatibility.test.ts +14 -13
  271. package/src/__tests__/migration-export-http.test.ts +17 -17
  272. package/src/__tests__/migration-export-to-gcs.test.ts +491 -0
  273. package/src/__tests__/migration-import-commit-http.test.ts +16 -16
  274. package/src/__tests__/migration-import-from-gcs.test.ts +533 -0
  275. package/src/__tests__/migration-import-from-url.test.ts +16 -23
  276. package/src/__tests__/migration-import-preflight-http.test.ts +13 -13
  277. package/src/__tests__/migration-jobs-status.test.ts +164 -0
  278. package/src/__tests__/migration-validate-http.test.ts +48 -83
  279. package/src/__tests__/mock-gateway-ipc.ts +32 -62
  280. package/src/__tests__/model-intents.test.ts +15 -2
  281. package/src/__tests__/nl-approval-parser.test.ts +13 -17
  282. package/src/__tests__/non-member-access-request.test.ts +13 -5
  283. package/src/__tests__/notification-guardian-path.test.ts +15 -8
  284. package/src/__tests__/notification-schedule-notify-dedup.test.ts +2 -1
  285. package/src/__tests__/notification-telegram-adapter.test.ts +57 -55
  286. package/src/__tests__/oauth-apps-routes.test.ts +76 -122
  287. package/src/__tests__/oauth-cli.test.ts +14 -1
  288. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  289. package/src/__tests__/oauth-provider-visibility.test.ts +3 -1
  290. package/src/__tests__/oauth-providers-routes.test.ts +78 -101
  291. package/src/__tests__/oauth-store.test.ts +3 -1
  292. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -3
  293. package/src/__tests__/openai-provider.test.ts +105 -6
  294. package/src/__tests__/openai-responses-provider.test.ts +146 -4
  295. package/src/__tests__/openrouter-provider-only.test.ts +22 -4
  296. package/src/__tests__/overflow-reduce-pipeline.test.ts +4 -9
  297. package/src/__tests__/permission-types.test.ts +3 -18
  298. package/src/__tests__/persistence-pipeline.test.ts +3 -2
  299. package/src/__tests__/pipeline-runner.test.ts +1 -1
  300. package/src/__tests__/platform-bash-auto-approve.test.ts +27 -20
  301. package/src/__tests__/platform.test.ts +11 -63
  302. package/src/__tests__/playbook-execution.test.ts +2 -1
  303. package/src/__tests__/playbook-tools.test.ts +2 -1
  304. package/src/__tests__/plugin-bootstrap.test.ts +51 -5
  305. package/src/__tests__/plugin-registry.test.ts +30 -0
  306. package/src/__tests__/plugin-route-contribution.test.ts +17 -11
  307. package/src/__tests__/plugin-skill-contribution.test.ts +3 -3
  308. package/src/__tests__/plugin-tool-contribution.test.ts +10 -4
  309. package/src/__tests__/plugin-types.test.ts +1 -1
  310. package/src/__tests__/pricing.test.ts +151 -2
  311. package/src/__tests__/profiler-routes.test.ts +112 -177
  312. package/src/__tests__/provider-send-message-override-profile.test.ts +223 -0
  313. package/src/__tests__/proxy-approval-callback.test.ts +6 -554
  314. package/src/__tests__/qdrant-collection-migration.test.ts +7 -7
  315. package/src/__tests__/reaction-persistence.test.ts +3 -2
  316. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  317. package/src/__tests__/recording-handler.test.ts +0 -2
  318. package/src/__tests__/registry.test.ts +1 -0
  319. package/src/__tests__/relay-server.test.ts +19 -4
  320. package/src/__tests__/require-fresh-approval.test.ts +19 -168
  321. package/src/__tests__/resolve-trust-class.test.ts +2 -1
  322. package/src/__tests__/retry-thinking-tool-choice.test.ts +19 -7
  323. package/src/__tests__/retry-verbosity-normalization.test.ts +139 -0
  324. package/src/__tests__/runtime-attachment-metadata.test.ts +26 -6
  325. package/src/__tests__/runtime-events-sse-parity.test.ts +12 -13
  326. package/src/__tests__/runtime-events-sse.test.ts +13 -21
  327. package/src/__tests__/schedule-routes.test.ts +226 -129
  328. package/src/__tests__/schedule-store.test.ts +119 -1
  329. package/src/__tests__/schedule-tools.test.ts +2 -1
  330. package/src/__tests__/scheduler-recurrence.test.ts +2 -1
  331. package/src/__tests__/scheduler-reuse-conversation.test.ts +2 -1
  332. package/src/__tests__/scheduler-wake.test.ts +356 -0
  333. package/src/__tests__/scoped-approval-grants.test.ts +2 -1
  334. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -1
  335. package/src/__tests__/secret-detection-handler.test.ts +2 -9
  336. package/src/__tests__/secret-ingress-http.test.ts +38 -21
  337. package/src/__tests__/secret-routes-managed-proxy.test.ts +46 -102
  338. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  339. package/src/__tests__/send-endpoint-busy.test.ts +38 -25
  340. package/src/__tests__/sequence-store.test.ts +2 -1
  341. package/src/__tests__/server-history-render.test.ts +2 -2
  342. package/src/__tests__/service-contracts-import-guard.test.ts +185 -0
  343. package/src/__tests__/set-permission-mode.test.ts +0 -10
  344. package/src/__tests__/settings-routes.test.ts +35 -68
  345. package/src/__tests__/skill-boundary-guard.test.ts +105 -0
  346. package/src/__tests__/skill-load-inline-command.test.ts +2 -2
  347. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  348. package/src/__tests__/skill-runtime-path.test.ts +64 -0
  349. package/src/__tests__/skills-file-content-endpoint.test.ts +0 -2
  350. package/src/__tests__/slack-inbound-verification.test.ts +11 -2
  351. package/src/__tests__/slack-messaging-token-resolution.test.ts +1 -3
  352. package/src/__tests__/slack-reaction-approvals.test.ts +4 -4
  353. package/src/__tests__/slack-share-routes.test.ts +37 -72
  354. package/src/__tests__/subagent-call-site-routing.test.ts +79 -0
  355. package/src/__tests__/subagent-fork-spawn.test.ts +20 -28
  356. package/src/__tests__/subagent-notify-parent.test.ts +6 -29
  357. package/src/__tests__/subagent-role-registry.test.ts +3 -3
  358. package/src/__tests__/subagent-spawn-tool-fork.test.ts +52 -104
  359. package/src/__tests__/subagent-tools.test.ts +0 -1
  360. package/src/__tests__/suggestion-routes.test.ts +55 -62
  361. package/src/__tests__/task-compiler.test.ts +2 -1
  362. package/src/__tests__/task-management-tools.test.ts +2 -1
  363. package/src/__tests__/task-memory-cleanup.test.ts +2 -1
  364. package/src/__tests__/task-scheduler.test.ts +2 -1
  365. package/src/__tests__/telegram-config.test.ts +0 -1
  366. package/src/__tests__/terminal-tools.test.ts +5 -314
  367. package/src/__tests__/test-preload.ts +0 -11
  368. package/src/__tests__/thread-backfill.test.ts +3 -2
  369. package/src/__tests__/token-estimate-pipeline.test.ts +68 -15
  370. package/src/__tests__/tool-approval-handler.test.ts +21 -63
  371. package/src/__tests__/tool-audit-listener.test.ts +3 -3
  372. package/src/__tests__/tool-domain-event-publisher.test.ts +3 -3
  373. package/src/__tests__/tool-error-pipeline.test.ts +6 -6
  374. package/src/__tests__/tool-execute-pipeline.test.ts +6 -8
  375. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +64 -1
  376. package/src/__tests__/tool-executor-lifecycle-events.test.ts +28 -56
  377. package/src/__tests__/tool-executor.test.ts +322 -1633
  378. package/src/__tests__/tool-grant-request-escalation.test.ts +90 -311
  379. package/src/__tests__/tool-result-truncate-pipeline.test.ts +1 -1
  380. package/src/__tests__/trust-context-guards.test.ts +1 -1
  381. package/src/__tests__/trusted-contact-approval-notifier.test.ts +7 -15
  382. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +178 -354
  383. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  384. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  385. package/src/__tests__/trusted-contact-verification.test.ts +2 -1
  386. package/src/__tests__/turn-boundary-resolution.test.ts +2 -1
  387. package/src/__tests__/twilio-routes.test.ts +25 -66
  388. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -7
  389. package/src/__tests__/usage-routes.test.ts +73 -90
  390. package/src/__tests__/user-plugin-loader.test.ts +54 -12
  391. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -2
  392. package/src/__tests__/verification-control-plane-policy.test.ts +95 -14
  393. package/src/__tests__/voice-ingress-preflight.test.ts +5 -5
  394. package/src/__tests__/voice-invite-redemption.test.ts +2 -1
  395. package/src/__tests__/voice-scoped-grant-consumer.test.ts +3 -3
  396. package/src/__tests__/voice-session-bridge.test.ts +285 -106
  397. package/src/__tests__/volume-security-guard.test.ts +0 -2
  398. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -1
  399. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +3 -1
  400. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +2 -1
  401. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +1 -1
  402. package/src/__tests__/workspace-migration-052-seed-default-inference-profiles.test.ts +260 -0
  403. package/src/__tests__/workspace-migration-053-release-notes-acp-codex.test.ts +225 -0
  404. package/src/__tests__/workspace-migration-054-seed-recall-callsite.test.ts +235 -0
  405. package/src/__tests__/workspace-migration-055-release-notes-agentic-recall.test.ts +128 -0
  406. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +232 -0
  407. package/src/__tests__/workspace-migration-acp-sessions-ui.test.ts +144 -0
  408. package/src/__tests__/workspace-migration-drop-user-md.test.ts +1 -1
  409. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +274 -0
  410. package/src/acp/__tests__/client-handler.test.ts +64 -0
  411. package/src/acp/__tests__/helpers/acp-config-stub.ts +62 -0
  412. package/src/acp/__tests__/helpers/which-stub.ts +45 -0
  413. package/src/acp/__tests__/session-manager-persistence.test.ts +366 -0
  414. package/src/acp/__tests__/session-manager-startup.test.ts +159 -0
  415. package/src/acp/__tests__/session-manager.test.ts +83 -0
  416. package/src/acp/client-handler.ts +23 -139
  417. package/src/acp/resolve-agent.test.ts +291 -0
  418. package/src/acp/resolve-agent.ts +176 -0
  419. package/src/acp/session-manager.ts +166 -7
  420. package/src/acp/types.ts +2 -50
  421. package/src/agent/loop.ts +37 -14
  422. package/src/agent/message-types.ts +0 -2
  423. package/src/approvals/AGENTS.md +1 -1
  424. package/src/approvals/__tests__/guardian-feed-event.test.ts +1 -9
  425. package/src/approvals/approval-primitive.ts +3 -20
  426. package/src/approvals/guardian-decision-primitive.ts +37 -68
  427. package/src/approvals/guardian-request-resolvers.ts +29 -103
  428. package/src/avatar/character-components.ts +6 -6
  429. package/src/{config/bundled-skills/settings/tools → avatar}/identity-avatar.ts +1 -1
  430. package/src/backup/__tests__/backup-worker.test.ts +0 -2
  431. package/src/backup/__tests__/paths.test.ts +3 -2
  432. package/src/backup/backup-worker.ts +1 -10
  433. package/src/backup/paths.ts +2 -18
  434. package/src/backup/restore.ts +7 -11
  435. package/src/browser/__tests__/operations.test.ts +0 -35
  436. package/src/browser/operations.ts +1 -47
  437. package/src/bundler/package-resolver.ts +2 -6
  438. package/src/calls/active-call-lease.ts +1 -1
  439. package/src/calls/call-constants.ts +1 -1
  440. package/src/calls/call-controller.ts +1 -5
  441. package/src/calls/call-domain.ts +14 -14
  442. package/src/calls/call-pointer-messages.ts +4 -9
  443. package/src/calls/call-store.ts +2 -1
  444. package/src/calls/guardian-action-sweep.ts +9 -25
  445. package/src/calls/guardian-dispatch.ts +1 -20
  446. package/src/calls/media-stream-audio-transcode.ts +2 -41
  447. package/src/calls/media-stream-server.ts +2 -3
  448. package/src/calls/media-stream-stt-session.ts +1 -3
  449. package/src/calls/relay-access-wait.ts +5 -8
  450. package/src/calls/relay-server.ts +15 -18
  451. package/src/calls/relay-setup-router.ts +2 -2
  452. package/src/calls/relay-verification.ts +4 -4
  453. package/src/calls/twilio-rest.ts +1 -1
  454. package/src/calls/twilio-routes.ts +160 -78
  455. package/src/calls/voice-control-protocol.ts +10 -10
  456. package/src/calls/voice-ingress-preflight.ts +2 -2
  457. package/src/calls/voice-session-bridge.ts +137 -42
  458. package/src/channels/__tests__/types.test.ts +25 -3
  459. package/src/channels/permission-profiles.ts +2 -72
  460. package/src/channels/types.ts +42 -26
  461. package/src/cli/AGENTS.md +1 -0
  462. package/src/cli/__tests__/notifications.test.ts +12 -10
  463. package/src/cli/commands/__tests__/attachment.test.ts +14 -8
  464. package/src/cli/commands/__tests__/backup.test.ts +3 -14
  465. package/src/cli/commands/__tests__/browser.test.ts +36 -31
  466. package/src/cli/commands/__tests__/cache.test.ts +23 -18
  467. package/src/cli/commands/__tests__/memory-v2.test.ts +396 -0
  468. package/src/cli/commands/__tests__/task.test.ts +36 -35
  469. package/src/cli/commands/__tests__/trust.test.ts +602 -0
  470. package/src/cli/commands/__tests__/ui-confirm.test.ts +14 -14
  471. package/src/cli/commands/__tests__/ui.test.ts +17 -17
  472. package/src/cli/commands/__tests__/watchers.test.ts +29 -29
  473. package/src/cli/commands/__tests__/webhooks.test.ts +544 -0
  474. package/src/cli/commands/attachment.ts +12 -8
  475. package/src/cli/commands/auth.ts +1 -1
  476. package/src/cli/commands/avatar.ts +192 -9
  477. package/src/cli/commands/backup.ts +14 -44
  478. package/src/cli/commands/browser.ts +52 -4
  479. package/src/cli/commands/cache.ts +7 -5
  480. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  481. package/src/cli/commands/clients.ts +11 -12
  482. package/src/cli/commands/completions.ts +1 -1
  483. package/src/cli/commands/contacts.ts +10 -10
  484. package/src/cli/commands/conversations-defer.ts +364 -0
  485. package/src/cli/commands/conversations-import.ts +2 -3
  486. package/src/cli/commands/conversations.ts +63 -53
  487. package/src/cli/commands/credential-execution.ts +1 -1
  488. package/src/cli/commands/credentials.ts +139 -5
  489. package/src/cli/commands/default-action.ts +1 -1
  490. package/src/cli/commands/domain.ts +2 -2
  491. package/src/cli/commands/email.ts +7 -7
  492. package/src/cli/commands/image-generation.ts +1 -1
  493. package/src/cli/commands/keys.ts +2 -2
  494. package/src/cli/commands/mcp.ts +1 -1
  495. package/src/cli/commands/memory-v2.ts +343 -0
  496. package/src/cli/commands/memory.ts +8 -8
  497. package/src/cli/commands/notifications.ts +21 -20
  498. package/src/cli/commands/oauth/__tests__/connect.test.ts +23 -5
  499. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +1 -1
  500. package/src/cli/commands/oauth/__tests__/mode.test.ts +1 -1
  501. package/src/cli/commands/oauth/__tests__/status.test.ts +1 -1
  502. package/src/cli/commands/oauth/__tests__/token.test.ts +1 -1
  503. package/src/cli/commands/oauth/connect.ts +2 -2
  504. package/src/cli/commands/oauth/shared.ts +29 -2
  505. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -6
  506. package/src/cli/commands/platform/__tests__/connect.test.ts +23 -11
  507. package/src/cli/commands/platform/__tests__/disconnect.test.ts +22 -10
  508. package/src/cli/commands/platform/__tests__/status.test.ts +22 -10
  509. package/src/cli/commands/platform/connect.ts +3 -3
  510. package/src/cli/commands/platform/disconnect.ts +4 -6
  511. package/src/cli/commands/platform/index.ts +12 -10
  512. package/src/cli/commands/routes.ts +7 -1
  513. package/src/cli/commands/sequence.ts +7 -7
  514. package/src/cli/commands/skills.ts +188 -82
  515. package/src/cli/commands/task.ts +12 -10
  516. package/src/cli/commands/trust.ts +460 -162
  517. package/src/cli/commands/ui.ts +3 -3
  518. package/src/cli/commands/usage.ts +10 -5
  519. package/src/cli/commands/watchers.ts +8 -8
  520. package/src/cli/commands/webhooks.ts +270 -0
  521. package/src/cli/lib/daemon-avatar-client.ts +37 -0
  522. package/src/cli/lib/daemon-credential-client.ts +27 -189
  523. package/src/cli/lib/ipc-params.ts +22 -0
  524. package/src/cli/program.ts +4 -0
  525. package/src/cli.ts +1 -61
  526. package/src/config/acp-defaults.test.ts +57 -0
  527. package/src/config/acp-defaults.ts +40 -0
  528. package/src/config/acp-schema.ts +1 -1
  529. package/src/config/assistant-feature-flags.ts +18 -142
  530. package/src/config/bundled-skills/acp/SKILL.md +44 -16
  531. package/src/config/bundled-skills/acp/TOOLS.json +45 -1
  532. package/src/config/bundled-skills/acp/tools/acp-list-agents.ts +12 -0
  533. package/src/config/bundled-skills/acp/tools/acp-steer.ts +12 -0
  534. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +14 -14
  535. package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -4
  536. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +11 -6
  537. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +6 -6
  538. package/src/config/bundled-skills/media-processing/services/reduce.ts +0 -13
  539. package/src/config/bundled-skills/messaging/tools/gmail-mime-helpers.ts +1 -1
  540. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  541. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -1
  542. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +1 -1
  543. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -1
  544. package/src/config/bundled-skills/settings/SKILL.md +2 -17
  545. package/src/config/bundled-skills/settings/TOOLS.json +0 -56
  546. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  547. package/src/config/bundled-tool-registry.ts +4 -6
  548. package/src/config/env.ts +7 -8
  549. package/src/config/feature-flag-registry.json +16 -24
  550. package/src/config/llm-resolver.ts +51 -33
  551. package/src/config/loader.ts +12 -15
  552. package/src/config/schema.ts +5 -72
  553. package/src/config/schemas/__tests__/filing.test.ts +58 -0
  554. package/src/config/schemas/__tests__/memory-v2.test.ts +186 -0
  555. package/src/config/schemas/filing.ts +12 -0
  556. package/src/config/schemas/host-browser.ts +2 -2
  557. package/src/config/schemas/inference.ts +0 -2
  558. package/src/config/schemas/ingress.ts +1 -1
  559. package/src/config/schemas/llm.ts +51 -9
  560. package/src/config/schemas/memory-storage.ts +1 -1
  561. package/src/config/schemas/memory-v2.ts +176 -0
  562. package/src/config/schemas/memory.ts +2 -0
  563. package/src/config/schemas/security.ts +0 -60
  564. package/src/config/schemas/services.ts +46 -7
  565. package/src/config/skills.ts +1 -1
  566. package/src/config/types.ts +0 -41
  567. package/src/contacts/contact-store.ts +2 -2
  568. package/src/contacts/contacts-write.ts +0 -38
  569. package/src/contacts/types.ts +8 -10
  570. package/src/context/token-estimator.ts +1 -1
  571. package/src/context/tool-result-truncation.ts +1 -1
  572. package/src/context/window-manager.ts +1 -1
  573. package/src/credential-execution/approval-bridge.ts +7 -69
  574. package/src/credential-execution/client.ts +17 -422
  575. package/src/credential-execution/feature-gates.ts +1 -2
  576. package/src/credential-execution/managed-catalog.ts +1 -1
  577. package/src/credential-health/credential-health-service.ts +1 -1
  578. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -13
  579. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  580. package/src/daemon/__tests__/daemon-skill-host.test.ts +272 -0
  581. package/src/daemon/__tests__/meet-host-supervisor.test.ts +587 -0
  582. package/src/daemon/__tests__/meet-manifest-loader.test.ts +463 -0
  583. package/src/daemon/approval-generators.ts +2 -14
  584. package/src/daemon/classifier.ts +0 -106
  585. package/src/daemon/config-watcher.ts +14 -54
  586. package/src/daemon/connection-policy.ts +0 -14
  587. package/src/daemon/conversation-agent-loop-handlers.ts +37 -6
  588. package/src/daemon/conversation-agent-loop.ts +164 -53
  589. package/src/daemon/conversation-attachments.ts +5 -81
  590. package/src/daemon/conversation-error.ts +9 -5
  591. package/src/daemon/conversation-history.ts +1 -1
  592. package/src/daemon/conversation-launch.ts +1 -1
  593. package/src/daemon/conversation-messaging.ts +1 -1
  594. package/src/daemon/conversation-notifiers.ts +1 -1
  595. package/src/daemon/conversation-process.ts +9 -13
  596. package/src/daemon/conversation-runtime-assembly.ts +26 -88
  597. package/src/daemon/conversation-slash.ts +4 -160
  598. package/src/daemon/conversation-store.ts +368 -0
  599. package/src/daemon/conversation-surfaces.ts +5 -4
  600. package/src/daemon/conversation-tool-setup.ts +23 -172
  601. package/src/daemon/conversation.ts +46 -182
  602. package/src/daemon/daemon-control.ts +3 -3
  603. package/src/daemon/daemon-skill-host.ts +262 -0
  604. package/src/daemon/external-plugins-bootstrap.ts +67 -13
  605. package/src/daemon/handlers/config-channels.ts +2 -2
  606. package/src/daemon/handlers/config-embeddings.ts +1 -1
  607. package/src/daemon/handlers/config-ingress.ts +24 -2
  608. package/src/daemon/handlers/config-model.test.ts +17 -0
  609. package/src/daemon/handlers/config-model.ts +7 -52
  610. package/src/daemon/handlers/config-telegram.ts +6 -53
  611. package/src/daemon/handlers/config-voice.ts +1 -1
  612. package/src/daemon/handlers/conversations.ts +22 -156
  613. package/src/daemon/handlers/recording.ts +1 -1
  614. package/src/daemon/handlers/shared.ts +34 -35
  615. package/src/daemon/handlers/skills.ts +15 -23
  616. package/src/daemon/host-transfer-proxy.ts +500 -0
  617. package/src/daemon/lifecycle.ts +23 -258
  618. package/src/daemon/meet-host-startup.ts +51 -0
  619. package/src/daemon/meet-host-supervisor.ts +781 -0
  620. package/src/daemon/meet-manifest-loader.ts +410 -0
  621. package/src/daemon/memory-v2-startup.ts +35 -0
  622. package/src/daemon/message-protocol.ts +4 -7
  623. package/src/daemon/message-types/acp.ts +1 -0
  624. package/src/daemon/message-types/conversations.ts +16 -2
  625. package/src/daemon/message-types/host-transfer.ts +41 -0
  626. package/src/daemon/message-types/integrations.ts +6 -0
  627. package/src/daemon/message-types/messages.ts +14 -14
  628. package/src/daemon/message-types/schedules.ts +1 -0
  629. package/src/daemon/message-types/settings.ts +0 -6
  630. package/src/daemon/message-types/shared.ts +5 -2
  631. package/src/daemon/message-types/subagents.ts +2 -1
  632. package/src/daemon/message-types/workspace.ts +0 -2
  633. package/src/daemon/pkb-reminder-builder.test.ts +13 -12
  634. package/src/daemon/pkb-reminder-builder.ts +8 -16
  635. package/src/daemon/process-message.ts +616 -0
  636. package/src/daemon/providers-setup.ts +14 -6
  637. package/src/daemon/server.ts +79 -1274
  638. package/src/daemon/shutdown-handlers.ts +1 -1
  639. package/src/daemon/startup-error.ts +1 -1
  640. package/src/daemon/trust-context.ts +32 -0
  641. package/src/daemon/wake-target-adapter.ts +223 -0
  642. package/src/email/feature-gate.ts +1 -1
  643. package/src/events/domain-events.ts +1 -8
  644. package/src/events/tool-audit-listener.ts +2 -8
  645. package/src/events/tool-metrics-listener.ts +1 -4
  646. package/src/filing/filing-service.ts +194 -54
  647. package/src/followups/followup-store.ts +3 -71
  648. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +89 -21
  649. package/src/heartbeat/heartbeat-service.ts +32 -11
  650. package/src/home/__tests__/phase5-exit-criteria.test.ts +18 -1
  651. package/src/home/__tests__/rollup-producer.test.ts +67 -2
  652. package/src/home/assistant-feed-authoring.ts +8 -1
  653. package/src/home/feed-types.ts +1 -1
  654. package/src/home/relationship-state-writer.ts +1 -1
  655. package/src/home/rewrite-feed-title.ts +58 -0
  656. package/src/home/rollup-producer.ts +16 -3
  657. package/src/inbound/platform-callback-registration.ts +1 -17
  658. package/src/ipc/__tests__/attachment-ipc.test.ts +128 -66
  659. package/src/ipc/__tests__/browser-ipc.test.ts +75 -51
  660. package/src/ipc/__tests__/cache-ipc.test.ts +52 -107
  661. package/src/ipc/__tests__/cli-ipc.test.ts +9 -6
  662. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +254 -0
  663. package/src/ipc/__tests__/skill-server.test.ts +182 -0
  664. package/src/ipc/__tests__/socket-path.test.ts +69 -23
  665. package/src/ipc/__tests__/ui-request-route.test.ts +241 -216
  666. package/src/ipc/__tests__/watcher-ipc.test.ts +33 -33
  667. package/src/ipc/assistant-server.ts +450 -0
  668. package/src/ipc/cli-client.ts +3 -3
  669. package/src/ipc/gateway-client.test.ts +131 -0
  670. package/src/ipc/gateway-client.ts +98 -123
  671. package/src/ipc/ipc-framing.ts +281 -0
  672. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +152 -0
  673. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +219 -0
  674. package/src/ipc/routes/db-proxy.ts +73 -0
  675. package/src/ipc/routes/route-adapter.ts +32 -0
  676. package/src/ipc/routes/trust-rules.test.ts +218 -0
  677. package/src/ipc/skill-ipc-types.ts +13 -0
  678. package/src/ipc/skill-routes/__tests__/config.test.ts +146 -0
  679. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +402 -0
  680. package/src/ipc/skill-routes/__tests__/identity.test.ts +81 -0
  681. package/src/ipc/skill-routes/__tests__/log.test.ts +133 -0
  682. package/src/ipc/skill-routes/__tests__/memory.test.ts +178 -0
  683. package/src/ipc/skill-routes/__tests__/platform.test.ts +111 -0
  684. package/src/ipc/skill-routes/__tests__/providers.test.ts +265 -0
  685. package/src/ipc/skill-routes/__tests__/registries.test.ts +361 -0
  686. package/src/ipc/skill-routes/config.ts +47 -0
  687. package/src/ipc/skill-routes/events.ts +131 -0
  688. package/src/ipc/skill-routes/identity.ts +34 -0
  689. package/src/ipc/skill-routes/index.ts +37 -0
  690. package/src/ipc/skill-routes/log.ts +40 -0
  691. package/src/ipc/skill-routes/memory.ts +76 -0
  692. package/src/ipc/skill-routes/platform.ts +39 -0
  693. package/src/ipc/skill-routes/providers.ts +163 -0
  694. package/src/ipc/skill-routes/registries.ts +393 -0
  695. package/src/ipc/skill-server.ts +771 -0
  696. package/src/ipc/skill-socket-path.ts +20 -0
  697. package/src/ipc/socket-cleanup.ts +92 -0
  698. package/src/ipc/socket-path.ts +63 -32
  699. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +374 -0
  700. package/src/live-voice/__tests__/live-voice-archive.test.ts +525 -0
  701. package/src/live-voice/__tests__/live-voice-events.test.ts +473 -0
  702. package/src/live-voice/__tests__/live-voice-integration.test.ts +359 -0
  703. package/src/live-voice/__tests__/live-voice-metrics.test.ts +179 -0
  704. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +349 -0
  705. package/src/live-voice/__tests__/live-voice-stt.test.ts +244 -0
  706. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +337 -0
  707. package/src/live-voice/__tests__/live-voice-tts.test.ts +337 -0
  708. package/src/live-voice/__tests__/protocol.test.ts +295 -0
  709. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +421 -0
  710. package/src/live-voice/live-voice-archive.ts +758 -0
  711. package/src/live-voice/live-voice-metrics.ts +472 -0
  712. package/src/live-voice/live-voice-session-manager.ts +222 -0
  713. package/src/live-voice/live-voice-session.ts +1144 -0
  714. package/src/live-voice/live-voice-tts.ts +260 -0
  715. package/src/live-voice/protocol.ts +524 -0
  716. package/src/mcp/client.ts +2 -2
  717. package/src/media/types.ts +4 -4
  718. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +4 -28
  719. package/src/memory/__tests__/auto-analysis-guard.test.ts +2 -2
  720. package/src/memory/__tests__/conversation-analyze-job.test.ts +7 -62
  721. package/src/memory/__tests__/conversation-group-migration.test.ts +2 -2
  722. package/src/memory/__tests__/find-analysis-conversation.test.ts +2 -1
  723. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +235 -0
  724. package/src/memory/admin.ts +65 -7
  725. package/src/memory/app-git-service.ts +0 -14
  726. package/src/memory/attachments-store.ts +14 -16
  727. package/src/memory/auto-analysis-enqueue.ts +2 -17
  728. package/src/memory/canonical-guardian-store.ts +2 -1
  729. package/src/memory/channel-verification-sessions.ts +1 -1
  730. package/src/memory/checkpoints.ts +1 -1
  731. package/src/memory/context-search/agent-protocol.ts +424 -0
  732. package/src/memory/context-search/agent-runner.ts +1295 -0
  733. package/src/memory/context-search/format.ts +160 -0
  734. package/src/memory/context-search/limits.ts +106 -0
  735. package/src/memory/context-search/search.ts +387 -0
  736. package/src/memory/context-search/sources/conversations.ts +278 -0
  737. package/src/memory/context-search/sources/memory.ts +90 -0
  738. package/src/memory/context-search/sources/pkb.ts +468 -0
  739. package/src/memory/context-search/sources/workspace.ts +1255 -0
  740. package/src/memory/context-search/types.ts +49 -0
  741. package/src/memory/conversation-analyze-job.ts +3 -24
  742. package/src/memory/conversation-attention-store.ts +1 -1
  743. package/src/memory/conversation-bootstrap.ts +1 -1
  744. package/src/memory/conversation-crud.ts +69 -127
  745. package/src/memory/conversation-directories.ts +1 -11
  746. package/src/memory/conversation-display-order-migration.ts +11 -2
  747. package/src/memory/conversation-group-migration.ts +20 -4
  748. package/src/memory/conversation-key-store.ts +3 -4
  749. package/src/memory/conversation-queries.ts +13 -26
  750. package/src/memory/conversation-starter-validation.ts +88 -0
  751. package/src/memory/conversation-starters-cadence.ts +1 -1
  752. package/src/memory/conversation-title-service.ts +2 -1
  753. package/src/memory/db-init.ts +14 -4
  754. package/src/memory/db-maintenance.ts +1 -1
  755. package/src/memory/delivery-channels.ts +1 -14
  756. package/src/memory/delivery-crud.ts +2 -32
  757. package/src/memory/delivery-status.ts +1 -1
  758. package/src/memory/embedding-gemini.test.ts +4 -4
  759. package/src/memory/external-conversation-store.ts +1 -1
  760. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +412 -0
  761. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +225 -0
  762. package/src/memory/graph/bootstrap.test.ts +2 -7
  763. package/src/memory/graph/bootstrap.ts +2 -1
  764. package/src/memory/graph/capability-seed.ts +3 -3
  765. package/src/memory/graph/compaction.ts +1 -1
  766. package/src/memory/graph/consolidation.ts +13 -10
  767. package/src/memory/graph/conversation-graph-memory.ts +151 -1
  768. package/src/memory/graph/decay.ts +1 -1
  769. package/src/memory/graph/extraction.ts +53 -21
  770. package/src/memory/graph/graph-memory-state-store.ts +1 -1
  771. package/src/memory/graph/graph-search.test.ts +94 -2
  772. package/src/memory/graph/graph-search.ts +22 -7
  773. package/src/memory/graph/image-ref-utils.ts +1 -1
  774. package/src/memory/graph/retriever.test.ts +158 -4
  775. package/src/memory/graph/retriever.ts +17 -5
  776. package/src/memory/graph/store.test.ts +2 -1
  777. package/src/memory/graph/store.ts +1 -1
  778. package/src/memory/graph/tool-handlers.ts +73 -247
  779. package/src/memory/graph/tools.ts +35 -53
  780. package/src/memory/group-crud.ts +1 -2
  781. package/src/memory/guardian-action-store.ts +2 -1
  782. package/src/memory/guardian-approvals.ts +1 -1
  783. package/src/memory/guardian-rate-limits.ts +1 -1
  784. package/src/memory/indexer.ts +43 -17
  785. package/src/memory/invite-store.ts +1 -1
  786. package/src/memory/job-handlers/backfill.ts +1 -1
  787. package/src/memory/job-handlers/cleanup.ts +2 -1
  788. package/src/memory/job-handlers/conversation-starters.ts +18 -10
  789. package/src/memory/job-handlers/embedding.test.ts +2 -1
  790. package/src/memory/job-handlers/embedding.ts +1 -1
  791. package/src/memory/job-handlers/index-maintenance.ts +1 -1
  792. package/src/memory/job-handlers/summarization.ts +3 -3
  793. package/src/memory/job-utils.ts +3 -3
  794. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +362 -0
  795. package/src/memory/jobs/embed-concept-page.ts +210 -0
  796. package/src/memory/jobs/embed-pkb-file.test.ts +2 -1
  797. package/src/memory/jobs-store.ts +10 -2
  798. package/src/memory/jobs-worker.ts +58 -5
  799. package/src/memory/lifecycle-events-store.ts +1 -1
  800. package/src/memory/llm-request-log-store.ts +1 -1
  801. package/src/memory/llm-usage-store.ts +1 -1
  802. package/src/memory/media-store.ts +1 -1
  803. package/src/memory/memory-recall-log-store.ts +1 -1
  804. package/src/memory/migrations/038-actor-token-records.ts +3 -0
  805. package/src/memory/migrations/039-actor-refresh-token-records.ts +3 -0
  806. package/src/memory/migrations/226-schedule-wake-conversation-id.ts +11 -0
  807. package/src/memory/migrations/227-add-conversation-inference-profile.ts +18 -0
  808. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +27 -0
  809. package/src/memory/migrations/229-delete-private-conversations.test.ts +1087 -0
  810. package/src/memory/migrations/229-delete-private-conversations.ts +210 -0
  811. package/src/memory/migrations/230-acp-session-history.ts +41 -0
  812. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +128 -0
  813. package/src/memory/migrations/232-activation-state.ts +38 -0
  814. package/src/memory/migrations/index.ts +10 -0
  815. package/src/memory/migrations/registry.ts +7 -0
  816. package/src/memory/pkb/pkb-index.test.ts +4 -5
  817. package/src/memory/pkb/pkb-reconcile.test.ts +4 -5
  818. package/src/memory/pkb/pkb-search.test.ts +83 -3
  819. package/src/memory/pkb/pkb-search.ts +27 -14
  820. package/src/memory/published-pages-store.ts +1 -1
  821. package/src/memory/schema/acp.ts +30 -0
  822. package/src/memory/schema/conversations.ts +1 -1
  823. package/src/memory/schema/index.ts +1 -0
  824. package/src/memory/schema/infrastructure.ts +1 -32
  825. package/src/memory/schema/memory-graph.ts +36 -14
  826. package/src/memory/scoped-approval-grants.ts +2 -1
  827. package/src/memory/search/semantic.ts +2 -2
  828. package/src/memory/shared-app-links-store.ts +2 -1
  829. package/src/memory/tool-usage-store.ts +1 -1
  830. package/src/memory/trace-event-store.ts +2 -1
  831. package/src/memory/turn-events-store.ts +1 -1
  832. package/src/memory/v2/__tests__/activation-store.test.ts +202 -0
  833. package/src/memory/v2/__tests__/activation.test.ts +956 -0
  834. package/src/memory/v2/__tests__/backfill-jobs.test.ts +610 -0
  835. package/src/memory/v2/__tests__/consolidation-job.test.ts +395 -0
  836. package/src/memory/v2/__tests__/edges.test.ts +435 -0
  837. package/src/memory/v2/__tests__/injection.test.ts +792 -0
  838. package/src/memory/v2/__tests__/migration.test.ts +812 -0
  839. package/src/memory/v2/__tests__/page-store.test.ts +334 -0
  840. package/src/memory/v2/__tests__/qdrant.test.ts +438 -0
  841. package/src/memory/v2/__tests__/sim.test.ts +549 -0
  842. package/src/memory/v2/__tests__/skill-content.test.ts +85 -0
  843. package/src/memory/v2/__tests__/skill-qdrant.test.ts +657 -0
  844. package/src/memory/v2/__tests__/skill-store.test.ts +351 -0
  845. package/src/memory/v2/__tests__/sweep-job.test.ts +441 -0
  846. package/src/memory/v2/activation-store.ts +109 -0
  847. package/src/memory/v2/activation.ts +490 -0
  848. package/src/memory/v2/backfill-jobs.ts +442 -0
  849. package/src/memory/v2/consolidation-job.ts +304 -0
  850. package/src/memory/v2/edges.ts +217 -0
  851. package/src/memory/v2/injection.ts +307 -0
  852. package/src/memory/v2/migration.ts +654 -0
  853. package/src/memory/v2/now-text.ts +38 -0
  854. package/src/memory/v2/page-store.ts +245 -0
  855. package/src/memory/v2/prompts/consolidation.ts +185 -0
  856. package/src/memory/v2/prompts/sweep.ts +56 -0
  857. package/src/memory/v2/qdrant.ts +342 -0
  858. package/src/memory/v2/sim.ts +206 -0
  859. package/src/memory/v2/skill-content.ts +42 -0
  860. package/src/memory/v2/skill-qdrant.ts +395 -0
  861. package/src/memory/v2/skill-store.ts +128 -0
  862. package/src/memory/v2/sweep-job.ts +298 -0
  863. package/src/memory/v2/types.ts +116 -0
  864. package/src/memory/validation.ts +1 -1
  865. package/src/messaging/providers/index.ts +262 -0
  866. package/src/messaging/providers/slack/api.ts +242 -0
  867. package/src/messaging/providers/slack/message-metadata.ts +1 -1
  868. package/src/messaging/providers/slack/send.ts +383 -0
  869. package/src/messaging/providers/telegram-bot/adapter.ts +4 -42
  870. package/src/messaging/providers/telegram-bot/api.ts +253 -0
  871. package/src/messaging/providers/telegram-bot/client.ts +17 -58
  872. package/src/messaging/providers/telegram-bot/send.ts +232 -0
  873. package/src/messaging/providers/whatsapp/adapter.ts +4 -36
  874. package/src/messaging/providers/whatsapp/api.ts +319 -0
  875. package/src/messaging/providers/whatsapp/client.ts +4 -48
  876. package/src/messaging/providers/whatsapp/send.ts +209 -0
  877. package/src/notifications/adapters/slack.ts +5 -23
  878. package/src/notifications/adapters/telegram.ts +8 -29
  879. package/src/notifications/conversation-candidates.ts +1 -1
  880. package/src/notifications/conversation-seed-composer.ts +12 -6
  881. package/src/notifications/copy-composer.ts +1 -1
  882. package/src/notifications/decision-engine.ts +1 -1
  883. package/src/notifications/decisions-store.ts +1 -1
  884. package/src/notifications/deliveries-store.ts +2 -1
  885. package/src/notifications/deterministic-checks.ts +1 -1
  886. package/src/notifications/events-store.ts +1 -13
  887. package/src/notifications/preferences-store.ts +1 -1
  888. package/src/notifications/signal.ts +0 -9
  889. package/src/oauth/connection-resolver.ts +11 -2
  890. package/src/oauth/oauth-store.ts +2 -1
  891. package/src/outbound-proxy/index.ts +0 -1
  892. package/src/permissions/approval-policy.test.ts +149 -132
  893. package/src/permissions/approval-policy.ts +65 -91
  894. package/src/permissions/checker.test.ts +632 -0
  895. package/src/permissions/checker.ts +266 -459
  896. package/src/permissions/gateway-threshold-reader.ts +28 -47
  897. package/src/permissions/ipc-risk-types.ts +95 -0
  898. package/src/permissions/prompter.ts +4 -9
  899. package/src/permissions/risk-types.ts +24 -210
  900. package/src/permissions/types.ts +17 -47
  901. package/src/permissions/workspace-policy.ts +2 -4
  902. package/src/playbooks/playbook-compiler.ts +1 -1
  903. package/src/plugins/defaults/index.ts +1 -1
  904. package/src/plugins/defaults/injectors.ts +18 -21
  905. package/src/plugins/defaults/llm-call.ts +6 -9
  906. package/src/plugins/defaults/memory-retrieval.ts +1 -6
  907. package/src/plugins/defaults/overflow-reduce.ts +9 -5
  908. package/src/plugins/defaults/token-estimate.ts +2 -3
  909. package/src/plugins/registry.ts +61 -1
  910. package/src/plugins/types.ts +6 -7
  911. package/src/plugins/user-loader.ts +36 -10
  912. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +197 -0
  913. package/src/prompts/persona-resolver.ts +2 -4
  914. package/src/prompts/system-prompt.ts +39 -0
  915. package/src/prompts/templates/SOUL.md +3 -1
  916. package/src/providers/__tests__/provider-env-vars.test.ts +0 -21
  917. package/src/providers/__tests__/retry-callsite.test.ts +3 -6
  918. package/src/providers/anthropic/client.ts +71 -19
  919. package/src/providers/call-site-routing.ts +7 -3
  920. package/src/providers/fireworks/client.ts +3 -0
  921. package/src/providers/gemini/client.ts +96 -22
  922. package/src/providers/managed-proxy/context.ts +0 -12
  923. package/src/providers/model-catalog.ts +83 -8
  924. package/src/providers/model-intents.ts +7 -8
  925. package/src/providers/openai/chat-completions-provider.ts +37 -7
  926. package/src/providers/openai/responses-provider.ts +39 -4
  927. package/src/providers/openrouter/client.ts +4 -5
  928. package/src/providers/provider-env-vars.ts +4 -12
  929. package/src/providers/provider-send-message.ts +16 -11
  930. package/src/providers/registry.ts +1 -1
  931. package/src/providers/retry.ts +52 -23
  932. package/src/providers/speech-to-text/openai-whisper-stream.ts +1 -1
  933. package/src/providers/speech-to-text/openai-whisper.ts +3 -6
  934. package/src/providers/speech-to-text/provider-catalog.ts +75 -0
  935. package/src/providers/speech-to-text/xai.ts +5 -5
  936. package/src/providers/thinking-config.ts +34 -0
  937. package/src/providers/types.ts +22 -10
  938. package/src/runtime/AGENTS.md +10 -9
  939. package/src/runtime/__tests__/agent-wake.test.ts +33 -9
  940. package/src/runtime/__tests__/client-registry.test.ts +5 -27
  941. package/src/runtime/__tests__/interactive-ui.test.ts +157 -246
  942. package/src/runtime/access-request-helper.ts +9 -20
  943. package/src/runtime/actor-trust-resolver.ts +2 -2
  944. package/src/runtime/agent-wake.ts +174 -68
  945. package/src/runtime/approval-conversation-turn.ts +2 -15
  946. package/src/runtime/approval-message-composer.ts +11 -60
  947. package/src/runtime/assistant-event.ts +18 -66
  948. package/src/runtime/auth/__tests__/guard-tests.test.ts +6 -30
  949. package/src/runtime/auth/__tests__/middleware.test.ts +10 -10
  950. package/src/runtime/auth/__tests__/route-policy.test.ts +0 -8
  951. package/src/runtime/auth/context.ts +9 -0
  952. package/src/runtime/auth/middleware.ts +4 -4
  953. package/src/runtime/auth/route-policy.ts +195 -4
  954. package/src/runtime/auth/token-service.ts +1 -100
  955. package/src/runtime/capability-tokens.ts +89 -313
  956. package/src/runtime/channel-approval-types.ts +1 -6
  957. package/src/runtime/channel-approvals.ts +7 -79
  958. package/src/runtime/channel-readiness-service.ts +2 -2
  959. package/src/runtime/channel-reply-delivery.ts +2 -8
  960. package/src/runtime/channel-retry-sweep.ts +20 -17
  961. package/src/runtime/client-registry.ts +21 -28
  962. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -7
  963. package/src/runtime/gateway-client.ts +37 -378
  964. package/src/runtime/guardian-action-grant-minter.ts +2 -3
  965. package/src/runtime/guardian-action-message-composer.ts +11 -52
  966. package/src/runtime/guardian-action-service.ts +19 -7
  967. package/src/runtime/guardian-decision-types.ts +4 -65
  968. package/src/runtime/guardian-reply-router.ts +10 -19
  969. package/src/runtime/guardian-vellum-migration.ts +5 -64
  970. package/src/runtime/http-errors.ts +3 -0
  971. package/src/runtime/http-router.ts +50 -7
  972. package/src/runtime/http-server.ts +345 -1110
  973. package/src/runtime/http-types.ts +15 -98
  974. package/src/runtime/interactive-ui-types.ts +145 -0
  975. package/src/runtime/interactive-ui.ts +38 -196
  976. package/src/runtime/invite-redemption-service.ts +1 -1
  977. package/src/runtime/invite-redemption-templates.ts +1 -1
  978. package/src/runtime/local-actor-identity.ts +13 -43
  979. package/src/runtime/message-composer-types.ts +134 -0
  980. package/src/runtime/middleware/rate-limiter.ts +1 -1
  981. package/src/runtime/middleware/request-logger.ts +5 -2
  982. package/src/runtime/migrations/__tests__/job-registry.test.ts +346 -0
  983. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +16 -0
  984. package/src/runtime/migrations/job-registry.ts +281 -0
  985. package/src/runtime/migrations/vbundle-builder.ts +3 -4
  986. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  987. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -13
  988. package/src/runtime/migrations/vbundle-tar-stream.ts +11 -3
  989. package/src/runtime/nl-approval-parser.ts +16 -21
  990. package/src/runtime/pending-interactions.ts +29 -12
  991. package/src/runtime/routes/__tests__/acp-routes.test.ts +395 -0
  992. package/src/runtime/routes/__tests__/backup-routes.test.ts +204 -320
  993. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +72 -4
  994. package/src/runtime/routes/__tests__/stt-routes.test.ts +182 -223
  995. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +230 -0
  996. package/src/{ipc/__tests__/task-ipc.test.ts → runtime/routes/__tests__/task-routes.test.ts} +116 -96
  997. package/src/runtime/routes/__tests__/tts-routes.test.ts +185 -289
  998. package/src/runtime/routes/access-request-decision.ts +25 -50
  999. package/src/runtime/routes/acp-routes.test.ts +371 -0
  1000. package/src/runtime/routes/acp-routes.ts +392 -166
  1001. package/src/runtime/routes/app-management-routes.ts +464 -660
  1002. package/src/runtime/routes/app-routes.ts +192 -177
  1003. package/src/runtime/routes/approval-routes.ts +116 -434
  1004. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +24 -84
  1005. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +3 -10
  1006. package/src/runtime/routes/attachment-routes.ts +409 -253
  1007. package/src/runtime/routes/audio-routes.ts +51 -18
  1008. package/src/runtime/routes/avatar-routes.ts +82 -75
  1009. package/src/runtime/routes/background-tool-routes.ts +94 -0
  1010. package/src/runtime/routes/backup-routes.ts +154 -336
  1011. package/src/runtime/routes/brain-graph-routes.ts +83 -110
  1012. package/src/runtime/routes/browser-routes.ts +141 -0
  1013. package/src/runtime/routes/btw-routes.ts +62 -106
  1014. package/src/runtime/routes/cache-routes.ts +96 -0
  1015. package/src/runtime/routes/call-routes.ts +208 -247
  1016. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +1 -1
  1017. package/src/runtime/routes/channel-delivery-routes.ts +25 -27
  1018. package/src/runtime/routes/channel-readiness-routes.ts +83 -120
  1019. package/src/runtime/routes/channel-route-definitions.ts +62 -0
  1020. package/src/runtime/routes/channel-route-shared.ts +14 -18
  1021. package/src/runtime/routes/channel-verification-routes.ts +207 -187
  1022. package/src/runtime/routes/client-routes.ts +48 -0
  1023. package/src/runtime/routes/contact-routes.ts +533 -407
  1024. package/src/runtime/routes/conversation-analysis-routes.ts +48 -49
  1025. package/src/runtime/routes/conversation-attention-routes.ts +55 -67
  1026. package/src/runtime/routes/conversation-list-routes.ts +265 -0
  1027. package/src/runtime/routes/conversation-management-routes.ts +626 -715
  1028. package/src/runtime/routes/conversation-query-routes.ts +510 -460
  1029. package/src/runtime/routes/conversation-routes.ts +456 -368
  1030. package/src/runtime/routes/conversation-starter-routes.ts +121 -71
  1031. package/src/runtime/routes/credential-prompt-routes.ts +124 -0
  1032. package/src/runtime/routes/debug-routes.ts +34 -39
  1033. package/src/runtime/routes/defer-routes.ts +230 -0
  1034. package/src/runtime/routes/diagnostics-routes.ts +79 -70
  1035. package/src/runtime/routes/documents-routes.ts +117 -106
  1036. package/src/runtime/routes/errors.ts +132 -0
  1037. package/src/runtime/routes/events-routes.ts +97 -58
  1038. package/src/runtime/routes/filing-routes.ts +65 -78
  1039. package/src/runtime/routes/global-search-routes.ts +51 -57
  1040. package/src/runtime/routes/group-routes.ts +199 -181
  1041. package/src/runtime/routes/guardian-action-routes.ts +103 -169
  1042. package/src/runtime/routes/guardian-approval-interception.ts +27 -58
  1043. package/src/runtime/routes/guardian-approval-prompt.ts +10 -21
  1044. package/src/runtime/routes/guardian-approval-reply-helpers.ts +2 -6
  1045. package/src/runtime/routes/guardian-expiry-sweep.ts +19 -36
  1046. package/src/runtime/routes/heartbeat-routes.ts +194 -209
  1047. package/src/runtime/routes/home-feed-routes.ts +85 -187
  1048. package/src/runtime/routes/home-state-routes.ts +27 -24
  1049. package/src/runtime/routes/host-bash-routes.ts +42 -52
  1050. package/src/runtime/routes/host-browser-routes.ts +38 -69
  1051. package/src/runtime/routes/host-cu-routes.ts +74 -70
  1052. package/src/runtime/routes/host-file-routes.ts +50 -60
  1053. package/src/runtime/routes/host-transfer-routes.ts +220 -0
  1054. package/src/runtime/routes/http-adapter.ts +172 -0
  1055. package/src/runtime/routes/identity-routes.ts +83 -79
  1056. package/src/runtime/routes/inbound-conversation.ts +11 -18
  1057. package/src/runtime/routes/inbound-message-handler.ts +74 -110
  1058. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +79 -138
  1059. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +2 -3
  1060. package/src/runtime/routes/inbound-stages/background-dispatch.ts +54 -90
  1061. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +25 -50
  1062. package/src/runtime/routes/inbound-stages/edit-intercept.ts +7 -7
  1063. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  1064. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +5 -6
  1065. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -24
  1066. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -10
  1067. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -4
  1068. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +3 -3
  1069. package/src/runtime/routes/inbound-stages/verification-intercept.ts +19 -26
  1070. package/src/runtime/routes/index.ts +197 -0
  1071. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +25 -32
  1072. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +22 -31
  1073. package/src/runtime/routes/integrations/slack/channel.ts +69 -66
  1074. package/src/runtime/routes/integrations/slack/share.ts +49 -58
  1075. package/src/runtime/routes/integrations/telegram.ts +91 -74
  1076. package/src/runtime/routes/integrations/twilio.ts +163 -240
  1077. package/src/runtime/routes/integrations/vercel.ts +57 -54
  1078. package/src/runtime/routes/interface-routes.ts +43 -0
  1079. package/src/runtime/routes/internal-oauth-routes.ts +56 -0
  1080. package/src/runtime/routes/internal-twilio-routes.ts +46 -0
  1081. package/src/runtime/routes/llm-context-normalization.ts +4 -2
  1082. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  1083. package/src/runtime/routes/log-export-routes.ts +90 -100
  1084. package/src/runtime/routes/memory-item-routes.test.ts +152 -175
  1085. package/src/runtime/routes/memory-item-routes.ts +243 -323
  1086. package/src/runtime/routes/memory-v2-routes.ts +193 -0
  1087. package/src/runtime/routes/migration-rollback-routes.ts +167 -212
  1088. package/src/runtime/routes/migration-routes.ts +877 -374
  1089. package/src/runtime/routes/notification-routes.ts +199 -70
  1090. package/src/runtime/routes/oauth-apps.ts +254 -251
  1091. package/src/runtime/routes/oauth-providers.ts +66 -57
  1092. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +60 -120
  1093. package/src/runtime/routes/playground/__tests__/guard.test.ts +34 -54
  1094. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +107 -151
  1095. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +41 -117
  1096. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +95 -138
  1097. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +115 -217
  1098. package/src/runtime/routes/playground/__tests__/state.test.ts +41 -90
  1099. package/src/runtime/routes/playground/conversation-not-found.ts +9 -11
  1100. package/src/runtime/routes/playground/force-compact.ts +41 -54
  1101. package/src/runtime/routes/playground/guard.ts +18 -19
  1102. package/src/runtime/routes/playground/helpers.ts +103 -0
  1103. package/src/runtime/routes/playground/index.ts +15 -25
  1104. package/src/runtime/routes/playground/inject-failures.ts +48 -64
  1105. package/src/runtime/routes/playground/reset-circuit.ts +31 -57
  1106. package/src/runtime/routes/playground/seed-conversation.ts +66 -92
  1107. package/src/runtime/routes/playground/seeded-conversations.ts +60 -64
  1108. package/src/runtime/routes/playground/state.ts +23 -24
  1109. package/src/runtime/routes/profiler-routes.ts +132 -167
  1110. package/src/runtime/routes/ps-routes.ts +120 -0
  1111. package/src/runtime/routes/recording-routes.ts +197 -258
  1112. package/src/runtime/routes/rename-conversation-routes.ts +89 -0
  1113. package/src/runtime/routes/schedule-routes.ts +238 -242
  1114. package/src/runtime/routes/secret-routes.ts +219 -265
  1115. package/src/runtime/routes/secrets-deps.ts +24 -0
  1116. package/src/runtime/routes/settings-routes.ts +361 -441
  1117. package/src/runtime/routes/skills-routes.ts +434 -469
  1118. package/src/runtime/routes/stt-routes.ts +196 -206
  1119. package/src/runtime/routes/subagents-routes.ts +125 -141
  1120. package/src/runtime/routes/suggest-trust-rule-routes.ts +244 -0
  1121. package/src/runtime/routes/surface-action-routes.ts +135 -190
  1122. package/src/runtime/routes/surface-content-routes.ts +84 -118
  1123. package/src/runtime/routes/task-routes.ts +354 -0
  1124. package/src/runtime/routes/telemetry-routes.ts +33 -49
  1125. package/src/runtime/routes/trace-event-routes.ts +55 -74
  1126. package/src/runtime/routes/trust-rules-routes.ts +147 -239
  1127. package/src/runtime/routes/tts-routes.ts +187 -169
  1128. package/src/runtime/routes/types.ts +139 -0
  1129. package/src/{ipc/routes/ui-request.ts → runtime/routes/ui-request-routes.ts} +23 -17
  1130. package/src/runtime/routes/upgrade-broadcast-routes.ts +156 -197
  1131. package/src/runtime/routes/usage-routes.ts +143 -169
  1132. package/src/runtime/routes/user-routes.ts +102 -18
  1133. package/src/runtime/routes/wake-conversation-routes.ts +49 -0
  1134. package/src/{ipc/routes/watcher.ts → runtime/routes/watcher-routes.ts} +84 -39
  1135. package/src/runtime/routes/wipe-conversation-routes.ts +89 -0
  1136. package/src/runtime/routes/work-items-routes.test.ts +10 -20
  1137. package/src/runtime/routes/work-items-routes.ts +418 -433
  1138. package/src/runtime/routes/workspace-commit-routes.ts +30 -61
  1139. package/src/runtime/routes/workspace-routes.test.ts +254 -381
  1140. package/src/runtime/routes/workspace-routes.ts +238 -246
  1141. package/src/runtime/runtime-mode.ts +8 -1
  1142. package/src/runtime/services/__tests__/analyze-conversation.test.ts +80 -118
  1143. package/src/runtime/services/analyze-conversation.ts +14 -41
  1144. package/src/runtime/services/conversation-serializer.ts +181 -0
  1145. package/src/runtime/trust-context-resolver.ts +3 -2
  1146. package/src/runtime/verification-outbound-actions.ts +13 -49
  1147. package/src/schedule/schedule-store.ts +64 -2
  1148. package/src/schedule/scheduler.ts +101 -0
  1149. package/src/security/ces-credential-client.ts +32 -169
  1150. package/src/security/ces-rpc-credential-backend.ts +1 -1
  1151. package/src/security/credential-backend.ts +6 -6
  1152. package/src/security/oauth-completion-page.ts +1 -1
  1153. package/src/security/oauth2.ts +3 -6
  1154. package/src/sequence/analytics.ts +1 -1
  1155. package/src/sequence/guardrails.ts +3 -3
  1156. package/src/sequence/store.ts +2 -1
  1157. package/src/signals/bash.ts +1 -1
  1158. package/src/signals/event-stream.ts +1 -1
  1159. package/src/skills/catalog-cache.ts +7 -0
  1160. package/src/skills/catalog-files.ts +0 -5
  1161. package/src/skills/catalog-install.ts +28 -18
  1162. package/src/skills/category-inference.ts +0 -11
  1163. package/src/skills/clawhub.ts +2 -2
  1164. package/src/skills/managed-store.ts +2 -2
  1165. package/src/skills/remote-skill-policy.ts +6 -7
  1166. package/src/subagent/index.ts +2 -6
  1167. package/src/subagent/manager.ts +27 -23
  1168. package/src/subagent/types.ts +9 -0
  1169. package/src/tasks/SPEC.md +2 -2
  1170. package/src/tasks/task-compiler.ts +1 -1
  1171. package/src/tasks/task-runner.ts +2 -22
  1172. package/src/tasks/task-store.ts +1 -1
  1173. package/src/tools/acp/list-agents.test.ts +115 -0
  1174. package/src/tools/acp/list-agents.ts +31 -0
  1175. package/src/tools/acp/spawn.test.ts +379 -0
  1176. package/src/tools/acp/spawn.ts +142 -62
  1177. package/src/tools/acp/steer.test.ts +101 -0
  1178. package/src/tools/acp/steer.ts +38 -0
  1179. package/src/tools/background-tool-registry.ts +98 -0
  1180. package/src/tools/browser/browser-execution.ts +34 -7
  1181. package/src/tools/browser/browser-manager.ts +1 -8
  1182. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +1 -1
  1183. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +3 -1
  1184. package/src/tools/browser/cdp-client/types.ts +4 -1
  1185. package/src/tools/computer-use/definitions.ts +1 -1
  1186. package/src/tools/credential-execution/make-authenticated-request.ts +2 -2
  1187. package/src/tools/credential-execution/manage-secure-command-tool.ts +1 -1
  1188. package/src/tools/credential-execution/run-authenticated-command.ts +2 -2
  1189. package/src/tools/credentials/broker-types.ts +2 -1
  1190. package/src/tools/document/editor-template.ts +1 -1
  1191. package/src/tools/execution-timeout.ts +1 -1
  1192. package/src/tools/executor.ts +10 -15
  1193. package/src/tools/host-filesystem/transfer.test.ts +268 -0
  1194. package/src/tools/host-filesystem/transfer.ts +234 -0
  1195. package/src/tools/host-terminal/host-shell.ts +189 -11
  1196. package/src/tools/mcp/mcp-tool-factory.ts +1 -1
  1197. package/src/tools/memory/register.test.ts +161 -1
  1198. package/src/tools/memory/register.ts +19 -34
  1199. package/src/tools/permission-checker.ts +18 -219
  1200. package/src/tools/policy-context.ts +1 -8
  1201. package/src/tools/registry.ts +16 -1
  1202. package/src/tools/secret-detection-handler.ts +13 -103
  1203. package/src/tools/shared/shell-output.ts +4 -1
  1204. package/src/tools/side-effects.ts +2 -2
  1205. package/src/tools/skills/execute.ts +1 -1
  1206. package/src/tools/subagent/spawn.ts +35 -11
  1207. package/src/tools/terminal/safe-env.ts +9 -1
  1208. package/src/tools/terminal/shell.ts +161 -31
  1209. package/src/tools/tool-approval-handler.ts +4 -70
  1210. package/src/tools/tool-input-summary.ts +10 -0
  1211. package/src/tools/types.ts +143 -163
  1212. package/src/tools/ui-surface/definitions.ts +2 -2
  1213. package/src/util/debounce.ts +0 -21
  1214. package/src/util/errors.ts +0 -8
  1215. package/src/util/log-redact.ts +0 -1
  1216. package/src/util/platform.ts +85 -124
  1217. package/src/util/pricing.ts +109 -6
  1218. package/src/watcher/engine.ts +42 -20
  1219. package/src/watcher/watcher-store.ts +2 -1
  1220. package/src/work-items/work-item-store.ts +1 -1
  1221. package/src/workspace/git-service.ts +1 -6
  1222. package/src/workspace/migrations/006-services-config.ts +10 -1
  1223. package/src/workspace/migrations/017-seed-persona-dirs.ts +1 -1
  1224. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +1 -1
  1225. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +1 -1
  1226. package/src/workspace/migrations/031-drop-user-md.ts +1 -1
  1227. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +3 -4
  1228. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +150 -0
  1229. package/src/workspace/migrations/053-release-notes-acp-codex.ts +107 -0
  1230. package/src/workspace/migrations/054-seed-recall-callsite.ts +102 -0
  1231. package/src/workspace/migrations/055-release-notes-agentic-recall.ts +63 -0
  1232. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +65 -0
  1233. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +98 -0
  1234. package/src/workspace/migrations/058-release-notes-acp-sessions-ui.ts +71 -0
  1235. package/src/workspace/migrations/059-move-pid-to-workspace.ts +53 -0
  1236. package/src/workspace/migrations/060-memory-v2-init.ts +53 -0
  1237. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +1 -1
  1238. package/src/workspace/migrations/registry.ts +18 -0
  1239. package/src/workspace/migrations/runner.ts +2 -2
  1240. package/src/workspace/provider-commit-message-generator.ts +1 -1
  1241. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +0 -471
  1242. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +0 -436
  1243. package/src/__tests__/cli-command-risk-guard.test.ts +0 -368
  1244. package/src/__tests__/config-watcher-feature-flags.test.ts +0 -211
  1245. package/src/__tests__/conversation-approval-overrides.test.ts +0 -207
  1246. package/src/__tests__/conversation-host-access-routes.test.ts +0 -229
  1247. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +0 -226
  1248. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +0 -167
  1249. package/src/__tests__/ephemeral-permissions.test.ts +0 -474
  1250. package/src/__tests__/extension-id-sync-guard.test.ts +0 -241
  1251. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +0 -374
  1252. package/src/__tests__/native-host-marker-sync-guard.test.ts +0 -157
  1253. package/src/__tests__/pairing-concurrent.test.ts +0 -84
  1254. package/src/__tests__/pairing-routes.test.ts +0 -181
  1255. package/src/__tests__/parser.test.ts +0 -595
  1256. package/src/__tests__/permission-checker-host-gate.test.ts +0 -488
  1257. package/src/__tests__/permission-controls-v2-flag.test.ts +0 -55
  1258. package/src/__tests__/permission-mode.test.ts +0 -89
  1259. package/src/__tests__/provider-env-vars-scope.test.ts +0 -52
  1260. package/src/__tests__/risk-classifier-parity.test.ts +0 -230
  1261. package/src/__tests__/shell-identity.test.ts +0 -236
  1262. package/src/__tests__/shell-parser-fuzz.test.ts +0 -629
  1263. package/src/__tests__/shell-parser-property.test.ts +0 -936
  1264. package/src/__tests__/starter-bundle.test.ts +0 -173
  1265. package/src/__tests__/stt-catalog-parity.test.ts +0 -282
  1266. package/src/__tests__/task-runner.test.ts +0 -224
  1267. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -354
  1268. package/src/__tests__/trust-store-pattern-matches.test.ts +0 -29
  1269. package/src/__tests__/trust-store.test.ts +0 -2013
  1270. package/src/__tests__/v2-consent-policy.test.ts +0 -103
  1271. package/src/browser/identifiers.ts +0 -51
  1272. package/src/cli/db.ts +0 -1
  1273. package/src/config/bundled-skills/settings/tools/avatar-get.ts +0 -40
  1274. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +0 -64
  1275. package/src/config/bundled-skills/settings/tools/avatar-update.ts +0 -88
  1276. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +0 -127
  1277. package/src/daemon/approved-devices-store.ts +0 -110
  1278. package/src/daemon/external-skills-bootstrap.ts +0 -41
  1279. package/src/daemon/message-types/trust.ts +0 -71
  1280. package/src/daemon/pairing-store.ts +0 -229
  1281. package/src/ipc/cli-server.ts +0 -252
  1282. package/src/ipc/routes/attachment.ts +0 -114
  1283. package/src/ipc/routes/browser-context.ts +0 -63
  1284. package/src/ipc/routes/browser.ts +0 -97
  1285. package/src/ipc/routes/cache.ts +0 -96
  1286. package/src/ipc/routes/get-contact.ts +0 -16
  1287. package/src/ipc/routes/index.ts +0 -35
  1288. package/src/ipc/routes/list-clients.ts +0 -31
  1289. package/src/ipc/routes/merge-contacts.ts +0 -17
  1290. package/src/ipc/routes/notification.ts +0 -133
  1291. package/src/ipc/routes/rename-conversation.ts +0 -59
  1292. package/src/ipc/routes/search-contacts.ts +0 -19
  1293. package/src/ipc/routes/task-queue.ts +0 -226
  1294. package/src/ipc/routes/task.ts +0 -173
  1295. package/src/ipc/routes/upsert-contact.ts +0 -25
  1296. package/src/ipc/routes/wake-conversation.ts +0 -19
  1297. package/src/memory/db.ts +0 -23
  1298. package/src/permissions/arg-parser.test.ts +0 -161
  1299. package/src/permissions/arg-parser.ts +0 -141
  1300. package/src/permissions/bash-risk-classifier.test.ts +0 -1620
  1301. package/src/permissions/bash-risk-classifier.ts +0 -950
  1302. package/src/permissions/command-registry.test.ts +0 -774
  1303. package/src/permissions/command-registry.ts +0 -1005
  1304. package/src/permissions/defaults.ts +0 -314
  1305. package/src/permissions/file-risk-classifier.test.ts +0 -535
  1306. package/src/permissions/file-risk-classifier.ts +0 -274
  1307. package/src/permissions/permission-mode.ts +0 -24
  1308. package/src/permissions/schedule-risk-classifier.test.ts +0 -129
  1309. package/src/permissions/schedule-risk-classifier.ts +0 -85
  1310. package/src/permissions/shell-identity.ts +0 -297
  1311. package/src/permissions/skill-risk-classifier.test.ts +0 -311
  1312. package/src/permissions/skill-risk-classifier.ts +0 -214
  1313. package/src/permissions/trust-client.ts +0 -359
  1314. package/src/permissions/trust-store-interface.ts +0 -100
  1315. package/src/permissions/trust-store.ts +0 -1330
  1316. package/src/permissions/v2-consent-policy.ts +0 -87
  1317. package/src/permissions/web-risk-classifier.test.ts +0 -170
  1318. package/src/permissions/web-risk-classifier.ts +0 -89
  1319. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +0 -715
  1320. package/src/runtime/__tests__/capability-tokens.test.ts +0 -258
  1321. package/src/runtime/actor-refresh-token-store.ts +0 -156
  1322. package/src/runtime/actor-token-store.ts +0 -207
  1323. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -264
  1324. package/src/runtime/auth/credential-service.ts +0 -352
  1325. package/src/runtime/conversation-approval-overrides.ts +0 -86
  1326. package/src/runtime/routes/browser-extension-pair-routes.ts +0 -575
  1327. package/src/runtime/routes/channel-routes.ts +0 -112
  1328. package/src/runtime/routes/contact-routes.test.ts +0 -298
  1329. package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -175
  1330. package/src/runtime/routes/guardian-refresh-routes.ts +0 -79
  1331. package/src/runtime/routes/invite-routes.ts +0 -280
  1332. package/src/runtime/routes/pairing-routes.ts +0 -431
  1333. package/src/runtime/routes/playground/deps.ts +0 -56
  1334. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +0 -67
  1335. package/src/runtime/services/analyze-deps-singleton.ts +0 -32
  1336. package/src/tasks/ephemeral-permissions.ts +0 -55
  1337. package/src/tools/terminal/parser.ts +0 -623
  1338. package/src/types/qrcode.d.ts +0 -13
  1339. package/src/util/network-info.ts +0 -55
  1340. /package/node_modules/@vellumai/{ces-contracts → ces-client}/tsconfig.json +0 -0
  1341. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/grants.test.ts +0 -0
  1342. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/error.ts +0 -0
  1343. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/grants.ts +0 -0
  1344. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/handles.ts +0 -0
  1345. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rendering.ts +0 -0
  1346. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rpc.ts +0 -0
@@ -0,0 +1,1348 @@
1
+ /**
2
+ * `SkillHostClient` — IPC-backed concretion of the neutral `SkillHost`
3
+ * interface. Lets an out-of-process first-party skill consume the daemon's
4
+ * host surface over the Unix domain socket exposed by `SkillIpcServer`.
5
+ *
6
+ * Wire protocol (mirrors `assistant/src/ipc/skill-server.ts`):
7
+ *
8
+ * skill-initiated RPC (one-shot)
9
+ * → { id: "s:<n>", method, params? }
10
+ * ← { id: "s:<n>", result } | { id: "s:<n>", error }
11
+ *
12
+ * daemon-initiated RPC (one-shot, requires registered handler)
13
+ * ← { id: "d:<n>", method, params? }
14
+ * → { id: "d:<n>", result } | { id: "d:<n>", error }
15
+ *
16
+ * streaming RPC (e.g. `host.events.subscribe`, skill-initiated only)
17
+ * → { id: "s:<n>", method, params? }
18
+ * ← { id: "s:<n>", result: { subscribed: true } } (open ack)
19
+ * ← { id: "s:<n>", event: "delivery", payload: <data> } (0..N)
20
+ * ← { id: "s:<n>", error } (terminal)
21
+ * → { id: "s:<n>", method: "host.events.subscribe.close",
22
+ * params: { subscribeId: <original-id> } }
23
+ * ← { id: "s:<n>", result: { closed: true } }
24
+ *
25
+ * The `s:` / `d:` id prefixes namespace the two directions so each side can
26
+ * route inbound responses to its own pending-request map without collision.
27
+ *
28
+ * ### Daemon-initiated dispatch handlers
29
+ *
30
+ * After the skill registers tools / routes / shutdown hooks via
31
+ * `registries.registerTools`, `registries.registerSkillRoute`, and
32
+ * `registries.registerShutdownHook`, the client also installs local
33
+ * handlers for the matching `skill.dispatch_*` methods so the daemon can
34
+ * invoke skill-side closures over the bidirectional RPC. The wire shapes
35
+ * are:
36
+ *
37
+ * skill.dispatch_tool
38
+ * daemon → skill: { name: string, input: Record<string, unknown>,
39
+ * context?: unknown }
40
+ * skill → daemon: { result: unknown }
41
+ * errors: throws "unknown tool: <name>" when the tool name is not in
42
+ * the most recently registered provider's output.
43
+ *
44
+ * skill.dispatch_route
45
+ * daemon → skill: { patternSource: string,
46
+ * request: { method: string, url: string,
47
+ * headers?: Record<string, string>,
48
+ * body?: string } }
49
+ * skill → daemon: { status: number,
50
+ * headers: Record<string, string>,
51
+ * body: string }
52
+ * errors: throws "unknown route: <patternSource>" when no registered
53
+ * route matches the patternSource, or "url did not match
54
+ * pattern: <patternSource>" when the regex fails to match.
55
+ *
56
+ * skill.shutdown
57
+ * daemon → skill: { name?: string, reason?: string }
58
+ * skill → daemon: { ok: true }
59
+ * semantics: when `name` is set, runs only that hook; otherwise runs
60
+ * all registered hooks in reverse-registration order. Per-
61
+ * hook errors are swallowed (logged via the host logger if
62
+ * `connect()` has populated one).
63
+ *
64
+ * ### Sync-method bootstrap
65
+ *
66
+ * The `SkillHost` contract exposes a number of synchronous accessors
67
+ * (`identity.internalAssistantId`, `platform.workspaceDir()`,
68
+ * `platform.runtimeMode()`, etc.) that naturally cannot round-trip an async
69
+ * IPC call on every invocation. `connect()` prefetches the stable subset of
70
+ * these values once, caches them locally, and every subsequent sync accessor
71
+ * reads from the cache. Skill code MUST await `connect()` before any
72
+ * synchronous host accessor fires; calling a sync accessor before connect
73
+ * throws a clear "not connected" error.
74
+ *
75
+ * ### Opaque handle methods
76
+ *
77
+ * Several provider accessors on `SkillHost` (`providers.llm.getConfigured`,
78
+ * `providers.llm.userMessage`, `providers.llm.extractToolUse`,
79
+ * `providers.stt.resolveStreamingTranscriber`, `providers.tts.get`,
80
+ * `speakers.createTracker`) return opaque handles whose concrete types live
81
+ * inside `assistant/`. Across IPC they cannot carry the handle's method
82
+ * closures — the skill treats the return value as a black-box token and
83
+ * threads it into `host.providers.llm.complete` / future dispatch routes.
84
+ * The client implements each as a passthrough that returns a tagged
85
+ * descriptor object; the daemon-side handler that ultimately consumes the
86
+ * token narrows it back to the concrete type at its boundary.
87
+ *
88
+ * ### Reconnect
89
+ *
90
+ * When `autoReconnect` is enabled, a lost socket connection is retried with
91
+ * exponential backoff (capped at `reconnectMaxDelayMs`). In-flight requests
92
+ * are rejected with a clear error because no response correlation survives
93
+ * a socket reset; callers are responsible for retrying at a higher level.
94
+ * Long-lived subscriptions are re-opened on reconnect with the same filter
95
+ * so skill-side callbacks keep firing once the socket is back.
96
+ */
97
+
98
+ import { randomUUID } from "node:crypto";
99
+ import { connect, type Socket } from "node:net";
100
+
101
+ import type { AssistantEvent } from "./assistant-event.js";
102
+ import type { DaemonRuntimeMode } from "./runtime-mode.js";
103
+ import type { ServerMessage } from "./server-message.js";
104
+ import type {
105
+ AssistantEventCallback,
106
+ ConfigFacet,
107
+ EventsFacet,
108
+ Filter,
109
+ IdentityFacet,
110
+ InsertMessageFn,
111
+ LlmProvidersFacet,
112
+ Logger,
113
+ LoggerFacet,
114
+ MemoryFacet,
115
+ PlatformFacet,
116
+ Provider,
117
+ ProvidersFacet,
118
+ RegistriesFacet,
119
+ SecureKeysFacet,
120
+ SkillHost,
121
+ SkillRoute,
122
+ SkillRouteHandle,
123
+ SpeakersFacet,
124
+ SttProvidersFacet,
125
+ Subscription,
126
+ ToolUse,
127
+ TtsConfig,
128
+ TtsProvider,
129
+ TtsProvidersFacet,
130
+ UserMessage,
131
+ } from "./skill-host.js";
132
+ import type { Tool, ToolContext } from "./tool-types.js";
133
+
134
+ // ---------------------------------------------------------------------------
135
+ // Constants
136
+ // ---------------------------------------------------------------------------
137
+
138
+ const SUBSCRIBE_CLOSE_METHOD = "host.events.subscribe.close" as const;
139
+ const DEFAULT_CALL_TIMEOUT_MS = 30_000;
140
+ const DEFAULT_CONNECT_TIMEOUT_MS = 3_000;
141
+ const DEFAULT_RECONNECT_BASE_DELAY_MS = 200;
142
+ const DEFAULT_RECONNECT_MAX_DELAY_MS = 10_000;
143
+
144
+ /** Prefix for ids minted by the daemon (server) side. */
145
+ const DAEMON_ID_PREFIX = "d:" as const;
146
+ /** Prefix for ids minted by the skill (client) side. */
147
+ const SKILL_ID_PREFIX = "s:" as const;
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Wire-format types
151
+ // ---------------------------------------------------------------------------
152
+
153
+ type IpcRequest = {
154
+ id: string;
155
+ method: string;
156
+ params?: Record<string, unknown>;
157
+ };
158
+
159
+ type IpcResponseFrame = {
160
+ id: string;
161
+ method?: string;
162
+ params?: unknown;
163
+ result?: unknown;
164
+ error?: string;
165
+ event?: "delivery";
166
+ payload?: unknown;
167
+ };
168
+
169
+ /**
170
+ * Handler for a daemon-initiated request. Returning a value (or a Promise)
171
+ * resolves the daemon's `sendRequest` call; throwing rejects it with the
172
+ * thrown error's message. Synchronous returns are wrapped automatically.
173
+ */
174
+ export type SkillHostRequestHandler = (
175
+ params: unknown,
176
+ ) => unknown | Promise<unknown>;
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Public options
180
+ // ---------------------------------------------------------------------------
181
+
182
+ export interface SkillHostClientOptions {
183
+ /** Absolute path to the `assistant-skill.sock` Unix domain socket. */
184
+ socketPath: string;
185
+ /**
186
+ * Identifier for the owning skill. Sent as the default logger-scope name
187
+ * when `logger.get(name)` is not explicitly scoped, and reserved for
188
+ * future per-skill routing at the daemon boundary.
189
+ */
190
+ skillId: string;
191
+ /**
192
+ * Automatically reconnect the underlying socket when it drops. Existing
193
+ * subscriptions are reopened with the same filter; in-flight one-shot
194
+ * requests are rejected with a "connection lost" error.
195
+ *
196
+ * @default false
197
+ */
198
+ autoReconnect?: boolean;
199
+ /** Initial retry delay (ms). Exponentially backs off to the max. */
200
+ reconnectBaseDelayMs?: number;
201
+ /** Maximum retry delay (ms). */
202
+ reconnectMaxDelayMs?: number;
203
+ /** Per-call timeout for one-shot RPCs. */
204
+ callTimeoutMs?: number;
205
+ /** Socket `connect()` timeout. */
206
+ connectTimeoutMs?: number;
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // Internal state for pending calls and subscriptions
211
+ // ---------------------------------------------------------------------------
212
+
213
+ interface PendingCall {
214
+ resolve: (value: unknown) => void;
215
+ reject: (err: Error) => void;
216
+ timer: ReturnType<typeof setTimeout>;
217
+ }
218
+
219
+ interface ActiveSubscription {
220
+ id: string;
221
+ filter: Filter;
222
+ callback: AssistantEventCallback;
223
+ disposed: boolean;
224
+ }
225
+
226
+ // ---------------------------------------------------------------------------
227
+ // Helpers
228
+ // ---------------------------------------------------------------------------
229
+
230
+ function notConnected(): Error {
231
+ return new Error(
232
+ "SkillHostClient: not connected. Call `await client.connect()` before using synchronous host accessors.",
233
+ );
234
+ }
235
+
236
+ /**
237
+ * Wraps a sync logger call so a host.log RPC failure never throws at the
238
+ * call site — skills treat logging as side-effectful and don't want a
239
+ * transient socket issue to abort whatever they were doing.
240
+ */
241
+ function swallow(err: unknown): void {
242
+ // Intentional no-op; logging here would recurse into the same broken
243
+ // logger. The stderr path is a deliberate last-resort sink.
244
+ if (err && process.env.SKILL_HOST_CLIENT_DEBUG) {
245
+ // eslint-disable-next-line no-console
246
+ console.error("[SkillHostClient] log RPC failed:", err);
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Stringify an unknown error value for the wire — `Error.message` when
252
+ * available, otherwise `String(err)` so non-Error throws still surface
253
+ * something readable on the daemon side.
254
+ */
255
+ function errorMessage(err: unknown): string {
256
+ if (err instanceof Error) return err.message;
257
+ return String(err);
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // Client implementation
262
+ // ---------------------------------------------------------------------------
263
+
264
+ export class SkillHostClient implements SkillHost {
265
+ // Facets are populated in the constructor so every `SkillHost` method
266
+ // has a concrete target even before `connect()` resolves. Sync methods
267
+ // that depend on prefetched state throw `notConnected()` until then.
268
+ readonly logger: LoggerFacet;
269
+ readonly config: ConfigFacet;
270
+ readonly identity: IdentityFacet;
271
+ readonly platform: PlatformFacet;
272
+ readonly providers: ProvidersFacet;
273
+ readonly memory: MemoryFacet;
274
+ readonly events: EventsFacet;
275
+ readonly registries: RegistriesFacet;
276
+ readonly speakers: SpeakersFacet;
277
+
278
+ private readonly options: Required<
279
+ Pick<
280
+ SkillHostClientOptions,
281
+ | "socketPath"
282
+ | "skillId"
283
+ | "callTimeoutMs"
284
+ | "connectTimeoutMs"
285
+ | "reconnectBaseDelayMs"
286
+ | "reconnectMaxDelayMs"
287
+ >
288
+ > & { autoReconnect: boolean };
289
+
290
+ private socket: Socket | null = null;
291
+ private buffer = "";
292
+ private readonly pending = new Map<string, PendingCall>();
293
+ private readonly subscriptions = new Map<string, ActiveSubscription>();
294
+ private connectingPromise: Promise<void> | null = null;
295
+ private closed = false;
296
+ private reconnectAttempt = 0;
297
+ /**
298
+ * Monotonic counter for skill-initiated request ids, formatted as
299
+ * `s:<n>`. The daemon mints `d:<n>` ids independently, so the two
300
+ * sequences never collide on a shared socket.
301
+ */
302
+ private nextSkillRequestSeq = 1;
303
+ /**
304
+ * Handlers for daemon-initiated requests, keyed by method name. Populated
305
+ * via `registerHandler(method, handler)`. Daemon→skill request frames
306
+ * (id starts with `d:`) are dispatched through this table.
307
+ */
308
+ private readonly daemonRequestHandlers = new Map<
309
+ string,
310
+ SkillHostRequestHandler
311
+ >();
312
+
313
+ // Prefetched sync state — populated by `connect()`.
314
+ private cachedInternalAssistantId: string | null = null;
315
+ private cachedAssistantName: string | undefined = undefined;
316
+ private cachedWorkspaceDir: string | null = null;
317
+ private cachedVellumRoot: string | null = null;
318
+ private cachedRuntimeMode: DaemonRuntimeMode | null = null;
319
+
320
+ // ── Local dispatch state ────────────────────────────────────────────────
321
+ // Caches populated by `registries.register*` so the daemon can dispatch
322
+ // skill-owned closures back over the bidirectional RPC. Mirrors what the
323
+ // out-of-process skill installed in-process before the IPC split.
324
+
325
+ /** Most recently registered tool provider (last writer wins, matching the in-process semantics where a single skill module owns one provider). */
326
+ private cachedToolsProvider: (() => Tool[]) | null = null;
327
+ /**
328
+ * Routes keyed by `pattern.source`. Last writer wins on collision so
329
+ * re-registering the same regex source replaces the prior handler — mirrors
330
+ * how an in-process skill would re-`registerSkillRoute` on hot reload.
331
+ */
332
+ private readonly cachedRoutes = new Map<string, SkillRoute>();
333
+ /**
334
+ * Shutdown hooks ordered by registration time. We use an array (not a Map
335
+ * keyed by name) because the spec requires running hooks in reverse-
336
+ * registration order when no name is provided. Re-registering an existing
337
+ * name replaces the prior entry in place to keep the cache idempotent.
338
+ */
339
+ private readonly cachedShutdownHooks: Array<{
340
+ name: string;
341
+ hook: (reason: string) => Promise<void>;
342
+ }> = [];
343
+
344
+ constructor(options: SkillHostClientOptions) {
345
+ this.options = {
346
+ socketPath: options.socketPath,
347
+ skillId: options.skillId,
348
+ autoReconnect: options.autoReconnect ?? false,
349
+ callTimeoutMs: options.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS,
350
+ connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
351
+ reconnectBaseDelayMs:
352
+ options.reconnectBaseDelayMs ?? DEFAULT_RECONNECT_BASE_DELAY_MS,
353
+ reconnectMaxDelayMs:
354
+ options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS,
355
+ };
356
+
357
+ this.logger = this.buildLoggerFacet();
358
+ this.config = this.buildConfigFacet();
359
+ this.identity = this.buildIdentityFacet();
360
+ this.platform = this.buildPlatformFacet();
361
+ this.providers = this.buildProvidersFacet();
362
+ this.memory = this.buildMemoryFacet();
363
+ this.events = this.buildEventsFacet();
364
+ this.registries = this.buildRegistriesFacet();
365
+ this.speakers = this.buildSpeakersFacet();
366
+ }
367
+
368
+ // ── Public lifecycle ────────────────────────────────────────────────────
369
+
370
+ /**
371
+ * Connect to the skill IPC socket and prefetch sync-cacheable state
372
+ * (assistant id, workspace dir, vellum root, runtime mode, assistant
373
+ * name). Safe to call multiple times — the first call initiates the
374
+ * connection, concurrent calls await the same promise.
375
+ */
376
+ async connect(): Promise<void> {
377
+ if (this.closed) {
378
+ throw new Error("SkillHostClient: cannot connect after close()");
379
+ }
380
+ if (this.connectingPromise) return this.connectingPromise;
381
+ // Fully connected: live socket *and* prefetch already populated.
382
+ // If the socket survived but a prior `prefetchSyncState()` rejected,
383
+ // the caches are still null and sync accessors would throw — fall
384
+ // through and re-run prefetch over the existing socket.
385
+ const socketAlive = !!this.socket && !this.socket.destroyed;
386
+ const prefetchDone = this.cachedInternalAssistantId !== null;
387
+ if (socketAlive && prefetchDone) return;
388
+
389
+ const ensureSocket = socketAlive ? Promise.resolve() : this.doConnect();
390
+ this.connectingPromise = ensureSocket
391
+ .then(async () => {
392
+ await this.prefetchSyncState();
393
+ })
394
+ .finally(() => {
395
+ this.connectingPromise = null;
396
+ });
397
+ return this.connectingPromise;
398
+ }
399
+
400
+ /**
401
+ * Close the socket, reject outstanding calls, and dispose all active
402
+ * subscriptions. Safe to call multiple times.
403
+ */
404
+ close(): void {
405
+ if (this.closed) return;
406
+ this.closed = true;
407
+ // Mark every subscription disposed so stray deliveries during teardown
408
+ // don't fire user callbacks.
409
+ for (const sub of this.subscriptions.values()) {
410
+ sub.disposed = true;
411
+ }
412
+ this.subscriptions.clear();
413
+ // Reject any in-flight calls.
414
+ const closeErr = new Error(
415
+ "SkillHostClient: client closed while request was in flight",
416
+ );
417
+ for (const { reject, timer } of this.pending.values()) {
418
+ clearTimeout(timer);
419
+ reject(closeErr);
420
+ }
421
+ this.pending.clear();
422
+ if (this.socket && !this.socket.destroyed) {
423
+ this.socket.destroy();
424
+ }
425
+ this.socket = null;
426
+ }
427
+
428
+ /**
429
+ * Install a handler for daemon-initiated requests of the given method.
430
+ * The daemon's `SkillIpcServer.sendRequest(connection, method, ...)`
431
+ * resolves with whatever the handler returns (or rejects with the
432
+ * handler's thrown error). Re-registering a method replaces the prior
433
+ * handler — last writer wins.
434
+ */
435
+ registerHandler(method: string, handler: SkillHostRequestHandler): void {
436
+ this.daemonRequestHandlers.set(method, handler);
437
+ }
438
+
439
+ // ── Internal: socket lifecycle ──────────────────────────────────────────
440
+
441
+ private async doConnect(): Promise<void> {
442
+ const { socketPath, connectTimeoutMs } = this.options;
443
+ return new Promise<void>((resolve, reject) => {
444
+ const socket = connect(socketPath);
445
+ let settled = false;
446
+
447
+ const timer = setTimeout(() => {
448
+ if (settled) return;
449
+ settled = true;
450
+ socket.destroy();
451
+ reject(
452
+ new Error(
453
+ `SkillHostClient: connect timed out after ${connectTimeoutMs}ms (${socketPath})`,
454
+ ),
455
+ );
456
+ }, connectTimeoutMs);
457
+
458
+ socket.once("connect", () => {
459
+ if (settled) return;
460
+ settled = true;
461
+ clearTimeout(timer);
462
+ // The client may have been `close()`d while `connect()` was
463
+ // still pending. If we attach now we'd reintroduce a live
464
+ // socket on a closed client and leak the server-side connection
465
+ // until process teardown.
466
+ if (this.closed) {
467
+ socket.destroy();
468
+ reject(new Error("SkillHostClient: closed during connect"));
469
+ return;
470
+ }
471
+ this.attachSocket(socket);
472
+ resolve();
473
+ });
474
+
475
+ socket.once("error", (err) => {
476
+ if (settled) return;
477
+ settled = true;
478
+ clearTimeout(timer);
479
+ reject(
480
+ new Error(
481
+ `SkillHostClient: socket error during connect: ${err.message}`,
482
+ ),
483
+ );
484
+ });
485
+ });
486
+ }
487
+
488
+ private attachSocket(socket: Socket): void {
489
+ this.socket = socket;
490
+ this.buffer = "";
491
+ this.reconnectAttempt = 0;
492
+
493
+ socket.on("data", (chunk) => {
494
+ this.buffer += chunk.toString();
495
+ let newlineIdx: number;
496
+ while ((newlineIdx = this.buffer.indexOf("\n")) !== -1) {
497
+ const line = this.buffer.slice(0, newlineIdx).trim();
498
+ this.buffer = this.buffer.slice(newlineIdx + 1);
499
+ if (line) this.handleFrame(line);
500
+ }
501
+ });
502
+
503
+ socket.on("close", () => {
504
+ this.socket = null;
505
+ const err = new Error(
506
+ "SkillHostClient: socket closed before response",
507
+ );
508
+ for (const { reject, timer } of this.pending.values()) {
509
+ clearTimeout(timer);
510
+ reject(err);
511
+ }
512
+ this.pending.clear();
513
+ if (!this.closed && this.options.autoReconnect) {
514
+ void this.scheduleReconnect();
515
+ }
516
+ });
517
+
518
+ socket.on("error", (err) => {
519
+ swallow(err);
520
+ });
521
+ }
522
+
523
+ private async scheduleReconnect(): Promise<void> {
524
+ if (this.closed) return;
525
+ this.reconnectAttempt += 1;
526
+ const delay = Math.min(
527
+ this.options.reconnectBaseDelayMs *
528
+ Math.pow(2, this.reconnectAttempt - 1),
529
+ this.options.reconnectMaxDelayMs,
530
+ );
531
+ await new Promise((resolve) => setTimeout(resolve, delay));
532
+ if (this.closed) return;
533
+ try {
534
+ await this.doConnect();
535
+ // Re-open every live subscription with a fresh request so the
536
+ // server-side hub installs a new callback.
537
+ const live = [...this.subscriptions.values()].filter((s) => !s.disposed);
538
+ this.subscriptions.clear();
539
+ for (const sub of live) {
540
+ this.reopenSubscription(sub);
541
+ }
542
+ } catch (err) {
543
+ swallow(err);
544
+ if (!this.closed) {
545
+ void this.scheduleReconnect();
546
+ }
547
+ }
548
+ }
549
+
550
+ private reopenSubscription(prev: ActiveSubscription): void {
551
+ // Same id so the application-visible Subscription handle still works.
552
+ const fresh: ActiveSubscription = {
553
+ id: prev.id,
554
+ filter: prev.filter,
555
+ callback: prev.callback,
556
+ disposed: false,
557
+ };
558
+ this.subscriptions.set(fresh.id, fresh);
559
+ // Mirror `openSubscription`: pre-register a pending entry so the
560
+ // server's `{ id, result: { subscribed: true } }` ack frame is
561
+ // matched (otherwise `handleFrame` silently drops it) and so we
562
+ // tear the subscription down on ack timeout instead of leaking it.
563
+ this.registerSubscribeAck(fresh);
564
+ try {
565
+ this.writeFrame({
566
+ id: fresh.id,
567
+ method: "host.events.subscribe",
568
+ params: { filter: fresh.filter },
569
+ });
570
+ } catch (err) {
571
+ this.cancelSubscribeAck(fresh);
572
+ swallow(err);
573
+ }
574
+ }
575
+
576
+ private registerSubscribeAck(active: ActiveSubscription): void {
577
+ const ackTimer = setTimeout(() => {
578
+ if (this.pending.delete(active.id)) {
579
+ active.disposed = true;
580
+ this.subscriptions.delete(active.id);
581
+ }
582
+ }, this.options.callTimeoutMs);
583
+ this.pending.set(active.id, {
584
+ resolve: () => {
585
+ clearTimeout(ackTimer);
586
+ },
587
+ reject: (err) => {
588
+ clearTimeout(ackTimer);
589
+ active.disposed = true;
590
+ this.subscriptions.delete(active.id);
591
+ swallow(err);
592
+ },
593
+ timer: ackTimer,
594
+ });
595
+ }
596
+
597
+ private cancelSubscribeAck(active: ActiveSubscription): void {
598
+ const entry = this.pending.get(active.id);
599
+ if (entry) {
600
+ clearTimeout(entry.timer);
601
+ this.pending.delete(active.id);
602
+ }
603
+ active.disposed = true;
604
+ this.subscriptions.delete(active.id);
605
+ }
606
+
607
+ // ── Internal: frame I/O ─────────────────────────────────────────────────
608
+
609
+ private handleFrame(line: string): void {
610
+ let frame: IpcResponseFrame;
611
+ try {
612
+ frame = JSON.parse(line) as IpcResponseFrame;
613
+ } catch (err) {
614
+ swallow(err);
615
+ return;
616
+ }
617
+
618
+ if (
619
+ !frame ||
620
+ typeof frame !== "object" ||
621
+ Array.isArray(frame) ||
622
+ typeof frame.id !== "string"
623
+ ) {
624
+ return;
625
+ }
626
+
627
+ // Delivery frames route into the subscription callback.
628
+ if (frame.event === "delivery") {
629
+ const sub = this.subscriptions.get(frame.id);
630
+ if (sub && !sub.disposed) {
631
+ try {
632
+ const r = sub.callback(frame.payload as AssistantEvent);
633
+ if (r instanceof Promise) r.catch(swallow);
634
+ } catch (err) {
635
+ swallow(err);
636
+ }
637
+ }
638
+ return;
639
+ }
640
+
641
+ // Daemon-initiated request frame — dispatch to a registered handler
642
+ // and write back the response. Identified by the `d:` id prefix and
643
+ // the presence of a `method` field.
644
+ if (
645
+ typeof frame.method === "string" &&
646
+ frame.id.startsWith(DAEMON_ID_PREFIX)
647
+ ) {
648
+ this.dispatchDaemonRequest(frame.id, frame.method, frame.params);
649
+ return;
650
+ }
651
+
652
+ // Response frame — resolve or reject the pending call.
653
+ const pending = this.pending.get(frame.id);
654
+ if (pending) {
655
+ this.pending.delete(frame.id);
656
+ clearTimeout(pending.timer);
657
+ if (frame.error !== undefined) {
658
+ pending.reject(
659
+ new Error(`SkillHostClient: remote error: ${frame.error}`),
660
+ );
661
+ } else {
662
+ pending.resolve(frame.result);
663
+ }
664
+ return;
665
+ }
666
+
667
+ // No pending entry — could be a terminal error on a subscription.
668
+ if (frame.error !== undefined) {
669
+ const sub = this.subscriptions.get(frame.id);
670
+ if (sub) {
671
+ sub.disposed = true;
672
+ this.subscriptions.delete(frame.id);
673
+ }
674
+ }
675
+ }
676
+
677
+ private dispatchDaemonRequest(
678
+ id: string,
679
+ method: string,
680
+ params: unknown,
681
+ ): void {
682
+ const handler = this.daemonRequestHandlers.get(method);
683
+ if (!handler) {
684
+ this.writeResponseFrame({
685
+ id,
686
+ error: `method not found: ${method}`,
687
+ });
688
+ return;
689
+ }
690
+ let result: unknown;
691
+ try {
692
+ result = handler(params);
693
+ } catch (err) {
694
+ this.writeResponseFrame({ id, error: errorMessage(err) });
695
+ return;
696
+ }
697
+ if (result instanceof Promise) {
698
+ result.then(
699
+ (value) => {
700
+ this.writeResponseFrame({ id, result: value });
701
+ },
702
+ (err) => {
703
+ this.writeResponseFrame({ id, error: errorMessage(err) });
704
+ },
705
+ );
706
+ } else {
707
+ this.writeResponseFrame({ id, result });
708
+ }
709
+ }
710
+
711
+ private writeResponseFrame(response: {
712
+ id: string;
713
+ result?: unknown;
714
+ error?: string;
715
+ }): void {
716
+ if (!this.socket || this.socket.destroyed) {
717
+ // The peer is gone; silently drop. The daemon-side pending entry
718
+ // will already have been rejected by the connection-close path.
719
+ return;
720
+ }
721
+ this.socket.write(JSON.stringify(response) + "\n");
722
+ }
723
+
724
+ private writeFrame(req: IpcRequest): void {
725
+ if (!this.socket || this.socket.destroyed) {
726
+ throw new Error("SkillHostClient: not connected");
727
+ }
728
+ this.socket.write(JSON.stringify(req) + "\n");
729
+ }
730
+
731
+ /** Allocate the next `s:<n>` id for a skill-initiated request. */
732
+ private nextSkillRequestId(): string {
733
+ return `${SKILL_ID_PREFIX}${this.nextSkillRequestSeq++}`;
734
+ }
735
+
736
+ private async call<T>(
737
+ method: string,
738
+ params?: Record<string, unknown>,
739
+ ): Promise<T> {
740
+ if (this.closed) {
741
+ throw new Error("SkillHostClient: client is closed");
742
+ }
743
+ if (!this.socket || this.socket.destroyed) {
744
+ throw new Error(
745
+ "SkillHostClient: not connected. Call `await client.connect()` first.",
746
+ );
747
+ }
748
+ const id = this.nextSkillRequestId();
749
+ return new Promise<T>((resolve, reject) => {
750
+ const timer = setTimeout(() => {
751
+ if (this.pending.delete(id)) {
752
+ reject(
753
+ new Error(
754
+ `SkillHostClient: call '${method}' timed out after ${this.options.callTimeoutMs}ms`,
755
+ ),
756
+ );
757
+ }
758
+ }, this.options.callTimeoutMs);
759
+ this.pending.set(id, {
760
+ resolve: (v) => resolve(v as T),
761
+ reject,
762
+ timer,
763
+ });
764
+ try {
765
+ this.writeFrame({ id, method, params });
766
+ } catch (err) {
767
+ this.pending.delete(id);
768
+ clearTimeout(timer);
769
+ reject(err as Error);
770
+ }
771
+ });
772
+ }
773
+
774
+ // ── Internal: bootstrap cache ───────────────────────────────────────────
775
+
776
+ private async prefetchSyncState(): Promise<void> {
777
+ const [assistantId, workspaceDir, vellumRootValue, runtimeMode, name] =
778
+ await Promise.all([
779
+ this.call<string>("host.identity.getInternalAssistantId"),
780
+ this.call<string>("host.platform.workspaceDir"),
781
+ this.call<string>("host.platform.vellumRoot"),
782
+ this.call<DaemonRuntimeMode>("host.platform.runtimeMode"),
783
+ this.call<string | null>("host.identity.getAssistantName"),
784
+ ]);
785
+ this.cachedInternalAssistantId = assistantId;
786
+ this.cachedWorkspaceDir = workspaceDir;
787
+ this.cachedVellumRoot = vellumRootValue;
788
+ this.cachedRuntimeMode = runtimeMode;
789
+ this.cachedAssistantName = name ?? undefined;
790
+ }
791
+
792
+ // ── Facet builders ──────────────────────────────────────────────────────
793
+
794
+ private buildLogger(name: string): Logger {
795
+ const scope = name || this.options.skillId;
796
+ const write = (
797
+ level: "debug" | "info" | "warn" | "error",
798
+ msg: string,
799
+ meta?: unknown,
800
+ ) => {
801
+ // Fire-and-forget: skills expect logging to be non-blocking and
802
+ // infallible. If the socket is down we just drop the line.
803
+ this.call("host.log", { level, msg, name: scope, meta }).catch(swallow);
804
+ };
805
+ return {
806
+ debug: (msg, meta) => write("debug", msg, meta),
807
+ info: (msg, meta) => write("info", msg, meta),
808
+ warn: (msg, meta) => write("warn", msg, meta),
809
+ error: (msg, meta) => write("error", msg, meta),
810
+ };
811
+ }
812
+
813
+ private buildLoggerFacet(): LoggerFacet {
814
+ return {
815
+ get: (name) => this.buildLogger(name),
816
+ };
817
+ }
818
+
819
+ private buildConfigFacet(): ConfigFacet {
820
+ return {
821
+ // `isFeatureFlagEnabled` and `getSection` are typed as sync on the
822
+ // contract but require a round-trip to resolve. We cannot block on
823
+ // async I/O inside a sync accessor, so the client surfaces the
824
+ // async semantics by returning a stale-safe value if one has been
825
+ // cached via `prefetchFlag` / `prefetchSection` helpers (future
826
+ // work) — for now, these throw a clear error so skill code that
827
+ // ever reaches them on the client path is audible instead of
828
+ // silently returning a wrong value. Async callers should use the
829
+ // underlying IPC method names directly via `rawCall`.
830
+ isFeatureFlagEnabled: (_key: string): boolean => {
831
+ throw new Error(
832
+ "SkillHostClient.config.isFeatureFlagEnabled: synchronous feature-flag reads are not supported over IPC. Use `client.rawCall('host.config.isFeatureFlagEnabled', { key })` and await the result.",
833
+ );
834
+ },
835
+ getSection: <T>(_path: string): T | undefined => {
836
+ throw new Error(
837
+ "SkillHostClient.config.getSection: synchronous config reads are not supported over IPC. Use `client.rawCall('host.config.getSection', { path })` and await the result.",
838
+ );
839
+ },
840
+ };
841
+ }
842
+
843
+ private buildIdentityFacet(): IdentityFacet {
844
+ const self = this;
845
+ return {
846
+ getAssistantName: () => {
847
+ if (self.cachedInternalAssistantId === null) throw notConnected();
848
+ return self.cachedAssistantName;
849
+ },
850
+ get internalAssistantId(): string {
851
+ if (self.cachedInternalAssistantId === null) throw notConnected();
852
+ return self.cachedInternalAssistantId;
853
+ },
854
+ };
855
+ }
856
+
857
+ private buildPlatformFacet(): PlatformFacet {
858
+ return {
859
+ workspaceDir: () => {
860
+ if (this.cachedWorkspaceDir === null) throw notConnected();
861
+ return this.cachedWorkspaceDir;
862
+ },
863
+ vellumRoot: () => {
864
+ if (this.cachedVellumRoot === null) throw notConnected();
865
+ return this.cachedVellumRoot;
866
+ },
867
+ runtimeMode: () => {
868
+ if (this.cachedRuntimeMode === null) throw notConnected();
869
+ return this.cachedRuntimeMode;
870
+ },
871
+ };
872
+ }
873
+
874
+ private buildLlmProvidersFacet(): LlmProvidersFacet {
875
+ // The provider, user-message, and tool-use values are opaque tokens on
876
+ // the contract; the client synthesizes structurally inert descriptors
877
+ // that round-trip through future dispatch routes.
878
+ return {
879
+ getConfigured: async (callSite: string): Promise<Provider | null> =>
880
+ ({
881
+ __vellumSkillHostClientHandle: "llm-provider",
882
+ callSite,
883
+ }) as unknown as Provider,
884
+ userMessage: (text: string): UserMessage =>
885
+ ({
886
+ __vellumSkillHostClientHandle: "user-message",
887
+ text,
888
+ }) as unknown as UserMessage,
889
+ extractToolUse: (_response: unknown): ToolUse | null => {
890
+ // The client cannot inspect daemon-shaped completion responses
891
+ // without pulling in the Anthropic SDK types; skills that need
892
+ // typed tool-use extraction should do it via the completion's
893
+ // `content` array directly. Return null as the conservative
894
+ // "no tool_use" answer.
895
+ return null;
896
+ },
897
+ createTimeout: (ms: number) => {
898
+ const controller = new AbortController();
899
+ const timer = setTimeout(() => controller.abort(), ms);
900
+ return {
901
+ signal: controller.signal,
902
+ cleanup: () => clearTimeout(timer),
903
+ };
904
+ },
905
+ };
906
+ }
907
+
908
+ private buildSttProvidersFacet(): SttProvidersFacet {
909
+ // stt sub-facet exposes two pure-data queries and one opaque-handle
910
+ // builder. The data queries would require async fetches; we expose
911
+ // them synchronously via the same "call rawCall" pattern the config
912
+ // facet uses.
913
+ return {
914
+ listProviderIds: (): string[] => {
915
+ throw new Error(
916
+ "SkillHostClient.providers.stt.listProviderIds: use `client.rawCall('host.providers.stt.listProviderIds')` and await the result.",
917
+ );
918
+ },
919
+ supportsBoundary: (_id: string): boolean => {
920
+ throw new Error(
921
+ "SkillHostClient.providers.stt.supportsBoundary: use `client.rawCall('host.providers.stt.supportsBoundary', { id, boundary: 'daemon-streaming' })` and await the result.",
922
+ );
923
+ },
924
+ resolveStreamingTranscriber: async (spec: unknown) =>
925
+ ({
926
+ __vellumSkillHostClientHandle: "streaming-transcriber",
927
+ spec,
928
+ }) as unknown,
929
+ };
930
+ }
931
+
932
+ private buildTtsProvidersFacet(): TtsProvidersFacet {
933
+ return {
934
+ get: (id: string): TtsProvider =>
935
+ ({
936
+ __vellumSkillHostClientHandle: "tts-provider",
937
+ id,
938
+ }) as unknown as TtsProvider,
939
+ resolveConfig: (): TtsConfig => {
940
+ throw new Error(
941
+ "SkillHostClient.providers.tts.resolveConfig: use `client.rawCall('host.providers.tts.resolveConfig')` and await the result.",
942
+ );
943
+ },
944
+ };
945
+ }
946
+
947
+ private buildSecureKeysFacet(): SecureKeysFacet {
948
+ return {
949
+ getProviderKey: async (id: string): Promise<string | null> =>
950
+ this.call<string | null>("host.providers.secureKeys.getProviderKey", {
951
+ id,
952
+ }),
953
+ };
954
+ }
955
+
956
+ private buildProvidersFacet(): ProvidersFacet {
957
+ return {
958
+ llm: this.buildLlmProvidersFacet(),
959
+ stt: this.buildSttProvidersFacet(),
960
+ tts: this.buildTtsProvidersFacet(),
961
+ secureKeys: this.buildSecureKeysFacet(),
962
+ };
963
+ }
964
+
965
+ private buildMemoryFacet(): MemoryFacet {
966
+ const addMessage: InsertMessageFn = async (
967
+ conversationId,
968
+ role,
969
+ content,
970
+ metadata,
971
+ opts,
972
+ ) =>
973
+ this.call("host.memory.addMessage", {
974
+ conversationId,
975
+ role,
976
+ content,
977
+ metadata,
978
+ opts,
979
+ });
980
+
981
+ return {
982
+ addMessage,
983
+ wakeAgentForOpportunity: async (req) => {
984
+ // The contract types `req` as opaque; the daemon route validates
985
+ // the concrete `{ conversationId, hint, source }` shape.
986
+ await this.call("host.memory.wakeAgentForOpportunity", {
987
+ ...(req as Record<string, unknown>),
988
+ });
989
+ },
990
+ };
991
+ }
992
+
993
+ private buildEventsFacet(): EventsFacet {
994
+ return {
995
+ publish: async (event) => {
996
+ await this.call("host.events.publish", { event });
997
+ },
998
+ subscribe: (filter, cb) => this.openSubscription(filter, cb),
999
+ buildEvent: (message: ServerMessage, conversationId?: string) => {
1000
+ // `buildEvent` is typed as sync on the contract (the daemon
1001
+ // allocates a uuid + timestamp and returns the envelope). A sync
1002
+ // round-trip isn't possible, so the client produces an envelope
1003
+ // locally using the cached assistant id and the standard uuid /
1004
+ // timestamp sources. This matches the observable shape of the
1005
+ // daemon's `buildAssistantEvent` without the round-trip.
1006
+ if (this.cachedInternalAssistantId === null) throw notConnected();
1007
+ return {
1008
+ id: randomUUID(),
1009
+ assistantId: this.cachedInternalAssistantId,
1010
+ conversationId,
1011
+ emittedAt: new Date().toISOString(),
1012
+ message,
1013
+ };
1014
+ },
1015
+ };
1016
+ }
1017
+
1018
+ private openSubscription(
1019
+ filter: Filter,
1020
+ callback: AssistantEventCallback,
1021
+ ): Subscription {
1022
+ const id = this.nextSkillRequestId();
1023
+ const active: ActiveSubscription = {
1024
+ id,
1025
+ filter,
1026
+ callback,
1027
+ disposed: false,
1028
+ };
1029
+ this.subscriptions.set(id, active);
1030
+ // Pre-register a pending call for the open ack. The server writes a
1031
+ // `{ id, result: { subscribed: true } }` frame back; subsequent
1032
+ // `delivery` frames share the same id.
1033
+ this.registerSubscribeAck(active);
1034
+ try {
1035
+ this.writeFrame({
1036
+ id,
1037
+ method: "host.events.subscribe",
1038
+ params: { filter },
1039
+ });
1040
+ } catch (err) {
1041
+ this.cancelSubscribeAck(active);
1042
+ throw err;
1043
+ }
1044
+
1045
+ const self = this;
1046
+ return {
1047
+ get active() {
1048
+ return !active.disposed;
1049
+ },
1050
+ dispose: () => {
1051
+ if (active.disposed) return;
1052
+ active.disposed = true;
1053
+ self.subscriptions.delete(id);
1054
+ // Fire-and-forget close RPC — we don't await the ack because the
1055
+ // server also tears down on socket close, which is the fallback.
1056
+ if (self.socket && !self.socket.destroyed) {
1057
+ self
1058
+ .call(SUBSCRIBE_CLOSE_METHOD, { subscribeId: id })
1059
+ .catch(swallow);
1060
+ }
1061
+ },
1062
+ };
1063
+ }
1064
+
1065
+ private buildRegistriesFacet(): RegistriesFacet {
1066
+ return {
1067
+ registerTools: (provider) => {
1068
+ // Invoke the provider synchronously so a failure blows up at the
1069
+ // registration call site (matching the in-process semantics)
1070
+ // rather than silently dropping the tools into the RPC.
1071
+ const tools: Tool[] = provider();
1072
+ const manifests = tools.map((t) => {
1073
+ const def = t.getDefinition();
1074
+ return {
1075
+ name: t.name,
1076
+ description: t.description,
1077
+ input_schema: def.input_schema,
1078
+ defaultRiskLevel: t.defaultRiskLevel,
1079
+ category: t.category,
1080
+ executionTarget: t.executionTarget,
1081
+ executionMode: t.executionMode ?? "proxy",
1082
+ ownerSkillId: t.ownerSkillId ?? this.options.skillId,
1083
+ ownerSkillBundled: t.ownerSkillBundled,
1084
+ ownerSkillVersionHash: t.ownerSkillVersionHash,
1085
+ };
1086
+ });
1087
+ // Cache the provider so `skill.dispatch_tool` can resolve a tool
1088
+ // name back to its `execute` closure. Last writer wins.
1089
+ this.cachedToolsProvider = provider;
1090
+ this.ensureDaemonHandler(
1091
+ "skill.dispatch_tool",
1092
+ this.dispatchTool.bind(this),
1093
+ );
1094
+ // Fire-and-forget; registration failures surface in the daemon log.
1095
+ this.call("host.registries.register_tools", { tools: manifests }).catch(
1096
+ swallow,
1097
+ );
1098
+ },
1099
+ registerSkillRoute: (route: SkillRoute): SkillRouteHandle => {
1100
+ // Cache the route by its regex source — `skill.dispatch_route` uses
1101
+ // the same key to find the handler closure. Re-registering the same
1102
+ // source replaces the prior route, matching in-process hot-reload.
1103
+ this.cachedRoutes.set(route.pattern.source, route);
1104
+ this.ensureDaemonHandler(
1105
+ "skill.dispatch_route",
1106
+ this.dispatchRoute.bind(this),
1107
+ );
1108
+ // The `handler` closure cannot cross IPC; the daemon side installs
1109
+ // a proxy that dispatches back over `skill.dispatch_route` (PR D).
1110
+ // `patternFlags` ships separately so `i/m/g/s/u/y` survive the
1111
+ // RegExp → string round-trip — `new RegExp(source)` alone drops them.
1112
+ this.call("host.registries.register_skill_route", {
1113
+ patternSource: route.pattern.source,
1114
+ patternFlags: route.pattern.flags,
1115
+ methods: route.methods,
1116
+ }).catch(swallow);
1117
+ // The contract models the handle as a branded opaque object — we
1118
+ // return a structurally inert placeholder.
1119
+ return {} as SkillRouteHandle;
1120
+ },
1121
+ registerShutdownHook: (name: string, hook) => {
1122
+ // Cache the hook so `skill.shutdown` can invoke it. If the same
1123
+ // name is re-registered, replace in place to keep the array tidy
1124
+ // without disturbing relative order of unrelated entries.
1125
+ const existingIdx = this.cachedShutdownHooks.findIndex(
1126
+ (h) => h.name === name,
1127
+ );
1128
+ const entry = { name, hook };
1129
+ if (existingIdx >= 0) {
1130
+ this.cachedShutdownHooks[existingIdx] = entry;
1131
+ } else {
1132
+ this.cachedShutdownHooks.push(entry);
1133
+ }
1134
+ this.ensureDaemonHandler(
1135
+ "skill.shutdown",
1136
+ this.dispatchShutdown.bind(this),
1137
+ );
1138
+ // Fire-and-forget; the daemon registers a proxy that fires the
1139
+ // skill.shutdown dispatch at teardown (PR D).
1140
+ this.call("host.registries.register_shutdown_hook", { name }).catch(
1141
+ swallow,
1142
+ );
1143
+ },
1144
+ };
1145
+ }
1146
+
1147
+ // ── Local dispatch helpers ──────────────────────────────────────────────
1148
+
1149
+ /**
1150
+ * Install a daemon-initiated request handler exactly once per method.
1151
+ * Idempotent so repeated `register*` calls don't churn the handler table
1152
+ * — every dispatch is routed through the same bound method anyway.
1153
+ */
1154
+ private ensureDaemonHandler(
1155
+ method: string,
1156
+ handler: SkillHostRequestHandler,
1157
+ ): void {
1158
+ if (!this.daemonRequestHandlers.has(method)) {
1159
+ this.daemonRequestHandlers.set(method, handler);
1160
+ }
1161
+ }
1162
+
1163
+ /**
1164
+ * `skill.dispatch_tool` handler — resolves the tool by name from the
1165
+ * cached provider and invokes its `execute(input, context)`. Returns
1166
+ * `{ result }` so the daemon can distinguish the wrapper from the
1167
+ * tool's own (potentially undefined) return value.
1168
+ */
1169
+ private async dispatchTool(params: unknown): Promise<{ result: unknown }> {
1170
+ const { name, input, context } = (params ?? {}) as {
1171
+ name?: unknown;
1172
+ input?: unknown;
1173
+ context?: unknown;
1174
+ };
1175
+ if (typeof name !== "string" || !name) {
1176
+ throw new Error(
1177
+ "skill.dispatch_tool: missing or invalid 'name' parameter",
1178
+ );
1179
+ }
1180
+ const provider = this.cachedToolsProvider;
1181
+ if (!provider) {
1182
+ throw new Error(`unknown tool: ${name}`);
1183
+ }
1184
+ // Re-invoke the provider on each dispatch so feature-flag-gated tool
1185
+ // lists stay live — matches the daemon's lazy-manifest semantics in
1186
+ // `assistant/src/tools/registry.ts`.
1187
+ const tools = provider();
1188
+ const tool = tools.find((t) => t.name === name);
1189
+ if (!tool) {
1190
+ throw new Error(`unknown tool: ${name}`);
1191
+ }
1192
+ // The daemon-side `ToolContext` is opaque on the wire; the skill's
1193
+ // `Tool.execute` runtime-validates any field it actually reads, so a
1194
+ // structural cast is sufficient here. Missing required-on-paper fields
1195
+ // are tolerated in practice — meet-host's tools only consult a small
1196
+ // subset that the daemon serializes through.
1197
+ const ctx = (context ?? {}) as ToolContext;
1198
+ const result = await tool.execute(
1199
+ (input ?? {}) as Record<string, unknown>,
1200
+ ctx,
1201
+ );
1202
+ return { result };
1203
+ }
1204
+
1205
+ /**
1206
+ * `skill.dispatch_route` handler — looks up the route by patternSource,
1207
+ * re-runs the regex against the inbound URL to recover match groups,
1208
+ * invokes the handler, and serializes the `Response` to a
1209
+ * JSON-friendly `{ status, headers, body }`.
1210
+ */
1211
+ private async dispatchRoute(params: unknown): Promise<{
1212
+ status: number;
1213
+ headers: Record<string, string>;
1214
+ body: string;
1215
+ }> {
1216
+ const { patternSource, request } = (params ?? {}) as {
1217
+ patternSource?: unknown;
1218
+ request?: unknown;
1219
+ };
1220
+ if (typeof patternSource !== "string" || !patternSource) {
1221
+ throw new Error(
1222
+ "skill.dispatch_route: missing or invalid 'patternSource' parameter",
1223
+ );
1224
+ }
1225
+ const route = this.cachedRoutes.get(patternSource);
1226
+ if (!route) {
1227
+ throw new Error(`unknown route: ${patternSource}`);
1228
+ }
1229
+ const req = (request ?? {}) as {
1230
+ method?: string;
1231
+ url?: string;
1232
+ headers?: Record<string, string>;
1233
+ body?: string;
1234
+ };
1235
+ if (typeof req.url !== "string" || !req.url) {
1236
+ throw new Error(
1237
+ "skill.dispatch_route: missing or invalid 'request.url' parameter",
1238
+ );
1239
+ }
1240
+ // Resolve against a synthetic base so `new Request` accepts the URL
1241
+ // (it rejects bare paths, but the daemon forwards `pathname + search`)
1242
+ // and so the regex can run against `pathname` alone — keeps query
1243
+ // strings out of anchored patterns like `^/v1/...$`.
1244
+ const parsedUrl = new URL(req.url, "http://skill.local");
1245
+ // Reset lastIndex so a global/sticky regex doesn't carry state across
1246
+ // dispatches — `exec()` mutates lastIndex on g/y flags and the route's
1247
+ // RegExp may be reused across requests.
1248
+ if (route.pattern.global || route.pattern.sticky) {
1249
+ route.pattern.lastIndex = 0;
1250
+ }
1251
+ const match = route.pattern.exec(parsedUrl.pathname);
1252
+ if (!match) {
1253
+ throw new Error(`url did not match pattern: ${patternSource}`);
1254
+ }
1255
+ const init: RequestInit = {
1256
+ method: req.method ?? "GET",
1257
+ headers: req.headers ?? {},
1258
+ };
1259
+ // GET/HEAD requests cannot carry a body in the standard fetch `Request`
1260
+ // constructor; only attach when the verb permits.
1261
+ if (
1262
+ req.body !== undefined &&
1263
+ init.method !== "GET" &&
1264
+ init.method !== "HEAD"
1265
+ ) {
1266
+ init.body = req.body;
1267
+ }
1268
+ const response = await route.handler(
1269
+ new Request(parsedUrl.toString(), init),
1270
+ match,
1271
+ );
1272
+ const headers: Record<string, string> = {};
1273
+ response.headers.forEach((value, key) => {
1274
+ headers[key] = value;
1275
+ });
1276
+ const body = await response.text();
1277
+ return { status: response.status, headers, body };
1278
+ }
1279
+
1280
+ /**
1281
+ * `skill.shutdown` handler — runs cached shutdown hooks. With `name`
1282
+ * set, runs only that hook; otherwise runs all hooks in reverse-
1283
+ * registration order. Per-hook errors are logged via the host logger
1284
+ * and otherwise swallowed so one misbehaving hook can't block the
1285
+ * daemon's overall teardown.
1286
+ */
1287
+ private async dispatchShutdown(params: unknown): Promise<{ ok: true }> {
1288
+ const { name, reason } = (params ?? {}) as {
1289
+ name?: unknown;
1290
+ reason?: unknown;
1291
+ };
1292
+ const reasonStr = typeof reason === "string" ? reason : "shutdown";
1293
+ const log = this.buildLogger(this.options.skillId);
1294
+ const runOne = async (entry: {
1295
+ name: string;
1296
+ hook: (reason: string) => Promise<void>;
1297
+ }): Promise<void> => {
1298
+ try {
1299
+ await entry.hook(reasonStr);
1300
+ } catch (err) {
1301
+ log.warn(`shutdown hook '${entry.name}' threw`, {
1302
+ error: errorMessage(err),
1303
+ });
1304
+ }
1305
+ };
1306
+ if (typeof name === "string" && name) {
1307
+ const entry = this.cachedShutdownHooks.find((h) => h.name === name);
1308
+ if (entry) await runOne(entry);
1309
+ // Silently no-op for unknown names — the daemon may call shutdown
1310
+ // for a hook that was never registered (e.g. a stale registration
1311
+ // leftover from a previous skill load), which shouldn't fail the
1312
+ // overall teardown.
1313
+ return { ok: true };
1314
+ }
1315
+ // Reverse-registration order so later-registered hooks (which often
1316
+ // depend on earlier ones) tear down first.
1317
+ for (let i = this.cachedShutdownHooks.length - 1; i >= 0; i--) {
1318
+ const entry = this.cachedShutdownHooks[i];
1319
+ if (entry) await runOne(entry);
1320
+ }
1321
+ return { ok: true };
1322
+ }
1323
+
1324
+ private buildSpeakersFacet(): SpeakersFacet {
1325
+ return {
1326
+ createTracker: () =>
1327
+ ({
1328
+ __vellumSkillHostClientHandle: "speaker-tracker",
1329
+ }) as unknown,
1330
+ };
1331
+ }
1332
+
1333
+ // ── Public escape hatch ─────────────────────────────────────────────────
1334
+
1335
+ /**
1336
+ * Escape hatch for invoking any `host.*` IPC method directly. Callers
1337
+ * that need to bypass the sync-method ergonomic gap (e.g. async reads
1338
+ * of `host.config.*` or `host.providers.stt.listProviderIds`) use this
1339
+ * to await a single RPC round-trip. The return type is unknown because
1340
+ * the method surface is open.
1341
+ */
1342
+ async rawCall<T = unknown>(
1343
+ method: string,
1344
+ params?: Record<string, unknown>,
1345
+ ): Promise<T> {
1346
+ return this.call<T>(method, params);
1347
+ }
1348
+ }