@vellumai/assistant 0.8.12 → 0.9.0-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 (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Per-run capability manifest and scoped tool resolution for the workflow
3
+ * orchestration engine.
4
+ *
5
+ * A workflow script declares — once, at the run level — which tools, host
6
+ * functions, and persona access its leaf agents may use. That declaration is
7
+ * the **single consent point** for the entire run: there are no per-leaf or
8
+ * per-call permission prompts inside a workflow run. The engine/leaf runner
9
+ * calls {@link resolveCapabilities} once and then **hard-denies**
10
+ * any tool invocation that falls outside the resolved set.
11
+ *
12
+ * Resolution policy:
13
+ *
14
+ * - Leaves always get a curated read-only baseline ({@link WORKFLOW_READONLY_BASELINE})
15
+ * without having to declare it — low-risk file reads, listing, search, and
16
+ * memory recall.
17
+ * - Side-effecting tools (writes, sends, shell, etc.) are NOT in the baseline;
18
+ * a run must declare them explicitly in its manifest to grant them.
19
+ * - A small set of tools is {@link WORKFLOW_FORBIDDEN_TOOLS forbidden} and can
20
+ * never be granted to a leaf regardless of declaration (recursion vectors,
21
+ * the workflow-management tools themselves, and CES bundle management).
22
+ * - Host-proxy tools (resolved `executionTarget === "host"` — e.g. `host_bash`,
23
+ * `host_file_*`, computer-use) are rejected too: a leaf builds a synthetic,
24
+ * anonymous `ToolContext` that carries none of the originating turn's
25
+ * `transportInterface` / `sourceActorPrincipalId` / proxy-resolver fields, so
26
+ * a host tool would either fall back to in-container execution or proxy to the
27
+ * user's machine WITHOUT the authenticated-identity binding those tools rely
28
+ * on. Unattended, fanned-out leaves must not perform host side effects in v1.
29
+ * (If host execution is ever wanted, the deliberate path is to thread the
30
+ * originating tool context through the engine — not to relax this gate.)
31
+ *
32
+ * This module is pure logic: it performs no feature-flag checks and no I/O
33
+ * beyond the synchronous tool-registry lookup. The `workflows` flag gates the
34
+ * callers, not this code.
35
+ */
36
+
37
+ import { z } from "zod";
38
+
39
+ import type { TrustClass } from "../runtime/actor-trust-resolver.js";
40
+ import { resolveExecutionTarget } from "../tools/execution-target.js";
41
+ import {
42
+ getCoreToolOverride,
43
+ getTool,
44
+ getToolOwner,
45
+ } from "../tools/registry.js";
46
+ import { isSideEffectTool } from "../tools/side-effects.js";
47
+ import type { Tool } from "../tools/types.js";
48
+
49
+ /**
50
+ * Curated read-only baseline available to every workflow leaf without
51
+ * declaration. Each entry is an existing core tool that only reads, lists, or
52
+ * searches — never writes, sends, executes arbitrary code, or otherwise
53
+ * mutates state. Mutating low-risk tools (e.g. `file_write`, `file_edit`,
54
+ * `remember`) and arbitrary-execution tools (`bash`, `skill_execute`) are
55
+ * deliberately excluded even though some carry a low risk band; the baseline
56
+ * is gated on read-only behavior, not on the risk level alone.
57
+ *
58
+ * `web_fetch` is deliberately NOT here: it is classified as a side-effect tool
59
+ * (an outbound request can trigger external actions or exfiltrate read data via
60
+ * the URL — see {@link isSideEffectTool}). The baseline is auto-granted with NO
61
+ * launch approval, so it must carry no side effects; a run that needs
62
+ * `web_fetch` must DECLARE it in its manifest, which forces the launch approval
63
+ * gate ({@link manifestGrantsSideEffects}). {@link resolveCapabilities} also
64
+ * filters the baseline through `isSideEffectTool` as defense in depth.
65
+ */
66
+ export const WORKFLOW_READONLY_BASELINE: readonly string[] = [
67
+ "file_read",
68
+ "file_list",
69
+ "recall",
70
+ "web_search",
71
+ ];
72
+
73
+ /**
74
+ * Tools that may never be granted to a workflow leaf, regardless of what a
75
+ * manifest declares. Declaring one is a hard error in
76
+ * {@link resolveCapabilities}.
77
+ *
78
+ * - `subagent_spawn` — leaves must not spawn nested agents; the workflow
79
+ * engine owns fan-out.
80
+ * - `run_workflow` / `manage_workflows` — the workflow tools themselves;
81
+ * granting either to a leaf would let it recurse into or reconfigure the
82
+ * engine.
83
+ * - `manage_secure_command_tool` — CES secure-bundle management is a
84
+ * human-in-the-loop install path and is never delegated to an unattended
85
+ * leaf.
86
+ * - `run_authenticated_command` / `make_authenticated_request` — these CES
87
+ * tools can return `cesApprovalRequired`, which `ToolExecutor` handles by
88
+ * bridging an interactive approval and RETRYING the call with the grant. A
89
+ * leaf invokes `tool.execute()` directly (the manifest is the run's single
90
+ * consent point, so leaves bypass the executor's permission/approval path),
91
+ * so it would see the raw `cesApprovalRequired` result as a plain error
92
+ * instead of the approve-and-retry flow — silently degrading. Forbid them
93
+ * until leaf invocations run the executor's post-processing (the deliberate
94
+ * future path); declaring one then fails loudly at the consent point rather
95
+ * than confusingly at run time.
96
+ */
97
+ export const WORKFLOW_FORBIDDEN_TOOLS: readonly string[] = [
98
+ "subagent_spawn",
99
+ "run_workflow",
100
+ "manage_workflows",
101
+ "manage_secure_command_tool",
102
+ "run_authenticated_command",
103
+ "make_authenticated_request",
104
+ ];
105
+
106
+ /**
107
+ * Per-run capability declaration. The single consent surface for a workflow
108
+ * run — `tools` and `hostFunctions` are explicit grants on top of the
109
+ * read-only baseline; `persona` opts the run into persona access.
110
+ */
111
+ export const CapabilityManifestSchema = z.object({
112
+ /** Tool names granted to leaves on top of the read-only baseline. */
113
+ tools: z.array(z.string()).default([]),
114
+ /** Host-function names the run is permitted to invoke. */
115
+ hostFunctions: z.array(z.string()).default([]),
116
+ /** Whether leaves may access persona context. */
117
+ persona: z.boolean().default(false),
118
+ });
119
+
120
+ export type CapabilityManifest = z.infer<typeof CapabilityManifestSchema>;
121
+
122
+ /**
123
+ * Normalize a stored/raw capability blob into a {@link CapabilityManifest},
124
+ * tolerating BOTH shapes a run row can hold:
125
+ *
126
+ * - the canonical declared shape — `tools`/`hostFunctions` as string names; and
127
+ * - the older RESOLVED shape some interrupted runs persisted — `tools` as
128
+ * resolved Tool objects (`[{ name: "bash" }, …]`).
129
+ *
130
+ * Tool entries are reduced to their string name (string entries kept as-is,
131
+ * object entries via `.name`); non-string host functions are dropped; `persona`
132
+ * is coerced to a boolean. Total and never-throwing: a malformed/absent blob
133
+ * yields empty arrays.
134
+ *
135
+ * This is THE single normalization both the resume path
136
+ * ({@link WorkflowRunManager.resume}, which re-grants the recovered tools) and
137
+ * the consent gate ({@link manifestGrantsSideEffects}) must share, so the gate
138
+ * cannot decide "no side effects" on a blob that `resume()` would in fact grant
139
+ * side-effecting tools from.
140
+ */
141
+ export function normalizeCapabilityManifest(
142
+ stored: unknown,
143
+ ): CapabilityManifest {
144
+ const obj =
145
+ stored && typeof stored === "object"
146
+ ? (stored as Record<string, unknown>)
147
+ : {};
148
+ const tools = Array.isArray(obj.tools)
149
+ ? obj.tools
150
+ .map((t) =>
151
+ typeof t === "string"
152
+ ? t
153
+ : t && typeof t === "object"
154
+ ? ((t as Record<string, unknown>).name as string | undefined)
155
+ : undefined,
156
+ )
157
+ .filter((n): n is string => typeof n === "string")
158
+ : [];
159
+ const hostFunctions = Array.isArray(obj.hostFunctions)
160
+ ? obj.hostFunctions.filter((n): n is string => typeof n === "string")
161
+ : [];
162
+ return { tools, hostFunctions, persona: obj.persona === true };
163
+ }
164
+
165
+ /**
166
+ * True if a capability manifest grants any side-effecting capability beyond the
167
+ * read-only baseline — i.e. it declares one or more `tools` or `hostFunctions`.
168
+ *
169
+ * The tool executor uses this to force an interactive approval at LAUNCH for
170
+ * such runs (and on RESUME of a stored run). The manifest is the single consent
171
+ * point, but it is authored and declared BY THE MODEL, and a workflow's leaves
172
+ * execute granted tools directly (no per-call permission check) — so without a
173
+ * launch/resume-time gate the model could self-grant `bash`/sends/writes and
174
+ * have leaves run them with no user consent, bypassing the gate those tools hit
175
+ * when the main agent calls them. A read-only run (no declared
176
+ * `tools`/`hostFunctions`) needs no prompt.
177
+ *
178
+ * Runs the SAME {@link normalizeCapabilityManifest} the resume path uses, so it
179
+ * detects grants in BOTH the declared (string names) and older resolved
180
+ * (Tool-object) stored shapes — a strict parse would reject the resolved shape
181
+ * and wrongly report "no grant", letting `resume()` restart side-effecting
182
+ * leaves without approval. Total and never-throwing (the risk-gating path must
183
+ * not throw); a malformed/absent manifest yields "no grant" (false). `persona`
184
+ * is deliberately NOT treated as side-effecting: it grants identity/memory
185
+ * context, not world-mutating tools.
186
+ */
187
+ export function manifestGrantsSideEffects(rawCapabilities: unknown): boolean {
188
+ const m = normalizeCapabilityManifest(rawCapabilities);
189
+ return m.tools.length > 0 || m.hostFunctions.length > 0;
190
+ }
191
+
192
+ /**
193
+ * Whether `caller` may see and control a workflow run. A guardian (the trusted
194
+ * user) owns every run; any other actor owns only runs ORIGINATED by their own
195
+ * conversation (`run.conversationId` is the launching conversation `run_workflow`
196
+ * records). THE single authorization scope shared by the `manage_workflows` tool
197
+ * and the executor's resume-approval gate, so the gate never prompts for — and
198
+ * thereby leaks the existence of — a run the tool would hide as not-found.
199
+ */
200
+ export function callerOwnsWorkflowRun(
201
+ run: { conversationId: string | null },
202
+ caller: { trustClass: TrustClass; conversationId: string },
203
+ ): boolean {
204
+ return (
205
+ caller.trustClass === "guardian" ||
206
+ run.conversationId === caller.conversationId
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Error thrown when a manifest declares a tool that cannot be resolved — either
212
+ * because it does not exist in the tool registry or because it is forbidden.
213
+ */
214
+ export class CapabilityResolutionError extends Error {
215
+ constructor(
216
+ message: string,
217
+ readonly reason: "unknown_tool" | "forbidden_tool" | "host_tool",
218
+ readonly toolName: string,
219
+ ) {
220
+ super(message);
221
+ this.name = "CapabilityResolutionError";
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Result of {@link resolveCapabilities}: the concrete set of tools a leaf may
227
+ * invoke (baseline ∪ declared, minus forbidden), plus the run's host-function
228
+ * and persona grants. The engine/leaf runner hard-denies any invocation whose
229
+ * tool is not in `tools`.
230
+ */
231
+ export interface ResolvedCapabilities {
232
+ /**
233
+ * Resolved tool objects from the registry — the exhaustive allow-set for
234
+ * the run. Reuses the registry's {@link Tool} type so the leaf runner can
235
+ * pass these straight through to the agent loop.
236
+ */
237
+ tools: Tool[];
238
+ /** Host functions the run may invoke. */
239
+ hostFunctions: string[];
240
+ /** Whether the run has persona access. */
241
+ persona: boolean;
242
+ }
243
+
244
+ const FORBIDDEN_SET: ReadonlySet<string> = new Set(WORKFLOW_FORBIDDEN_TOOLS);
245
+
246
+ /**
247
+ * Resolve a read-only baseline name to its TRUSTED CORE implementation — never a
248
+ * workspace (or other non-core) override.
249
+ *
250
+ * The baseline is granted to every workflow leaf WITHOUT a manifest declaration,
251
+ * and {@link manifestGrantsSideEffects} only forces the launch approval for
252
+ * DECLARED tools/host functions. A workspace tool may register under a core name
253
+ * such as `file_read`: the registry stashes the original core tool and installs
254
+ * the workspace replacement under that name. A plain {@link getTool} lookup would
255
+ * then hand that replacement — with arbitrary side-effecting behavior — to every
256
+ * empty-manifest run, with no consent.
257
+ *
258
+ * Core tools have no owner ({@link getToolOwner} returns `undefined`). If any
259
+ * owner holds this name, the live entry is an override, so resolve the stashed
260
+ * original core tool ({@link getCoreToolOverride}) instead — and never the
261
+ * replacement. Returns `undefined` (skipped, not failed) when no trusted core
262
+ * entry exists, matching the baseline's convenience-grant semantics.
263
+ */
264
+ function resolveBaselineTool(name: string): Tool | undefined {
265
+ if (getToolOwner(name)) return getCoreToolOverride(name);
266
+ return getTool(name);
267
+ }
268
+
269
+ /**
270
+ * Resolve a {@link CapabilityManifest} into the concrete capability set a
271
+ * workflow run's leaves are allowed to use.
272
+ *
273
+ * The resolved tool set is the read-only baseline unioned with the manifest's
274
+ * declared tools, with forbidden tools rejected (never silently dropped):
275
+ *
276
+ * - Every declared tool name must exist in the tool registry, else a
277
+ * `CapabilityResolutionError("unknown_tool")` is thrown.
278
+ * - Any declared tool in {@link WORKFLOW_FORBIDDEN_TOOLS} throws a
279
+ * `CapabilityResolutionError("forbidden_tool")`.
280
+ * - The baseline is intersected against the registry too, so a baseline entry
281
+ * that is somehow unregistered is skipped rather than dangling.
282
+ *
283
+ * Because the manifest is the single consent point, the returned `tools` array
284
+ * is the exhaustive allow-set: the leaf runner hard-denies anything outside it
285
+ * and performs no per-call permission prompts inside the run.
286
+ */
287
+ export function resolveCapabilities(
288
+ manifest: CapabilityManifest,
289
+ ): ResolvedCapabilities {
290
+ // Reject forbidden declarations up front — even though forbidden tools are
291
+ // stripped from the final union, an explicit declaration is a manifest
292
+ // authoring error and must surface loudly rather than be silently ignored.
293
+ for (const name of manifest.tools) {
294
+ if (FORBIDDEN_SET.has(name)) {
295
+ throw new CapabilityResolutionError(
296
+ `Tool "${name}" is forbidden in workflow runs and cannot be declared.`,
297
+ "forbidden_tool",
298
+ name,
299
+ );
300
+ }
301
+ }
302
+
303
+ const resolved = new Map<string, Tool>();
304
+
305
+ // Baseline first, resolved from the TRUSTED CORE implementation only (a
306
+ // workspace override of a core name must NOT be auto-granted without consent —
307
+ // see resolveBaselineTool). A baseline name with no trusted core entry
308
+ // (unexpected, but possible if a core tool is renamed or a workspace tool
309
+ // shadows it with no stashed original) is skipped — the baseline is a
310
+ // convenience grant, not a declaration, so a missing entry should not fail
311
+ // the run. Forbidden filtering still applies for defense in depth.
312
+ for (const name of WORKFLOW_READONLY_BASELINE) {
313
+ if (FORBIDDEN_SET.has(name)) continue;
314
+ // Defense in depth: the baseline is auto-granted with NO launch approval, so
315
+ // it must never carry a side-effecting tool (e.g. web_fetch, whose URL can
316
+ // exfiltrate read data or trigger external actions). isSideEffectTool is the
317
+ // single source of truth; skip any baseline entry it flags so the no-consent
318
+ // grant can never include a side effect, even if the list above drifts. A
319
+ // run that needs such a tool must DECLARE it (forcing the launch approval).
320
+ if (isSideEffectTool(name)) continue;
321
+ const tool = resolveBaselineTool(name);
322
+ if (tool) resolved.set(name, tool);
323
+ }
324
+
325
+ // Declared tools must exist — a missing one is an authoring error.
326
+ for (const name of manifest.tools) {
327
+ const tool = getTool(name);
328
+ if (!tool) {
329
+ throw new CapabilityResolutionError(
330
+ `Tool "${name}" declared in the workflow manifest does not exist in the tool registry.`,
331
+ "unknown_tool",
332
+ name,
333
+ );
334
+ }
335
+ // Host-proxy tools run on the user's machine (or fall back to in-container
336
+ // execution) and depend on originating-turn context — `transportInterface`,
337
+ // `sourceActorPrincipalId`, the proxy resolver — that a leaf's synthetic,
338
+ // anonymous ToolContext does not carry. Granting one to an unattended,
339
+ // fanned-out leaf would perform host side effects in the wrong environment
340
+ // or without the authenticated-identity binding the proxy requires. Reject
341
+ // loudly at the consent point rather than silently mis-route at run time.
342
+ if (resolveExecutionTarget(tool) === "host") {
343
+ throw new CapabilityResolutionError(
344
+ `Tool "${name}" runs on the host (executionTarget "host") and cannot ` +
345
+ `be granted to a workflow leaf, which runs unattended without the ` +
346
+ `originating turn's host-routing context.`,
347
+ "host_tool",
348
+ name,
349
+ );
350
+ }
351
+ resolved.set(name, tool);
352
+ }
353
+
354
+ return {
355
+ tools: Array.from(resolved.values()),
356
+ hostFunctions: [...manifest.hostFunctions],
357
+ persona: manifest.persona,
358
+ };
359
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Deterministic JSON stringify with recursively sorted object keys.
3
+ *
4
+ * Used by the workflow engine and sandbox so marshalled values that feed the
5
+ * resume hash (`call_hash = sha256(deterministicStringify({ prompt, opts }))`)
6
+ * and host-call results are stable regardless of insertion order. `undefined`
7
+ * round-trips to the string `"null"` (top-level `JSON.stringify(undefined)` is
8
+ * itself `undefined`, which the `?? "null"` fallback maps to `"null"`); JSON
9
+ * has no `undefined`.
10
+ *
11
+ * The byte-for-byte output of this function is load-bearing: it feeds the
12
+ * resume hash, so any change to its serialization would silently invalidate
13
+ * every existing journal's cached prefix. Do not "improve" it.
14
+ */
15
+ export function deterministicStringify(value: unknown): string {
16
+ return JSON.stringify(sortValue(value)) ?? "null";
17
+ }
18
+
19
+ function sortValue(value: unknown): unknown {
20
+ if (value === null || typeof value !== "object") return value;
21
+ if (Array.isArray(value)) return value.map(sortValue);
22
+ const out: Record<string, unknown> = {};
23
+ for (const key of Object.keys(value as Record<string, unknown>).sort()) {
24
+ out[key] = sortValue((value as Record<string, unknown>)[key]);
25
+ }
26
+ return out;
27
+ }