@vellumai/assistant 0.11.2 → 0.11.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -11,6 +11,7 @@ import { join } from "node:path";
11
11
  import { afterEach, describe, expect, test } from "bun:test";
12
12
 
13
13
  import {
14
+ DEFAULT_READ_LINE_LIMIT,
14
15
  FileSystemOps,
15
16
  type PathPolicy,
16
17
  } from "../tools/shared/filesystem/file-ops-service.js";
@@ -44,12 +45,12 @@ function sandboxPolicyFor(boundary: string): PathPolicy {
44
45
  // ---------------------------------------------------------------------------
45
46
 
46
47
  describe("FileSystemOps.readFileSafe", () => {
47
- test("reads a file successfully", () => {
48
+ test("reads a file successfully", async () => {
48
49
  const dir = makeTempDir();
49
50
  writeFileSync(join(dir, "hello.txt"), "line one\nline two\nline three\n");
50
51
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
51
52
 
52
- const result = ops.readFileSafe({ path: "hello.txt" });
53
+ const result = await ops.readFileSafe({ path: "hello.txt" });
53
54
  expect(result.ok).toBe(true);
54
55
  if (!result.ok) {
55
56
  return;
@@ -59,11 +60,11 @@ describe("FileSystemOps.readFileSafe", () => {
59
60
  expect(result.value.content).toContain("line three");
60
61
  });
61
62
 
62
- test("returns NOT_FOUND for missing file", () => {
63
+ test("returns NOT_FOUND for missing file", async () => {
63
64
  const dir = makeTempDir();
64
65
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
65
66
 
66
- const result = ops.readFileSafe({ path: "nonexistent.txt" });
67
+ const result = await ops.readFileSafe({ path: "nonexistent.txt" });
67
68
  expect(result.ok).toBe(false);
68
69
  if (result.ok) {
69
70
  return;
@@ -71,12 +72,12 @@ describe("FileSystemOps.readFileSafe", () => {
71
72
  expect(result.error.code).toBe("NOT_FOUND");
72
73
  });
73
74
 
74
- test("returns NOT_A_FILE for a directory", () => {
75
+ test("returns NOT_A_FILE for a directory", async () => {
75
76
  const dir = makeTempDir();
76
77
  mkdirSync(join(dir, "subdir"));
77
78
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
78
79
 
79
- const result = ops.readFileSafe({ path: "subdir" });
80
+ const result = await ops.readFileSafe({ path: "subdir" });
80
81
  expect(result.ok).toBe(false);
81
82
  if (result.ok) {
82
83
  return;
@@ -84,13 +85,13 @@ describe("FileSystemOps.readFileSafe", () => {
84
85
  expect(result.error.code).toBe("NOT_A_FILE");
85
86
  });
86
87
 
87
- test("returns SIZE_LIMIT_EXCEEDED for oversized file", () => {
88
+ test("returns SIZE_LIMIT_EXCEEDED for oversized file", async () => {
88
89
  const dir = makeTempDir();
89
90
  const filePath = join(dir, "big.txt");
90
91
  writeFileSync(filePath, "x".repeat(200));
91
92
 
92
93
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
93
- const result = ops.readFileSafe({ path: "big.txt" });
94
+ const result = await ops.readFileSafe({ path: "big.txt" });
94
95
  expect(result.ok).toBe(false);
95
96
  if (result.ok) {
96
97
  return;
@@ -98,11 +99,11 @@ describe("FileSystemOps.readFileSafe", () => {
98
99
  expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
99
100
  });
100
101
 
101
- test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", () => {
102
+ test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", async () => {
102
103
  const dir = makeTempDir();
103
104
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
104
105
 
105
- const result = ops.readFileSafe({ path: "../../../etc/passwd" });
106
+ const result = await ops.readFileSafe({ path: "../../../etc/passwd" });
106
107
  expect(result.ok).toBe(false);
107
108
  if (result.ok) {
108
109
  return;
@@ -110,20 +111,94 @@ describe("FileSystemOps.readFileSafe", () => {
110
111
  expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
111
112
  });
112
113
 
113
- test("respects offset and limit", () => {
114
+ test("respects offset and limit", async () => {
114
115
  const dir = makeTempDir();
115
116
  writeFileSync(join(dir, "lines.txt"), "a\nb\nc\nd\ne\n");
116
117
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
117
118
 
118
- const result = ops.readFileSafe({ path: "lines.txt", offset: 2, limit: 2 });
119
+ const result = await ops.readFileSafe({
120
+ path: "lines.txt",
121
+ offset: 2,
122
+ limit: 2,
123
+ });
124
+ expect(result.ok).toBe(true);
125
+ if (!result.ok) {
126
+ return;
127
+ }
128
+ // Assert on the numbered lines rather than bare letters: the truncation
129
+ // notice is prose, so a bare `toContain("d")` matches its wording.
130
+ const [body] = result.value.content.split("\n\n[Truncated:");
131
+ expect(body).toContain(" 2 b");
132
+ expect(body).toContain(" 3 c");
133
+ expect(body).not.toContain(" 1 a");
134
+ expect(body).not.toContain(" 4 d");
135
+ });
136
+
137
+ test("caps an unbounded read at the default line limit and says so", async () => {
138
+ const dir = makeTempDir();
139
+ const total = DEFAULT_READ_LINE_LIMIT + 500;
140
+ const lines = Array.from({ length: total }, (_, i) => `line${i + 1}`);
141
+ writeFileSync(join(dir, "big.txt"), lines.join("\n"));
142
+ const ops = new FileSystemOps(sandboxPolicyFor(dir));
143
+
144
+ const result = await ops.readFileSafe({ path: "big.txt" });
145
+ expect(result.ok).toBe(true);
146
+ if (!result.ok) {
147
+ return;
148
+ }
149
+ const content = result.value.content;
150
+ expect(content).toContain(` ${DEFAULT_READ_LINE_LIMIT} line2000`);
151
+ expect(content).not.toContain("line2001");
152
+ expect(content).toContain(
153
+ `[Truncated: showing through line ${DEFAULT_READ_LINE_LIMIT} of ${total}. Read on with offset=${DEFAULT_READ_LINE_LIMIT + 1}`,
154
+ );
155
+ });
156
+
157
+ test("an explicit limit is honored rather than replaced by the default", async () => {
158
+ const dir = makeTempDir();
159
+ const lines = Array.from(
160
+ { length: DEFAULT_READ_LINE_LIMIT + 500 },
161
+ (_, i) => `line${i + 1}`,
162
+ );
163
+ writeFileSync(join(dir, "big.txt"), lines.join("\n"));
164
+ const ops = new FileSystemOps(sandboxPolicyFor(dir));
165
+
166
+ const result = await ops.readFileSafe({
167
+ path: "big.txt",
168
+ limit: DEFAULT_READ_LINE_LIMIT + 500,
169
+ });
170
+ expect(result.ok).toBe(true);
171
+ if (!result.ok) {
172
+ return;
173
+ }
174
+ expect(result.value.content).toContain("line2500");
175
+ expect(result.value.content).not.toContain("[Truncated:");
176
+ });
177
+
178
+ test("a read that reaches the last line carries no truncation notice", async () => {
179
+ const dir = makeTempDir();
180
+ writeFileSync(join(dir, "small.txt"), "a\nb\nc");
181
+ const ops = new FileSystemOps(sandboxPolicyFor(dir));
182
+
183
+ const result = await ops.readFileSafe({ path: "small.txt" });
184
+ expect(result.ok).toBe(true);
185
+ if (!result.ok) {
186
+ return;
187
+ }
188
+ expect(result.value.content).not.toContain("[Truncated:");
189
+ });
190
+
191
+ test("paging past the end is not reported as truncation", async () => {
192
+ const dir = makeTempDir();
193
+ writeFileSync(join(dir, "small.txt"), "a\nb\nc");
194
+ const ops = new FileSystemOps(sandboxPolicyFor(dir));
195
+
196
+ const result = await ops.readFileSafe({ path: "small.txt", offset: 100 });
119
197
  expect(result.ok).toBe(true);
120
198
  if (!result.ok) {
121
199
  return;
122
200
  }
123
- expect(result.value.content).toContain("b");
124
- expect(result.value.content).toContain("c");
125
- expect(result.value.content).not.toContain(" 1");
126
- expect(result.value.content).not.toContain("d");
201
+ expect(result.value.content).not.toContain("[Truncated:");
127
202
  });
128
203
  });
129
204
 
@@ -132,11 +207,11 @@ describe("FileSystemOps.readFileSafe", () => {
132
207
  // ---------------------------------------------------------------------------
133
208
 
134
209
  describe("FileSystemOps.writeFileSafe", () => {
135
- test("writes a new file", () => {
210
+ test("writes a new file", async () => {
136
211
  const dir = makeTempDir();
137
212
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
138
213
 
139
- const result = ops.writeFileSafe({
214
+ const result = await ops.writeFileSafe({
140
215
  path: "new.txt",
141
216
  content: "hello world",
142
217
  });
@@ -150,12 +225,12 @@ describe("FileSystemOps.writeFileSafe", () => {
150
225
  expect(existsSync(join(dir, "new.txt"))).toBe(true);
151
226
  });
152
227
 
153
- test("overwrites an existing file and returns old content", () => {
228
+ test("overwrites an existing file and returns old content", async () => {
154
229
  const dir = makeTempDir();
155
230
  writeFileSync(join(dir, "existing.txt"), "old stuff");
156
231
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
157
232
 
158
- const result = ops.writeFileSafe({
233
+ const result = await ops.writeFileSafe({
159
234
  path: "existing.txt",
160
235
  content: "new stuff",
161
236
  });
@@ -168,11 +243,11 @@ describe("FileSystemOps.writeFileSafe", () => {
168
243
  expect(result.value.newContent).toBe("new stuff");
169
244
  });
170
245
 
171
- test("creates parent directories when needed", () => {
246
+ test("creates parent directories when needed", async () => {
172
247
  const dir = makeTempDir();
173
248
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
174
249
 
175
- const result = ops.writeFileSafe({
250
+ const result = await ops.writeFileSafe({
176
251
  path: "a/b/c/deep.txt",
177
252
  content: "deep",
178
253
  });
@@ -184,11 +259,11 @@ describe("FileSystemOps.writeFileSafe", () => {
184
259
  expect(existsSync(join(dir, "a/b/c/deep.txt"))).toBe(true);
185
260
  });
186
261
 
187
- test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", () => {
262
+ test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", async () => {
188
263
  const dir = makeTempDir();
189
264
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
190
265
 
191
- const result = ops.writeFileSafe({
266
+ const result = await ops.writeFileSafe({
192
267
  path: "../../../tmp/evil.txt",
193
268
  content: "bad",
194
269
  });
@@ -199,11 +274,11 @@ describe("FileSystemOps.writeFileSafe", () => {
199
274
  expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
200
275
  });
201
276
 
202
- test("returns SIZE_LIMIT_EXCEEDED for oversized content", () => {
277
+ test("returns SIZE_LIMIT_EXCEEDED for oversized content", async () => {
203
278
  const dir = makeTempDir();
204
279
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 10 });
205
280
 
206
- const result = ops.writeFileSafe({
281
+ const result = await ops.writeFileSafe({
207
282
  path: "big.txt",
208
283
  content: "x".repeat(50),
209
284
  });
@@ -220,11 +295,43 @@ describe("FileSystemOps.writeFileSafe", () => {
220
295
  // ---------------------------------------------------------------------------
221
296
 
222
297
  describe("FileSystemOps.editFileSafe", () => {
223
- test("returns NOT_FOUND for nonexistent file", () => {
298
+ test("concurrent edits of the same file both land", async () => {
299
+ const dir = makeTempDir();
300
+ const ops = new FileSystemOps(sandboxPolicyFor(dir));
301
+ writeFileSync(join(dir, "shared.txt"), "alpha\nbeta\n");
302
+
303
+ // Without per-path serialization, both edits read the same original
304
+ // content and the later write drops the earlier edit.
305
+ const [first, second] = await Promise.all([
306
+ ops.editFileSafe({
307
+ path: "shared.txt",
308
+ oldString: "alpha",
309
+ newString: "ALPHA",
310
+ replaceAll: false,
311
+ }),
312
+ ops.editFileSafe({
313
+ path: "shared.txt",
314
+ oldString: "beta",
315
+ newString: "BETA",
316
+ replaceAll: false,
317
+ }),
318
+ ]);
319
+
320
+ expect(first.ok).toBe(true);
321
+ expect(second.ok).toBe(true);
322
+ const read = await ops.readFileSafe({ path: "shared.txt" });
323
+ expect(read.ok).toBe(true);
324
+ if (read.ok) {
325
+ expect(read.value.content).toContain("ALPHA");
326
+ expect(read.value.content).toContain("BETA");
327
+ }
328
+ });
329
+
330
+ test("returns NOT_FOUND for nonexistent file", async () => {
224
331
  const dir = makeTempDir();
225
332
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
226
333
 
227
- const result = ops.editFileSafe({
334
+ const result = await ops.editFileSafe({
228
335
  path: "nope.txt",
229
336
  oldString: "a",
230
337
  newString: "b",
@@ -237,12 +344,12 @@ describe("FileSystemOps.editFileSafe", () => {
237
344
  expect(result.error.code).toBe("NOT_FOUND");
238
345
  });
239
346
 
240
- test("returns NOT_A_FILE when target is a directory", () => {
347
+ test("returns NOT_A_FILE when target is a directory", async () => {
241
348
  const dir = makeTempDir();
242
349
  mkdirSync(join(dir, "subdir"));
243
350
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
244
351
 
245
- const result = ops.editFileSafe({
352
+ const result = await ops.editFileSafe({
246
353
  path: "subdir",
247
354
  oldString: "a",
248
355
  newString: "b",
@@ -255,12 +362,12 @@ describe("FileSystemOps.editFileSafe", () => {
255
362
  expect(result.error.code).toBe("NOT_A_FILE");
256
363
  });
257
364
 
258
- test("returns MATCH_NOT_FOUND when old_string is absent", () => {
365
+ test("returns MATCH_NOT_FOUND when old_string is absent", async () => {
259
366
  const dir = makeTempDir();
260
367
  writeFileSync(join(dir, "file.txt"), "hello world");
261
368
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
262
369
 
263
- const result = ops.editFileSafe({
370
+ const result = await ops.editFileSafe({
264
371
  path: "file.txt",
265
372
  oldString: "xyz",
266
373
  newString: "abc",
@@ -273,12 +380,12 @@ describe("FileSystemOps.editFileSafe", () => {
273
380
  expect(result.error.code).toBe("MATCH_NOT_FOUND");
274
381
  });
275
382
 
276
- test("returns MATCH_AMBIGUOUS when old_string matches multiple times", () => {
383
+ test("returns MATCH_AMBIGUOUS when old_string matches multiple times", async () => {
277
384
  const dir = makeTempDir();
278
385
  writeFileSync(join(dir, "file.txt"), "foo bar foo baz foo");
279
386
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
280
387
 
281
- const result = ops.editFileSafe({
388
+ const result = await ops.editFileSafe({
282
389
  path: "file.txt",
283
390
  oldString: "foo",
284
391
  newString: "qux",
@@ -291,12 +398,12 @@ describe("FileSystemOps.editFileSafe", () => {
291
398
  expect(result.error.code).toBe("MATCH_AMBIGUOUS");
292
399
  });
293
400
 
294
- test("replaces all occurrences when replaceAll is true", () => {
401
+ test("replaces all occurrences when replaceAll is true", async () => {
295
402
  const dir = makeTempDir();
296
403
  writeFileSync(join(dir, "file.txt"), "foo bar foo baz foo");
297
404
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
298
405
 
299
- const result = ops.editFileSafe({
406
+ const result = await ops.editFileSafe({
300
407
  path: "file.txt",
301
408
  oldString: "foo",
302
409
  newString: "qux",
@@ -312,12 +419,12 @@ describe("FileSystemOps.editFileSafe", () => {
312
419
  expect(result.value.matchMethod).toBe("exact");
313
420
  });
314
421
 
315
- test("performs a unique edit successfully", () => {
422
+ test("performs a unique edit successfully", async () => {
316
423
  const dir = makeTempDir();
317
424
  writeFileSync(join(dir, "file.txt"), "one two three");
318
425
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
319
426
 
320
- const result = ops.editFileSafe({
427
+ const result = await ops.editFileSafe({
321
428
  path: "file.txt",
322
429
  oldString: "two",
323
430
  newString: "TWO",
@@ -333,12 +440,12 @@ describe("FileSystemOps.editFileSafe", () => {
333
440
  expect(result.value.filePath).toContain("file.txt");
334
441
  });
335
442
 
336
- test("returns MATCH_NOT_FOUND for empty oldString", () => {
443
+ test("returns MATCH_NOT_FOUND for empty oldString", async () => {
337
444
  const dir = makeTempDir();
338
445
  writeFileSync(join(dir, "file.txt"), "hello world");
339
446
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
340
447
 
341
- const result = ops.editFileSafe({
448
+ const result = await ops.editFileSafe({
342
449
  path: "file.txt",
343
450
  oldString: "",
344
451
  newString: "injected",
@@ -351,12 +458,12 @@ describe("FileSystemOps.editFileSafe", () => {
351
458
  expect(result.error.code).toBe("MATCH_NOT_FOUND");
352
459
  });
353
460
 
354
- test("returns SIZE_LIMIT_EXCEEDED for oversized file on edit", () => {
461
+ test("returns SIZE_LIMIT_EXCEEDED for oversized file on edit", async () => {
355
462
  const dir = makeTempDir();
356
463
  writeFileSync(join(dir, "big.txt"), "x".repeat(200));
357
464
  const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
358
465
 
359
- const result = ops.editFileSafe({
466
+ const result = await ops.editFileSafe({
360
467
  path: "big.txt",
361
468
  oldString: "x",
362
469
  newString: "y",
@@ -369,11 +476,11 @@ describe("FileSystemOps.editFileSafe", () => {
369
476
  expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
370
477
  });
371
478
 
372
- test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", () => {
479
+ test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", async () => {
373
480
  const dir = makeTempDir();
374
481
  const ops = new FileSystemOps(sandboxPolicyFor(dir));
375
482
 
376
- const result = ops.editFileSafe({
483
+ const result = await ops.editFileSafe({
377
484
  path: "../../../etc/passwd",
378
485
  oldString: "root",
379
486
  newString: "toor",
@@ -0,0 +1,137 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ mkdtempSync,
5
+ realpathSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import {
12
+ afterEach,
13
+ beforeAll,
14
+ beforeEach,
15
+ describe,
16
+ expect,
17
+ test,
18
+ } from "bun:test";
19
+
20
+ import { finalizeTool } from "../tools/tool-defaults.js";
21
+ import type { Tool, ToolContext } from "../tools/types.js";
22
+
23
+ let fileReadTool: Tool;
24
+ let fileWriteTool: Tool;
25
+
26
+ beforeAll(async () => {
27
+ const [read, write] = await Promise.all([
28
+ import("../tools/filesystem/read.js"),
29
+ import("../tools/filesystem/write.js"),
30
+ ]);
31
+ fileReadTool = finalizeTool(read.fileReadTool, "file_read");
32
+ fileWriteTool = finalizeTool(write.fileWriteTool, "file_write");
33
+ });
34
+
35
+ const testDirs: string[] = [];
36
+
37
+ function makeTempDir(prefix: string): string {
38
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), prefix)));
39
+ testDirs.push(dir);
40
+ return dir;
41
+ }
42
+
43
+ function makeContext(workingDir: string): ToolContext {
44
+ return {
45
+ workingDir,
46
+ conversationId: "test-conversation",
47
+ trustClass: "guardian",
48
+ };
49
+ }
50
+
51
+ /**
52
+ * A skill installed outside the workspace, mirroring a bundled skill in the
53
+ * container's install tree (`/app/assistant/src/config/bundled-skills/<id>`).
54
+ * Skill bodies point the model at these reference files by absolute path.
55
+ */
56
+ function makeInstalledSkill(id: string): string {
57
+ const installTree = makeTempDir("container-install-");
58
+ const directoryPath = join(installTree, id);
59
+ mkdirSync(join(directoryPath, "references"), { recursive: true });
60
+ writeFileSync(join(directoryPath, "SKILL.md"), `# ${id}\n`);
61
+ return directoryPath;
62
+ }
63
+
64
+ beforeEach(() => {
65
+ // The read allowance only changes behavior where the boundary is hard: on
66
+ // non-containerized installs out-of-workspace reads already fall back to
67
+ // host-style validation.
68
+ process.env.IS_CONTAINERIZED = "true";
69
+ });
70
+
71
+ afterEach(() => {
72
+ delete process.env.IS_CONTAINERIZED;
73
+ delete process.env.GATEWAY_SECURITY_DIR;
74
+ for (const dir of testDirs.splice(0)) {
75
+ rmSync(dir, { recursive: true, force: true });
76
+ }
77
+ });
78
+
79
+ describe("file_read outside the workspace in a container", () => {
80
+ test("reads a reference file from the install tree", async () => {
81
+ const workspace = makeTempDir("container-workspace-");
82
+ const skillDir = makeInstalledSkill("image-studio");
83
+ const referencePath = join(skillDir, "references", "guide.md");
84
+ writeFileSync(referencePath, "# Guide\nreference body\n");
85
+
86
+ const result = await fileReadTool.execute(
87
+ { path: referencePath },
88
+ makeContext(workspace),
89
+ );
90
+
91
+ expect(result.isError).toBe(false);
92
+ expect(result.content).toContain("reference body");
93
+ });
94
+
95
+ test("denies the service security directory", async () => {
96
+ const workspace = makeTempDir("container-workspace-");
97
+ const securityDir = makeTempDir("container-security-");
98
+ writeFileSync(join(securityDir, "trust.json"), "trust body\n");
99
+ process.env.GATEWAY_SECURITY_DIR = securityDir;
100
+
101
+ const result = await fileReadTool.execute(
102
+ { path: join(securityDir, "trust.json") },
103
+ makeContext(workspace),
104
+ );
105
+
106
+ expect(result.isError).toBe(true);
107
+ expect(result.content).toContain("service security directory");
108
+ expect(result.content).not.toContain("trust body");
109
+ });
110
+
111
+ test("denies the daemon's own process environment", async () => {
112
+ const workspace = makeTempDir("container-workspace-");
113
+
114
+ const result = await fileReadTool.execute(
115
+ { path: "/proc/self/environ" },
116
+ makeContext(workspace),
117
+ );
118
+
119
+ expect(result.isError).toBe(true);
120
+ expect(result.content).toContain("process environment");
121
+ });
122
+
123
+ test("denies file_write outside the workspace", async () => {
124
+ const workspace = makeTempDir("container-workspace-");
125
+ const skillDir = makeInstalledSkill("image-studio");
126
+ const targetPath = join(skillDir, "references", "injected.md");
127
+
128
+ const result = await fileWriteTool.execute(
129
+ { path: targetPath, content: "injected body\n" },
130
+ makeContext(workspace),
131
+ );
132
+
133
+ expect(result.isError).toBe(true);
134
+ expect(result.content).toContain("outside the working directory");
135
+ expect(existsSync(targetPath)).toBe(false);
136
+ });
137
+ });
@@ -103,16 +103,20 @@ describe("file_read tool (sandbox)", () => {
103
103
  expect(result.content).toContain("outside content");
104
104
  });
105
105
 
106
- test("rejects path traversal outside working dir when containerized", async () => {
106
+ test("reads a file outside the workspace boundary when containerized", async () => {
107
107
  const dir = makeTempDir();
108
+ const outside = makeTempDir();
109
+ const target = join(outside, "reference.md");
110
+ writeFileSync(target, "install tree content");
108
111
  process.env.IS_CONTAINERIZED = "true";
109
112
  try {
110
113
  const result = await fileReadTool.execute(
111
- { path: "../../../etc/passwd" },
114
+ { path: target },
112
115
  makeContext(dir),
113
116
  );
114
- expect(result.isError).toBe(true);
115
- expect(result.content).toContain("outside the working directory");
117
+
118
+ expect(result.isError).toBe(false);
119
+ expect(result.content).toContain("install tree content");
116
120
  } finally {
117
121
  delete process.env.IS_CONTAINERIZED;
118
122
  }
@@ -206,32 +210,40 @@ describe("file_read image support", () => {
206
210
  expect(result.content).toContain("Image loaded");
207
211
  });
208
212
 
209
- test("blocks image path traversal outside working dir when containerized", async () => {
213
+ test("reads an image outside the workspace boundary when containerized", async () => {
210
214
  const dir = makeTempDir();
215
+ const outside = makeTempDir();
216
+ writeFileSync(join(outside, "diagram.jpg"), JPEG_HEADER);
211
217
  process.env.IS_CONTAINERIZED = "true";
212
218
  try {
213
219
  const result = await fileReadTool.execute(
214
- { path: "../../etc/secret.png" },
220
+ { path: join(outside, "diagram.jpg") },
215
221
  makeContext(dir),
216
222
  );
217
223
 
218
- expect(result.isError).toBe(true);
219
- expect(result.content).toContain("outside the working directory");
224
+ expect(result.isError).toBe(false);
225
+ expect(result.content).toContain("Image loaded");
220
226
  } finally {
221
227
  delete process.env.IS_CONTAINERIZED;
222
228
  }
223
229
  });
224
230
  });
225
231
 
226
- // ── Out-of-bounds hint for host_file_read (containerized) ─────────────
232
+ // ── Out-of-bounds hint for host_file_read ─────────────────────────────
233
+ //
234
+ // Reads leave the workspace freely, so the boundary only holds when the
235
+ // service security deny set cannot be mirrored: a relative override resolves
236
+ // against the owning service's cwd, so the policy fails closed.
227
237
 
228
238
  describe("file_read out-of-bounds hint", () => {
229
239
  beforeEach(() => {
230
240
  process.env.IS_CONTAINERIZED = "true";
241
+ process.env.GATEWAY_SECURITY_DIR = "relative/security-dir";
231
242
  });
232
243
 
233
244
  afterEach(() => {
234
245
  delete process.env.IS_CONTAINERIZED;
246
+ delete process.env.GATEWAY_SECURITY_DIR;
235
247
  });
236
248
 
237
249
  test("suggests host_file_read for out-of-bounds text file path", async () => {