@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
@@ -89,6 +89,8 @@ function makeTarget(): Conversation {
89
89
  getTurnChannelContext: () => null,
90
90
  getTurnInterfaceContext: () => null,
91
91
  drainQueue: async () => {},
92
+ // Pre-run auto-compaction gate — no-op for these tests.
93
+ maybeCompact: async () => null,
92
94
  };
93
95
  return target as unknown as Conversation;
94
96
  }
@@ -101,6 +101,8 @@ function makeTarget(): {
101
101
  getTurnChannelContext: () => null,
102
102
  getTurnInterfaceContext: () => null,
103
103
  drainQueue: async () => {},
104
+ // Pre-run auto-compaction gate — no-op for these tests.
105
+ maybeCompact: async () => null,
104
106
  };
105
107
  return { target: target as unknown as Conversation, runArgs };
106
108
  }
@@ -159,6 +161,81 @@ describe("wakeAgentForOpportunity — overrideProfile forwarding", () => {
159
161
  expect(runArgs[0]!.options?.requestId).toBe("wake:scheduler");
160
162
  });
161
163
 
164
+ test("forceOverrideProfile replaces the pinned-profile lookup and forwards the force flag to agentLoop.run", async () => {
165
+ // The conversation's own pinned profile must be ignored — the caller's
166
+ // forced profile (e.g. a fork-based retrospective matching the source
167
+ // conversation) takes its place AND floats above the call-site layers
168
+ // via the resolver's `forceOverrideProfile` escape hatch.
169
+ mockOverrideProfile = "pinned-ignored";
170
+ mockLlmConfig = LLMSchema.parse({
171
+ default: {
172
+ provider: "anthropic",
173
+ model: "claude-sonnet-4-6",
174
+ maxTokens: 64000,
175
+ contextWindow: { maxInputTokens: 200000 },
176
+ },
177
+ profiles: {
178
+ forced: {
179
+ contextWindow: { maxInputTokens: 120000 },
180
+ },
181
+ },
182
+ callSites: {
183
+ memoryRetrospective: { contextWindow: { maxInputTokens: 180000 } },
184
+ },
185
+ }) as Record<string, unknown>;
186
+ const { target, runArgs } = makeTarget();
187
+
188
+ const result = await wakeAgentForOpportunity(
189
+ {
190
+ conversationId: target.conversationId,
191
+ hint: "test hint",
192
+ source: "memory-retrospective",
193
+ callSite: "memoryRetrospective",
194
+ forceOverrideProfile: "forced",
195
+ },
196
+ { resolveTarget: async () => target },
197
+ );
198
+
199
+ expect(result.invoked).toBe(true);
200
+ expect(runArgs).toHaveLength(1);
201
+ expect(runArgs[0]!.options?.overrideProfile).toBe("forced");
202
+ expect(runArgs[0]!.options?.forceOverrideProfile).toBe(true);
203
+ expect(runArgs[0]!.options?.callSite).toBe("memoryRetrospective");
204
+ // The effective context window resolves under the FORCED profile, above
205
+ // the explicit call-site override (120k beats the call site's 180k).
206
+ expect(runArgs[0]!.options?.resolveContextWindow?.().maxInputTokens).toBe(
207
+ 120000,
208
+ );
209
+ });
210
+
211
+ test("without forceOverrideProfile the wake never sets the force flag", async () => {
212
+ mockOverrideProfile = "frontier";
213
+ mockLlmConfig = LLMSchema.parse({
214
+ default: {
215
+ provider: "anthropic",
216
+ model: "claude-sonnet-4-6",
217
+ maxTokens: 64000,
218
+ contextWindow: { maxInputTokens: 200000 },
219
+ },
220
+ profiles: { frontier: {} },
221
+ callSites: { mainAgent: {} },
222
+ }) as Record<string, unknown>;
223
+ const { target, runArgs } = makeTarget();
224
+
225
+ await wakeAgentForOpportunity(
226
+ {
227
+ conversationId: target.conversationId,
228
+ hint: "test hint",
229
+ source: "scheduler",
230
+ },
231
+ { resolveTarget: async () => target },
232
+ );
233
+
234
+ expect(runArgs).toHaveLength(1);
235
+ expect(runArgs[0]!.options?.overrideProfile).toBe("frontier");
236
+ expect(runArgs[0]!.options?.forceOverrideProfile).toBe(false);
237
+ });
238
+
162
239
  test("passes undefined overrideProfile when the conversation has no pinned profile, but still forwards mainAgent callSite", async () => {
163
240
  mockOverrideProfile = undefined;
164
241
  const { target, runArgs } = makeTarget();
@@ -450,6 +450,12 @@ describe("package-resolver", () => {
450
450
  expect(ALLOWED_PACKAGES).toContain("lodash-es");
451
451
  expect(ALLOWED_PACKAGES).toContain("zod");
452
452
  expect(ALLOWED_PACKAGES).toContain("clsx");
453
- expect(ALLOWED_PACKAGES).toContain("lucide");
453
+ });
454
+
455
+ // `lucide` (the vanilla package) unpacks to ~29 MB, far above the resolver's
456
+ // size cap, so it is installed then deleted and never resolves. It also
457
+ // exports icon data arrays, not Preact components. Apps use inline SVG.
458
+ test("ALLOWED_PACKAGES excludes lucide", () => {
459
+ expect(ALLOWED_PACKAGES).not.toContain("lucide");
454
460
  });
455
461
  });
@@ -20,6 +20,7 @@ const ALLOWLIST = new Set([
20
20
  "assistant/src/memory/app-git-service.ts", // uses getAppsDir for git repo root, not per-app paths
21
21
  "assistant/src/daemon/app-source-watcher.ts", // uses getAppsDir for recursive fs.watch root, not per-app paths
22
22
  "assistant/src/tools/filesystem/write.ts", // uses getAppsDir as an exemption root for the artifact-HTML guard, not per-app paths
23
+ "assistant/src/memory/preloaded-apps.ts", // seeds fixed-slug apps (id === dirName by construction) and writes the definition JSON, which app-store has no fixed-id helper for
23
24
  ]);
24
25
 
25
26
  function isTestFile(filePath: string): boolean {
@@ -37,12 +38,15 @@ function isMigrationFile(filePath: string): boolean {
37
38
 
38
39
  describe("app directory path construction guard", () => {
39
40
  test("no non-allowlisted production files import getAppsDir", () => {
40
- // Search for files that import getAppsDir (not just mention it in comments)
41
+ // Match files that import getAppsDir or call it, but not files that only
42
+ // mention it in a comment (a doc comment referencing the helper by name is
43
+ // not a usage). `-n` yields `file:line:content` so comment-only lines can
44
+ // be filtered out before collapsing to the set of matching files.
41
45
  const pattern = "import.*getAppsDir.*from|getAppsDir\\(\\)";
42
46
 
43
47
  let grepOutput = "";
44
48
  try {
45
- grepOutput = execSync(`git grep -lE '${pattern}' -- '*.ts'`, {
49
+ grepOutput = execSync(`git grep -nE '${pattern}' -- '*.ts'`, {
46
50
  encoding: "utf-8",
47
51
  cwd: process.cwd() + "/..",
48
52
  }).trim();
@@ -54,7 +58,27 @@ describe("app directory path construction guard", () => {
54
58
  throw err;
55
59
  }
56
60
 
57
- const files = grepOutput.split("\n").filter((f) => f.length > 0);
61
+ const isCommentLine = (line: string): boolean => {
62
+ const trimmed = line.trim();
63
+ return (
64
+ trimmed.startsWith("//") ||
65
+ trimmed.startsWith("*") ||
66
+ trimmed.startsWith("/*")
67
+ );
68
+ };
69
+ const files = Array.from(
70
+ new Set(
71
+ grepOutput
72
+ .split("\n")
73
+ .filter((line) => line.length > 0)
74
+ .map((line) => {
75
+ const [file, , ...rest] = line.split(":");
76
+ return { file, content: rest.join(":") };
77
+ })
78
+ .filter(({ content }) => !isCommentLine(content))
79
+ .map(({ file }) => file),
80
+ ),
81
+ );
58
82
  const violations = files.filter((f) => {
59
83
  if (isTestFile(f)) return false;
60
84
  if (isMigrationFile(f)) return false;
@@ -168,6 +168,49 @@ describe("executeAppCreate", () => {
168
168
  expect(parsed.next_steps).toContain("app_refresh");
169
169
  });
170
170
 
171
+ test("associates the new app with its conversation when a conversationId is given", async () => {
172
+ const app = makeMultifileApp({ id: "app-xyz", name: "Assoc App" });
173
+ const associated: Array<{ appId: string; conversationId: string }> = [];
174
+ const store: AppStore = {
175
+ ...mockStore(app, {}),
176
+ addAppConversationId: (appId, conversationId) => {
177
+ associated.push({ appId, conversationId });
178
+ return true;
179
+ },
180
+ };
181
+
182
+ const result = await executeAppCreate(
183
+ { name: "Assoc App" },
184
+ store,
185
+ undefined,
186
+ "conv-assoc-1",
187
+ );
188
+
189
+ expect(result.isError).toBe(false);
190
+ expect(associated).toEqual([
191
+ { appId: "app-xyz", conversationId: "conv-assoc-1" },
192
+ ]);
193
+ });
194
+
195
+ test("a failed conversation association does not fail the create", async () => {
196
+ const app = makeMultifileApp({ id: "app-throw", name: "Throw App" });
197
+ const store: AppStore = {
198
+ ...mockStore(app, {}),
199
+ addAppConversationId: () => {
200
+ throw new Error("disk gone");
201
+ },
202
+ };
203
+
204
+ const result = await executeAppCreate(
205
+ { name: "Throw App" },
206
+ store,
207
+ undefined,
208
+ "conv-assoc-2",
209
+ );
210
+
211
+ expect(result.isError).toBe(false);
212
+ });
213
+
171
214
  test("skips auto_open on scaffold even when proxy resolver is available", async () => {
172
215
  const files: Record<string, string> = {};
173
216
  const app = makeMultifileApp({ name: "New App" });
@@ -40,11 +40,6 @@ mock.module("../util/logger.js", () => ({
40
40
  getLogger: () => makeLoggerStub(),
41
41
  }));
42
42
 
43
- mock.module("../memory/guardian-action-store.js", () => ({
44
- getGuardianActionRequest: () => null,
45
- resolveGuardianActionRequest: () => {},
46
- }));
47
-
48
43
  mock.module("../providers/registry.js", () => ({
49
44
  getProvider: () => ({ name: "mock-provider" }),
50
45
  initializeProviders: async () => {},
@@ -8,6 +8,7 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
8
8
 
9
9
  import type { Conversation } from "../daemon/conversation.js";
10
10
  import type { ServerMessage } from "../daemon/message-protocol.js";
11
+ import type { SecretPromptResult } from "../permissions/secret-prompter.js";
11
12
 
12
13
  mock.module("../util/logger.js", () => ({
13
14
  getLogger: () =>
@@ -196,6 +197,7 @@ function makeIdleSession(opts?: {
196
197
  pendingInteractions.resolve(requestId);
197
198
  opts?.onConfirmation?.(requestId, decision);
198
199
  },
200
+ hasPendingSecret: () => true,
199
201
  handleSecretResponse: (
200
202
  requestId: string,
201
203
  value?: string,
@@ -593,6 +595,54 @@ describe("standalone approval endpoints — HTTP layer", () => {
593
595
  await stopServer();
594
596
  });
595
597
 
598
+ test("resolves a conversation-less secret request via its resolver", async () => {
599
+ /**
600
+ * The CLI `credentials prompt` command registers a conversation-less
601
+ * secret interaction whose resolver lives in pendingInteractions. POST
602
+ * /v1/secret must drive that resolver directly — with no owning
603
+ * conversation — so the standalone prompt completes instead of only
604
+ * timing out.
605
+ */
606
+ // GIVEN a running server
607
+ await startServer(() => makeIdleSession());
608
+
609
+ // AND a conversation-less secret interaction whose resolver is captured
610
+ let resolved: SecretPromptResult | undefined;
611
+ pendingInteractions.register("standalone-secret-1", {
612
+ kind: "secret",
613
+ rpcResolve: (value: unknown) => {
614
+ resolved = value as SecretPromptResult;
615
+ },
616
+ });
617
+
618
+ // WHEN the secret is submitted for that requestId
619
+ const res = await fetch(url("secret"), {
620
+ method: "POST",
621
+ headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
622
+ body: JSON.stringify({
623
+ requestId: "standalone-secret-1",
624
+ value: "cli-secret-value",
625
+ delivery: "store",
626
+ }),
627
+ });
628
+ const body = (await res.json()) as { accepted: boolean };
629
+
630
+ // THEN the request is accepted
631
+ expect(res.status).toBe(200);
632
+ expect(body.accepted).toBe(true);
633
+
634
+ // AND the standalone resolver receives the value and delivery
635
+ expect(resolved).toEqual({
636
+ value: "cli-secret-value",
637
+ delivery: "store",
638
+ });
639
+
640
+ // AND the interaction is removed after resolution
641
+ expect(pendingInteractions.get("standalone-secret-1")).toBeUndefined();
642
+
643
+ await stopServer();
644
+ });
645
+
596
646
  test("returns 404 for unknown requestId", async () => {
597
647
  await startServer(() => makeIdleSession());
598
648
 
@@ -638,6 +688,47 @@ describe("standalone approval endpoints — HTTP layer", () => {
638
688
 
639
689
  await stopServer();
640
690
  });
691
+
692
+ test("rejects a non-secret requestId without consuming it", async () => {
693
+ /**
694
+ * /v1/secret only settles secret prompts. A confirmation (or any other
695
+ * interaction kind) posted here from stale client state must be rejected
696
+ * so its real approval endpoint still finds an intact pending interaction
697
+ * rather than one consumed and resolved with a SecretPromptResult.
698
+ */
699
+ // GIVEN a running server
700
+ await startServer(() => makeIdleSession());
701
+
702
+ // AND a pending confirmation interaction whose resolver would be corrupted
703
+ let resolverCalled = false;
704
+ pendingInteractions.register("confirm-not-secret", {
705
+ conversationId: "conv-1",
706
+ kind: "confirmation",
707
+ rpcResolve: () => {
708
+ resolverCalled = true;
709
+ },
710
+ });
711
+
712
+ // WHEN that confirmation's requestId is posted to /v1/secret
713
+ const res = await fetch(url("secret"), {
714
+ method: "POST",
715
+ headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
716
+ body: JSON.stringify({
717
+ requestId: "confirm-not-secret",
718
+ value: "leaked",
719
+ delivery: "store",
720
+ }),
721
+ });
722
+
723
+ // THEN the request is rejected
724
+ expect(res.status).toBe(404);
725
+
726
+ // AND the confirmation interaction is left intact and unresolved
727
+ expect(pendingInteractions.get("confirm-not-secret")).toBeDefined();
728
+ expect(resolverCalled).toBe(false);
729
+
730
+ await stopServer();
731
+ });
641
732
  });
642
733
 
643
734
  // ── POST /v1/trust-rules ─────────────────────────────────────────────
@@ -1,3 +1,5 @@
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
1
3
  import { beforeEach, describe, expect, test } from "bun:test";
2
4
 
3
5
  import type { AssistantEvent } from "../runtime/assistant-event.js";
@@ -8,6 +10,7 @@ import type {
8
10
  import {
9
11
  _peekStreamForTesting,
10
12
  _resetStreamStateForTesting,
13
+ _simulateRestartForTesting,
11
14
  getCurrentSeq,
12
15
  getPersistedSeq,
13
16
  getReplayWindow,
@@ -642,4 +645,108 @@ describe("assistant-stream-state", () => {
642
645
  expect(getPersistedSeq("conv_1")).toBeNull();
643
646
  });
644
647
  });
648
+
649
+ describe("seq persistence across restarts", () => {
650
+ test("counter resumes above the persisted reservation after a restart", () => {
651
+ // GIVEN a process that stamped events (reserving a seq block on disk)
652
+ const a = mkEvent();
653
+ stampAndBuffer(a);
654
+ expect(a.seq).toBe(1);
655
+
656
+ // WHEN the daemon restarts
657
+ _simulateRestartForTesting();
658
+
659
+ // THEN the next stamp resumes above the reserved block instead of 1,
660
+ // so clients never observe the counter moving backwards.
661
+ const b = mkEvent();
662
+ stampAndBuffer(b);
663
+ expect(b.seq).toBe(1025);
664
+ });
665
+
666
+ test("repeated restarts keep advancing monotonically", () => {
667
+ stampAndBuffer(mkEvent());
668
+ _simulateRestartForTesting();
669
+ const a = mkEvent();
670
+ stampAndBuffer(a);
671
+ _simulateRestartForTesting();
672
+ const b = mkEvent();
673
+ stampAndBuffer(b);
674
+ expect(a.seq).toBe(1025);
675
+ expect(b.seq).toBe(2049);
676
+ });
677
+
678
+ test("stamping within a reserved block does not advance the persisted ceiling", () => {
679
+ // GIVEN many stamps within one block
680
+ for (let i = 0; i < 100; i++) stampAndBuffer(mkEvent());
681
+
682
+ // WHEN the daemon restarts
683
+ _simulateRestartForTesting();
684
+
685
+ // THEN the resume point is the block ceiling, not per-event state.
686
+ const a = mkEvent();
687
+ stampAndBuffer(a);
688
+ expect(a.seq).toBe(1025);
689
+ });
690
+
691
+ test("a corrupt reservation file degrades to a cold start", () => {
692
+ const path = join(
693
+ process.env.VELLUM_WORKSPACE_DIR!,
694
+ "data",
695
+ "stream-seq.json",
696
+ );
697
+ mkdirSync(dirname(path), { recursive: true });
698
+ writeFileSync(path, "not json");
699
+
700
+ _simulateRestartForTesting();
701
+ const a = mkEvent();
702
+ stampAndBuffer(a);
703
+ expect(a.seq).toBe(1);
704
+ });
705
+
706
+ test("a pre-restart cursor replays the post-restart events (reservation gap is not an eviction)", () => {
707
+ // GIVEN a client that saw seq 1 before the daemon restarted
708
+ stampAndBuffer(mkEvent());
709
+ _simulateRestartForTesting();
710
+
711
+ // AND the restarted daemon has buffered new events
712
+ const a = mkEvent();
713
+ const b = mkEvent();
714
+ stampAndBuffer(a);
715
+ stampAndBuffer(b);
716
+ expect(a.seq).toBe(1025);
717
+
718
+ // WHEN the client reconnects with its pre-restart cursor
719
+ const window = getReplayWindow(1);
720
+
721
+ // THEN the post-restart events are replayed rather than treated as
722
+ // an out-of-window gap (no events below the reservation ever existed
723
+ // in this process, so nothing replayable is missing).
724
+ expect(window).not.toBeNull();
725
+ expect(window?.map((e) => e.seq)).toEqual([1025, 1026]);
726
+ });
727
+
728
+ test("a pre-restart cursor still gets the snapshot fallback once post-restart events evict", () => {
729
+ stampAndBuffer(mkEvent());
730
+ _simulateRestartForTesting();
731
+
732
+ // Stamp past the 200-event ring cap so the restarted process's
733
+ // earliest events are genuinely evicted.
734
+ for (let i = 0; i < 205; i++) stampAndBuffer(mkEvent());
735
+
736
+ // The gap now includes evicted post-restart events, so replay must
737
+ // signal the snapshot fallback.
738
+ expect(getReplayWindow(1)).toBeNull();
739
+ });
740
+
741
+ test("a missing reservation file is a cold start at 1", () => {
742
+ rmSync(
743
+ join(process.env.VELLUM_WORKSPACE_DIR!, "data", "stream-seq.json"),
744
+ { force: true },
745
+ );
746
+ _simulateRestartForTesting();
747
+ const a = mkEvent();
748
+ stampAndBuffer(a);
749
+ expect(a.seq).toBe(1);
750
+ });
751
+ });
645
752
  });
@@ -319,7 +319,7 @@ describe("executeBrowserFillCredential", () => {
319
319
  );
320
320
  expect(result.isError).toBe(true);
321
321
  expect(result.content).toContain("No credential stored for slack/api_key");
322
- expect(result.content).toContain("credential_store");
322
+ expect(result.content).toContain("assistant credentials set");
323
323
  // The broker short-circuits before DOM.focus is dispatched.
324
324
  const methods = sendCalls.map((c) => c.method);
325
325
  expect(methods).not.toContain("DOM.focus");
@@ -335,7 +335,7 @@ describe("executeBrowserFillCredential", () => {
335
335
  );
336
336
  expect(result.isError).toBe(true);
337
337
  expect(result.content).toContain("No credential stored for slack/api_key");
338
- expect(result.content).toContain("credential_store");
338
+ expect(result.content).toContain("assistant credentials set");
339
339
  const methods = sendCalls.map((c) => c.method);
340
340
  expect(methods).not.toContain("Input.insertText");
341
341
  });
@@ -465,7 +465,7 @@ describe("executeBrowserFillCredential", () => {
465
465
  expect(result.isError).toBe(true);
466
466
  expect(result.content).toContain("Policy denied");
467
467
  expect(result.content).toContain("not allowed to use credential");
468
- expect(result.content).toContain("credential_store");
468
+ expect(result.content).toContain("assistant credentials set");
469
469
  // The broker short-circuits before Input.insertText fires.
470
470
  const methods = sendCalls.map((c) => c.method);
471
471
  expect(methods).not.toContain("Input.insertText");
@@ -179,7 +179,7 @@ describe("bundled skill retrieval guard", () => {
179
179
  if (violations.length > 0) {
180
180
  const message = [
181
181
  "Direct credential lookup instructions were found in skills.",
182
- "Use credential_store and CLI/proxied flows instead.",
182
+ "Use the `assistant credentials` CLI and proxied flows instead.",
183
183
  "",
184
184
  "Violations:",
185
185
  ...violations.map((v) => ` - ${v}`),
@@ -41,11 +41,6 @@ mock.module("../util/logger.js", () => ({
41
41
  getLogger: () => makeLoggerStub(),
42
42
  }));
43
43
 
44
- mock.module("../memory/guardian-action-store.js", () => ({
45
- getGuardianActionRequest: () => null,
46
- resolveGuardianActionRequest: () => {},
47
- }));
48
-
49
44
  mock.module("../providers/registry.js", () => ({
50
45
  getProvider: () => ({ name: "mock-provider" }),
51
46
  initializeProviders: async () => {},
@@ -79,7 +74,14 @@ mock.module("../config/loader.js", () => ({
79
74
  },
80
75
  },
81
76
  profiles: {},
82
- callSites: {},
77
+ callSites: {
78
+ // Resolves a SMALLER window than mainAgent (which inherits
79
+ // llm.default's 100000) — exercised by the maybeCompact gate-sizing
80
+ // tests below.
81
+ memoryRetrospective: {
82
+ contextWindow: { maxInputTokens: 50000 },
83
+ },
84
+ },
83
85
  pricingOverrides: [],
84
86
  },
85
87
  rateLimit: { maxRequestsPerMinute: 0 },
@@ -187,11 +189,18 @@ let mockCompactResult: ContextWindowResult = {
187
189
  summaryText: "",
188
190
  };
189
191
 
192
+ // Config payloads handed to the manager's updateConfig — runCompaction calls
193
+ // it with the (possibly sizing-threaded) resolved context-window config
194
+ // before delegating to the manager.
195
+ const updateConfigCalls: Array<{ maxInputTokens?: number }> = [];
196
+
190
197
  mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
191
198
  ContextWindowManager: class {
192
199
  nonPersistedPrefixCount = 0;
193
200
  constructor() {}
194
- updateConfig() {}
201
+ updateConfig(cfg: { maxInputTokens?: number }) {
202
+ updateConfigCalls.push(cfg);
203
+ }
195
204
  shouldCompact() {
196
205
  return { needed: false, estimatedTokens: 0 };
197
206
  }
@@ -433,6 +442,53 @@ describe("forceCompact event emission", () => {
433
442
  });
434
443
  });
435
444
 
445
+ describe("maybeCompact gate sizing", () => {
446
+ test("default sizing resolves mainAgent's window; wake sizing resolves the wake's call-site window", async () => {
447
+ // The auto-threshold gate derives its trip point from the
448
+ // context-window config pushed via updateConfig. Sized against
449
+ // mainAgent (100k here), a wake whose call site resolves a smaller
450
+ // window (memoryRetrospective → 50k) would pass the gate un-compacted
451
+ // and then overflow at the provider — threading the sizing makes the
452
+ // gate see the 50k window instead.
453
+ mockCompactResult = {
454
+ messages: [],
455
+ compacted: false,
456
+ previousEstimatedInputTokens: 0,
457
+ estimatedInputTokens: 0,
458
+ maxInputTokens: 0,
459
+ thresholdTokens: 0,
460
+ compactedMessages: 0,
461
+ compactedPersistedMessages: 0,
462
+ summaryCalls: 0,
463
+ summaryInputTokens: 0,
464
+ summaryOutputTokens: 0,
465
+ summaryModel: "",
466
+ summaryText: "",
467
+ };
468
+ const conversation = makeConversation([], "conv-compact-sizing");
469
+
470
+ updateConfigCalls.length = 0;
471
+ await conversation.maybeCompact();
472
+ await conversation.maybeCompact({ callSite: "memoryRetrospective" });
473
+
474
+ expect(updateConfigCalls.map((cfg) => cfg.maxInputTokens)).toEqual([
475
+ 100000, 50000,
476
+ ]);
477
+ });
478
+
479
+ test("forceCompact keeps mainAgent sizing", async () => {
480
+ mockCompactResult = { ...mockCompactResult, compacted: false };
481
+ const conversation = makeConversation([], "conv-compact-force-sizing");
482
+
483
+ updateConfigCalls.length = 0;
484
+ await conversation.forceCompact();
485
+
486
+ expect(updateConfigCalls.map((cfg) => cfg.maxInputTokens)).toEqual([
487
+ 100000,
488
+ ]);
489
+ });
490
+ });
491
+
436
492
  // ---------------------------------------------------------------------------
437
493
  // computeSummaryQualitySignals — imported lazily after mocks are installed so
438
494
  // the logger stub and other module replacements take effect first.