@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.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 (430) hide show
  1. package/bun.lock +0 -20
  2. package/docs/workspace-tools.md +33 -42
  3. package/eslint-rules/cli-no-daemon-internals.js +0 -6
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
  5. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
  6. package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
  7. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
  8. package/node_modules/@vellumai/service-contracts/package.json +0 -1
  9. package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
  10. package/openapi.yaml +0 -155
  11. package/package.json +1 -4
  12. package/scripts/test.sh +15 -36
  13. package/src/__tests__/actor-token-service.test.ts +14 -36
  14. package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
  15. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
  16. package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
  17. package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
  18. package/src/__tests__/annotate-risk-options.test.ts +0 -2
  19. package/src/__tests__/approval-cascade.test.ts +0 -2
  20. package/src/__tests__/assistant-attachments.test.ts +0 -42
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  22. package/src/__tests__/btw-routes.test.ts +0 -2
  23. package/src/__tests__/build-persisted-content.test.ts +0 -2
  24. package/src/__tests__/call-controller.test.ts +0 -19
  25. package/src/__tests__/channel-guardian.test.ts +58 -94
  26. package/src/__tests__/channel-reply-delivery.test.ts +0 -2
  27. package/src/__tests__/compaction-events.test.ts +0 -2
  28. package/src/__tests__/compaction.benchmark.test.ts +0 -2
  29. package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
  30. package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
  31. package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
  32. package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
  33. package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
  34. package/src/__tests__/config-loader-backfill.test.ts +10 -123
  35. package/src/__tests__/config-schema.test.ts +0 -1
  36. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
  37. package/src/__tests__/contacts-relay-reads.test.ts +15 -13
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
  39. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  42. package/src/__tests__/conversation-agent-loop.test.ts +0 -134
  43. package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
  44. package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
  45. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
  46. package/src/__tests__/conversation-history-web-search.test.ts +0 -2
  47. package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
  48. package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
  49. package/src/__tests__/conversation-pairing.test.ts +0 -2
  50. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +0 -2
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
  53. package/src/__tests__/conversation-queue.test.ts +0 -91
  54. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
  56. package/src/__tests__/conversation-slash-queue.test.ts +0 -2
  57. package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
  58. package/src/__tests__/conversation-speed-override.test.ts +0 -2
  59. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
  60. package/src/__tests__/conversation-title-service.test.ts +0 -2
  61. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
  62. package/src/__tests__/conversation-usage.test.ts +0 -2
  63. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
  64. package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
  65. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
  66. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  67. package/src/__tests__/db-migration-rollback.test.ts +171 -205
  68. package/src/__tests__/db-test-helpers.ts +4 -5
  69. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
  70. package/src/__tests__/disk-pressure-guard.test.ts +0 -41
  71. package/src/__tests__/dm-persistence.test.ts +0 -2
  72. package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
  73. package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
  74. package/src/__tests__/exploration-drift-hook.test.ts +2 -3
  75. package/src/__tests__/filing-service.test.ts +0 -2
  76. package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
  77. package/src/__tests__/guardian-dispatch.test.ts +1 -95
  78. package/src/__tests__/guardian-outbound-http.test.ts +0 -13
  79. package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
  80. package/src/__tests__/heartbeat-service.test.ts +0 -2
  81. package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
  82. package/src/__tests__/host-app-control-routes.test.ts +30 -24
  83. package/src/__tests__/host-bash-routes.test.ts +41 -31
  84. package/src/__tests__/host-browser-routes.test.ts +32 -26
  85. package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
  86. package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
  87. package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
  88. package/src/__tests__/http-user-message-parity.test.ts +8 -290
  89. package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
  90. package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
  91. package/src/__tests__/invite-redemption-service.test.ts +0 -198
  92. package/src/__tests__/llm-context-normalization.test.ts +0 -105
  93. package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
  94. package/src/__tests__/llm-usage-store.test.ts +0 -25
  95. package/src/__tests__/mcp-health-check.test.ts +1 -2
  96. package/src/__tests__/media-stream-server-integration.test.ts +0 -127
  97. package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
  98. package/src/__tests__/messaging-send-tool.test.ts +0 -2
  99. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  100. package/src/__tests__/mtime-cache.test.ts +5 -146
  101. package/src/__tests__/native-web-search.test.ts +0 -2
  102. package/src/__tests__/non-member-access-request.test.ts +17 -189
  103. package/src/__tests__/notification-broadcaster.test.ts +0 -4
  104. package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
  105. package/src/__tests__/notification-deep-link.test.ts +0 -6
  106. package/src/__tests__/notification-guardian-path.test.ts +0 -19
  107. package/src/__tests__/openai-provider.test.ts +12 -22
  108. package/src/__tests__/openai-responses-provider.test.ts +2 -12
  109. package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
  110. package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
  111. package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
  112. package/src/__tests__/plugin-bootstrap.test.ts +73 -3
  113. package/src/__tests__/plugin-route-contribution.test.ts +17 -4
  114. package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
  115. package/src/__tests__/plugin-types.test.ts +2 -0
  116. package/src/__tests__/process-message-background-slack.test.ts +0 -2
  117. package/src/__tests__/process-message-display-content.test.ts +0 -2
  118. package/src/__tests__/provider-error-scenarios.test.ts +4 -5
  119. package/src/__tests__/provider-usage-tracking.test.ts +0 -39
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
  121. package/src/__tests__/registry.test.ts +1 -4
  122. package/src/__tests__/relay-server.test.ts +25 -694
  123. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
  124. package/src/__tests__/secret-ingress-http.test.ts +0 -14
  125. package/src/__tests__/send-endpoint-busy.test.ts +8 -30
  126. package/src/__tests__/skills.test.ts +0 -44
  127. package/src/__tests__/slack-inbound-verification.test.ts +2 -47
  128. package/src/__tests__/stt-hints.test.ts +13 -44
  129. package/src/__tests__/subagent-detail.test.ts +0 -27
  130. package/src/__tests__/subagent-disposal.test.ts +0 -65
  131. package/src/__tests__/subagent-notify-parent.test.ts +0 -2
  132. package/src/__tests__/subagent-role-registry.test.ts +2 -7
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
  134. package/src/__tests__/subagent-tools.test.ts +0 -2
  135. package/src/__tests__/suggestion-routes.test.ts +0 -2
  136. package/src/__tests__/title-generate-hook.test.ts +0 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
  138. package/src/__tests__/tool-executor.test.ts +11 -16
  139. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
  140. package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
  141. package/src/__tests__/tool-start-timestamp.test.ts +0 -2
  142. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
  143. package/src/__tests__/twilio-routes.test.ts +0 -96
  144. package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
  145. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  146. package/src/__tests__/voice-invite-redemption.test.ts +0 -33
  147. package/src/__tests__/web-search-backend-failure.test.ts +0 -2
  148. package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
  149. package/src/__tests__/workspace-tool-loader.test.ts +2 -195
  150. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  151. package/src/agent/loop.ts +0 -56
  152. package/src/api/index.ts +1 -19
  153. package/src/api/responses/llm-request-log-entry.ts +0 -29
  154. package/src/api/responses/subagent-detail.ts +0 -17
  155. package/src/api/surfaces.ts +3 -39
  156. package/src/approvals/guardian-request-resolvers.ts +11 -1
  157. package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
  158. package/src/calls/call-domain.ts +3 -3
  159. package/src/calls/guardian-dispatch.ts +8 -10
  160. package/src/calls/inbound-trust-reader.ts +1 -17
  161. package/src/calls/media-stream-server.ts +0 -21
  162. package/src/calls/relay-server.ts +50 -167
  163. package/src/calls/relay-setup-router.ts +7 -37
  164. package/src/calls/relay-verification.ts +4 -4
  165. package/src/calls/stt-hints.ts +12 -9
  166. package/src/calls/twilio-routes.ts +4 -14
  167. package/src/channels/types.ts +20 -10
  168. package/src/cli/commands/__tests__/cache.test.ts +1 -8
  169. package/src/cli/commands/cache.ts +181 -194
  170. package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
  171. package/src/cli/commands/db/status.ts +1 -37
  172. package/src/cli/commands/mcp.ts +218 -252
  173. package/src/cli/commands/memory/index.ts +0 -2
  174. package/src/cli/commands/plugins.ts +3 -75
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
  177. package/src/cli/lib/list-installed-plugins.ts +1 -179
  178. package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
  179. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
  180. package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
  181. package/src/config/bundled-skills/subagent/SKILL.md +1 -1
  182. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  183. package/src/config/feature-flag-registry.json +13 -5
  184. package/src/config/loader.ts +5 -38
  185. package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
  186. package/src/config/schemas/memory-lifecycle.ts +0 -12
  187. package/src/config/schemas/memory-v3.ts +0 -7
  188. package/src/config/schemas/memory.ts +0 -4
  189. package/src/config/schemas/timeouts.ts +0 -8
  190. package/src/config/seed-inference-profiles.ts +11 -21
  191. package/src/config/skills.ts +5 -27
  192. package/src/config/sync-gated-profiles.ts +13 -12
  193. package/src/contacts/contacts-write.ts +0 -3
  194. package/src/daemon/assistant-attachments.ts +4 -27
  195. package/src/daemon/conversation-agent-loop.ts +0 -28
  196. package/src/daemon/conversation-process.ts +16 -35
  197. package/src/daemon/conversation-surfaces.ts +38 -111
  198. package/src/daemon/conversation-tool-setup.ts +16 -50
  199. package/src/daemon/conversation.ts +1 -13
  200. package/src/daemon/disk-pressure-guard.ts +2 -12
  201. package/src/daemon/event-loop-watchdog.ts +1 -28
  202. package/src/daemon/external-plugins-bootstrap.ts +34 -4
  203. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
  204. package/src/daemon/handlers/config-a2a.ts +14 -6
  205. package/src/daemon/handlers/config-channels.ts +22 -78
  206. package/src/daemon/handlers/conversations.ts +0 -77
  207. package/src/daemon/lifecycle.ts +0 -4
  208. package/src/daemon/mcp-reload-service.ts +0 -10
  209. package/src/daemon/memory-v2-startup.test.ts +0 -72
  210. package/src/daemon/memory-v2-startup.ts +19 -87
  211. package/src/daemon/message-types/conversations.ts +0 -2
  212. package/src/daemon/message-types/surfaces.ts +12 -12
  213. package/src/daemon/server.ts +4 -0
  214. package/src/daemon/shutdown-handlers.ts +0 -20
  215. package/src/daemon/tool-setup-types.ts +0 -9
  216. package/src/daemon/workspace-tools-watcher.ts +328 -0
  217. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  218. package/src/ipc/assistant-server.ts +2 -2
  219. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  220. package/src/mcp/client.ts +1 -15
  221. package/src/mcp/mcp-auth-orchestrator.ts +1 -6
  222. package/src/mcp/mcp-oauth-provider.ts +8 -19
  223. package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
  224. package/src/memory/conversation-crud.ts +0 -38
  225. package/src/memory/db-connection.ts +3 -22
  226. package/src/memory/db-init.ts +502 -36
  227. package/src/memory/db-singleton.ts +4 -6
  228. package/src/memory/jobs-worker.ts +0 -58
  229. package/src/memory/llm-request-log-store.ts +1 -26
  230. package/src/memory/llm-usage-store.ts +20 -48
  231. package/src/memory/memory-retrospective-job.ts +8 -9
  232. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
  233. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  234. package/src/memory/migrations/registry.ts +573 -0
  235. package/src/memory/migrations/run-migrations.ts +6 -90
  236. package/src/memory/migrations/validate-migration-state.ts +66 -101
  237. package/src/memory/schema/conversations.ts +0 -9
  238. package/src/memory/schema/infrastructure.ts +0 -20
  239. package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
  240. package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
  241. package/src/memory/v2/cli-command-store.ts +38 -75
  242. package/src/memory/v2/prompts/consolidation.ts +82 -13
  243. package/src/memory/v2/prompts/router.ts +93 -21
  244. package/src/memory/v2/skill-store.ts +31 -68
  245. package/src/notifications/__tests__/broadcaster.test.ts +8 -16
  246. package/src/notifications/__tests__/decision-engine.test.ts +9 -78
  247. package/src/notifications/broadcaster.ts +1 -8
  248. package/src/notifications/decision-engine.ts +7 -15
  249. package/src/notifications/destination-resolver.ts +24 -68
  250. package/src/notifications/emit-signal.ts +14 -39
  251. package/src/permissions/question-prompter.test.ts +1 -1
  252. package/src/permissions/question-prompter.ts +4 -7
  253. package/src/plugin-api/index.ts +6 -6
  254. package/src/plugin-api/types.ts +5 -3
  255. package/src/plugin-api/vision-support.test.ts +4 -28
  256. package/src/plugin-api/vision-support.ts +31 -66
  257. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
  258. package/src/plugins/defaults/advisor/consult.ts +6 -110
  259. package/src/plugins/defaults/advisor/steering.ts +2 -14
  260. package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
  261. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
  262. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
  263. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
  264. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
  265. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
  266. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
  267. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
  268. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
  269. package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
  270. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
  271. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
  272. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
  273. package/src/plugins/mtime-cache.ts +291 -76
  274. package/src/plugins/pipeline.ts +13 -111
  275. package/src/plugins/types.ts +2 -0
  276. package/src/providers/anthropic/client.ts +0 -5
  277. package/src/providers/call-site-routing.ts +0 -4
  278. package/src/providers/model-catalog.ts +0 -16
  279. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  280. package/src/providers/openai/chat-completions-provider.ts +83 -37
  281. package/src/providers/openai/responses-provider.ts +46 -50
  282. package/src/providers/openrouter/client.ts +0 -5
  283. package/src/providers/provider-send-message.ts +0 -4
  284. package/src/providers/ratelimit.ts +0 -4
  285. package/src/providers/retry.ts +0 -4
  286. package/src/providers/types.ts +0 -9
  287. package/src/providers/usage-tracking.ts +0 -4
  288. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
  289. package/src/runtime/access-request-helper.ts +39 -19
  290. package/src/runtime/actor-trust-resolver.ts +2 -2
  291. package/src/runtime/assistant-event-hub.ts +1 -1
  292. package/src/runtime/assistant-stream-state.ts +2 -9
  293. package/src/runtime/auth/require-bound-guardian.ts +11 -21
  294. package/src/runtime/channel-verification-service.ts +31 -56
  295. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  296. package/src/runtime/guardian-vellum-migration.ts +7 -66
  297. package/src/runtime/invite-redemption-service.ts +187 -198
  298. package/src/runtime/local-actor-identity.ts +11 -76
  299. package/src/runtime/pending-interactions.ts +1 -11
  300. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
  301. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  302. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
  303. package/src/runtime/routes/browser-routes.ts +1 -1
  304. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
  305. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  306. package/src/runtime/routes/contact-routes.ts +32 -8
  307. package/src/runtime/routes/conversation-cli-routes.ts +5 -4
  308. package/src/runtime/routes/conversation-list-routes.ts +7 -4
  309. package/src/runtime/routes/conversation-query-routes.ts +0 -72
  310. package/src/runtime/routes/conversation-routes.ts +85 -84
  311. package/src/runtime/routes/events-routes.ts +2 -2
  312. package/src/runtime/routes/global-search-routes.ts +1 -3
  313. package/src/runtime/routes/guardian-action-routes.ts +5 -4
  314. package/src/runtime/routes/host-app-control-routes.ts +4 -5
  315. package/src/runtime/routes/host-bash-routes.ts +4 -5
  316. package/src/runtime/routes/host-browser-routes.ts +11 -9
  317. package/src/runtime/routes/host-cu-routes.ts +4 -5
  318. package/src/runtime/routes/host-file-routes.ts +4 -5
  319. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  320. package/src/runtime/routes/http-adapter.ts +1 -1
  321. package/src/runtime/routes/identity-routes.ts +2 -3
  322. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  323. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
  324. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
  325. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
  326. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  327. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
  328. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
  329. package/src/runtime/routes/index.ts +0 -2
  330. package/src/runtime/routes/llm-context-normalization.ts +0 -83
  331. package/src/runtime/routes/mcp-auth-routes.ts +19 -171
  332. package/src/runtime/routes/migration-rollback-routes.ts +3 -4
  333. package/src/runtime/routes/migration-routes.ts +1 -4
  334. package/src/runtime/routes/subagents-routes.ts +0 -5
  335. package/src/runtime/routes/surface-action-routes.ts +56 -42
  336. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
  337. package/src/runtime/services/conversation-serializer.ts +9 -7
  338. package/src/runtime/tool-grant-request-helper.ts +3 -3
  339. package/src/runtime/trust-verdict-consumer.ts +9 -85
  340. package/src/runtime/verification-outbound-actions.ts +18 -18
  341. package/src/signals/user-message.ts +0 -16
  342. package/src/subagent/manager.ts +0 -9
  343. package/src/subagent/types.ts +3 -3
  344. package/src/telemetry/types.ts +1 -34
  345. package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
  346. package/src/telemetry/usage-telemetry-reporter.ts +3 -87
  347. package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
  348. package/src/tools/ask-question/ask-question-tool.ts +0 -13
  349. package/src/tools/executor.ts +4 -4
  350. package/src/tools/registry.ts +0 -18
  351. package/src/tools/shared/filesystem/path-policy.ts +5 -12
  352. package/src/tools/tool-approval-handler.ts +1 -1
  353. package/src/tools/tool-defaults.ts +2 -9
  354. package/src/tools/tool-manifest.ts +0 -3
  355. package/src/tools/types.ts +2 -17
  356. package/src/tools/workspace-tools/loader.ts +244 -348
  357. package/src/util/errors.ts +1 -26
  358. package/src/util/platform.ts +0 -5
  359. package/src/workflows/library.test.ts +0 -140
  360. package/src/workflows/library.ts +28 -82
  361. package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
  362. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
  363. package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
  364. package/src/workspace/migrations/registry.ts +0 -2
  365. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
  366. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
  367. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
  368. package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
  369. package/src/__tests__/code-search-tool.test.ts +0 -585
  370. package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
  371. package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
  372. package/src/__tests__/plugin-pipeline.test.ts +0 -96
  373. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
  374. package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
  375. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
  376. package/src/agent/loop-exclusive-tool.test.ts +0 -150
  377. package/src/api/constants/sse-replay.ts +0 -41
  378. package/src/api/events/conversation-notice.ts +0 -26
  379. package/src/approvals/guardian-channel-delivery.ts +0 -30
  380. package/src/approvals/guardian-expiry-notifier.ts +0 -148
  381. package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
  382. package/src/cli/commands/memory/worker.ts +0 -175
  383. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
  384. package/src/config/prune-seeded-callsite-defaults.ts +0 -110
  385. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
  386. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
  387. package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
  388. package/src/contacts/guardian-delivery-reader.ts +0 -223
  389. package/src/contacts/member-write-relay.ts +0 -189
  390. package/src/daemon/conversation-notices.ts +0 -60
  391. package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
  392. package/src/hooks/hook-loader.ts +0 -341
  393. package/src/mcp/mcp-header-store.ts +0 -134
  394. package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
  395. package/src/memory/__tests__/prompt-override.test.ts +0 -192
  396. package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
  397. package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
  398. package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
  399. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
  400. package/src/memory/prompt-override.ts +0 -129
  401. package/src/memory/steps.ts +0 -573
  402. package/src/memory/watchdog-events-store.ts +0 -87
  403. package/src/memory/worker-control.ts +0 -118
  404. package/src/memory/worker-process.ts +0 -72
  405. package/src/notifications/__tests__/connected-channels.test.ts +0 -114
  406. package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
  407. package/src/onboarding/checkin-event.test.ts +0 -222
  408. package/src/onboarding/checkin-event.ts +0 -321
  409. package/src/onboarding/schedule-checkin.ts +0 -190
  410. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
  411. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
  412. package/src/plugins/defaults/advisor/context-pack.ts +0 -288
  413. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
  414. package/src/plugins/surface-import.ts +0 -121
  415. package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
  416. package/src/providers/openai/api-error-normalization.ts +0 -270
  417. package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
  418. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
  419. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
  420. package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
  421. package/src/runtime/anchored-guardian.test.ts +0 -156
  422. package/src/runtime/anchored-guardian.ts +0 -135
  423. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
  424. package/src/runtime/local-principal-trust.ts +0 -52
  425. package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
  426. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
  427. package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
  428. package/src/tools/filesystem/search.ts +0 -543
  429. package/src/util/telemetry-db-path.ts +0 -24
  430. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
@@ -1,190 +0,0 @@
1
- /**
2
- * Schedules the onboarding "Day 2 Check-in" calendar event server-side.
3
- *
4
- * Flow:
5
- * 1. Resolve the Google OAuth connection, requiring the calendar.events scope.
6
- * No connection / missing scope → a `scheduled: false` result (never an
7
- * error): the web caller treats this as a best-effort skip.
8
- * 2. List tomorrow's timed events in the 8am–8pm window (user's timezone) to
9
- * derive busy intervals. Uses events.list rather than freeBusy.query
10
- * because the onboarding grant is the narrow `calendar.events` scope, which
11
- * authorizes events.list/insert but not freeBusy.query.
12
- * 3. Choose the first open 15-minute slot (12pm–5pm, widening to 8am–8pm).
13
- * 4. Create the event with the locked title + HTML description, sendUpdates=all.
14
- *
15
- * Authenticates via `resolveOAuthConnection("google")` + `connection.request()`,
16
- * the same path the calendar watcher uses — no skill subprocess.
17
- */
18
-
19
- import { canonicalizeTimeZone } from "../daemon/date-context.js";
20
- import type { OAuthConnection } from "../oauth/connection.js";
21
- import { resolveOAuthConnection } from "../oauth/connection-resolver.js";
22
- import { getLogger } from "../util/logger.js";
23
- import {
24
- buildCheckinDescription,
25
- buildCheckinTitle,
26
- checkinAvailabilityWindow,
27
- type CheckinNames,
28
- chooseCheckinSlot,
29
- extractBusyFromEvents,
30
- type GcalEvent,
31
- } from "./checkin-event.js";
32
-
33
- const log = getLogger("onboarding:schedule-checkin");
34
-
35
- const GOOGLE_CALENDAR_BASE_URL = "https://www.googleapis.com/calendar/v3";
36
- const GOOGLE_CALENDAR_EVENTS_SCOPE =
37
- "https://www.googleapis.com/auth/calendar.events";
38
- /** Calendar shares the Google OAuth connection with Gmail. */
39
- const GOOGLE_PROVIDER = "google";
40
- const PRIMARY_CALENDAR_ID = "primary";
41
-
42
- export interface ScheduleCheckinInput extends CheckinNames {
43
- /** IANA timezone reported by the client (e.g. "America/New_York"). */
44
- timeZone?: string;
45
- /** Override "now" for deterministic tests. Defaults to the current time. */
46
- nowMs?: number;
47
- }
48
-
49
- export type ScheduleCheckinResult =
50
- | {
51
- scheduled: true;
52
- eventId: string;
53
- htmlLink: string | null;
54
- /** Event start, ISO 8601 (UTC). */
55
- start: string;
56
- /** Event end, ISO 8601 (UTC). */
57
- end: string;
58
- timeZone: string;
59
- }
60
- | {
61
- scheduled: false;
62
- /**
63
- * Why nothing was booked. `calendar_unavailable` covers both "not
64
- * connected" and "calendar scope not granted" — the client surfaces a
65
- * single best-effort skip either way.
66
- */
67
- reason: "calendar_unavailable";
68
- };
69
-
70
- interface EventsListResponse {
71
- items?: GcalEvent[];
72
- }
73
-
74
- /** Subset of the events.insert response we surface back to the caller. */
75
- interface CreatedEvent {
76
- id?: string;
77
- htmlLink?: string;
78
- }
79
-
80
- /**
81
- * Schedule the Day 2 check-in. Resolves to a `scheduled: false` result when no
82
- * calendar is connected; throws only on unexpected Calendar API failures (the
83
- * route handler maps those to a 5xx, the web caller swallows them).
84
- */
85
- export async function scheduleOnboardingCheckin(
86
- input: ScheduleCheckinInput,
87
- ): Promise<ScheduleCheckinResult> {
88
- const timeZone =
89
- canonicalizeTimeZone(input.timeZone) ??
90
- // Fall back to the daemon host timezone when the client didn't report one.
91
- Intl.DateTimeFormat().resolvedOptions().timeZone ??
92
- "UTC";
93
- const nowMs = input.nowMs ?? Date.now();
94
-
95
- let connection: OAuthConnection;
96
- try {
97
- connection = await resolveOAuthConnection(GOOGLE_PROVIDER, {
98
- requiredScopes: [GOOGLE_CALENDAR_EVENTS_SCOPE],
99
- });
100
- } catch (err) {
101
- // No active connection or the calendar scope wasn't granted — skip quietly.
102
- log.info(
103
- { err: err instanceof Error ? err.message : String(err) },
104
- "Check-in skipped: Google Calendar not available",
105
- );
106
- return { scheduled: false, reason: "calendar_unavailable" };
107
- }
108
-
109
- const { timeMinMs, timeMaxMs } = checkinAvailabilityWindow(nowMs, timeZone);
110
-
111
- const eventsResp = await connection.request({
112
- method: "GET",
113
- path: `/calendars/${encodeURIComponent(PRIMARY_CALENDAR_ID)}/events`,
114
- query: {
115
- timeMin: new Date(timeMinMs).toISOString(),
116
- timeMax: new Date(timeMaxMs).toISOString(),
117
- // Expand recurring events into instances so each occurrence is a concrete
118
- // busy interval in the window.
119
- singleEvents: "true",
120
- orderBy: "startTime",
121
- maxResults: "250",
122
- },
123
- baseUrl: GOOGLE_CALENDAR_BASE_URL,
124
- headers: { "Content-Type": "application/json" },
125
- });
126
- if (eventsResp.status < 200 || eventsResp.status >= 300) {
127
- throw new Error(
128
- `Calendar events.list ${eventsResp.status}: ${stringifyBody(eventsResp.body)}`,
129
- );
130
- }
131
-
132
- const busy = extractBusyFromEvents(
133
- (eventsResp.body as EventsListResponse).items ?? [],
134
- );
135
- const slot = chooseCheckinSlot(nowMs, timeZone, busy);
136
-
137
- const uuid = crypto.randomUUID();
138
- const eventResp = await connection.request({
139
- method: "POST",
140
- path: `/calendars/${encodeURIComponent(PRIMARY_CALENDAR_ID)}/events`,
141
- query: { sendUpdates: "all" },
142
- baseUrl: GOOGLE_CALENDAR_BASE_URL,
143
- headers: { "Content-Type": "application/json" },
144
- body: {
145
- summary: buildCheckinTitle(input),
146
- description: buildCheckinDescription(uuid),
147
- start: {
148
- dateTime: new Date(slot.startMs).toISOString(),
149
- timeZone,
150
- },
151
- end: {
152
- dateTime: new Date(slot.endMs).toISOString(),
153
- timeZone,
154
- },
155
- },
156
- });
157
- if (eventResp.status < 200 || eventResp.status >= 300) {
158
- throw new Error(
159
- `Calendar event create ${eventResp.status}: ${stringifyBody(eventResp.body)}`,
160
- );
161
- }
162
-
163
- const event = eventResp.body as CreatedEvent;
164
- if (!event.id) {
165
- throw new Error("Calendar event create returned no event id");
166
- }
167
-
168
- log.info(
169
- {
170
- eventId: event.id,
171
- start: new Date(slot.startMs).toISOString(),
172
- window: slot.window,
173
- timeZone,
174
- },
175
- "Scheduled onboarding Day 2 check-in",
176
- );
177
-
178
- return {
179
- scheduled: true,
180
- eventId: event.id,
181
- htmlLink: event.htmlLink ?? null,
182
- start: new Date(slot.startMs).toISOString(),
183
- end: new Date(slot.endMs).toISOString(),
184
- timeZone,
185
- };
186
- }
187
-
188
- function stringifyBody(body: unknown): string {
189
- return typeof body === "string" ? body : JSON.stringify(body ?? "");
190
- }
@@ -1,106 +0,0 @@
1
- /**
2
- * Personal-memory gating for the advisor context pack: NOW.md and PKB must only
3
- * reach the advisor when the turn's trust admits personal memory (and, for
4
- * NOW.md, when the scratchpad-injection toggle is on) — the same policy the
5
- * runtime memory injectors apply. Without it, a low-risk advisor consult on a
6
- * remote/trusted-contact turn could forward private content the main agent
7
- * would never receive.
8
- *
9
- * Mocks are isolated to this file (the test runner runs each file in its own
10
- * process), so the broad module stubs here don't leak into other suites.
11
- */
12
- import { beforeEach, describe, expect, mock, test } from "bun:test";
13
-
14
- let personalAllowed = false;
15
- let scratchpadEnabled = true;
16
- let gateArg: unknown = null;
17
-
18
- mock.module("../../../../daemon/trust-context.js", () => ({
19
- isPersonalMemoryAllowed: (trust: unknown) => {
20
- gateArg = trust;
21
- return personalAllowed;
22
- },
23
- }));
24
- mock.module("../../../../daemon/now-scratchpad.js", () => ({
25
- readNowScratchpad: () => "NOW-CONTENT",
26
- }));
27
- mock.module("../../../../memory/pkb/context.js", () => ({
28
- readPkbContext: () => "PKB-CONTENT",
29
- }));
30
- mock.module("../../../../config/loader.js", () => ({
31
- getConfig: () => ({
32
- memory: {
33
- retrieval: { scratchpadInjection: { enabled: scratchpadEnabled } },
34
- },
35
- llm: {},
36
- }),
37
- }));
38
- // Keep every other section empty so the assertions isolate NOW.md / PKB.
39
- mock.module("../../../../daemon/conversation-workspace.js", () => ({
40
- resolveWorkspaceTopLevelContext: () => null,
41
- }));
42
- mock.module("../../../../daemon/conversation-runtime-assembly.js", () => ({
43
- buildActiveDocuments: () => null,
44
- }));
45
- mock.module("../../../../runtime/capabilities.js", () => ({
46
- resolveCapabilities: () => ({ canAccessMemory: false }),
47
- }));
48
- mock.module("../../../../config/skills.js", () => ({
49
- loadSkillCatalog: () => [],
50
- }));
51
-
52
- const { buildAdvisorContext } = await import("../context-pack.js");
53
-
54
- const sources = {
55
- conversationId: "c1",
56
- workingDir: "/tmp",
57
- // A remote, non-guardian per-turn snapshot — the case the live-state read
58
- // could have wrongly elevated.
59
- trustClass: "unknown" as const,
60
- sourceChannel: "telegram",
61
- transcript: [],
62
- allowedToolNames: new Set<string>(),
63
- };
64
-
65
- beforeEach(() => {
66
- personalAllowed = false;
67
- scratchpadEnabled = true;
68
- gateArg = null;
69
- });
70
-
71
- describe("advisor context pack — personal-memory gating", () => {
72
- test("withholds NOW.md and PKB when personal memory is disallowed", async () => {
73
- personalAllowed = false;
74
- const ctx = (await buildAdvisorContext(sources)) ?? "";
75
- expect(ctx).not.toContain("NOW-CONTENT");
76
- expect(ctx).not.toContain("PKB-CONTENT");
77
- });
78
-
79
- test("includes NOW.md and PKB when allowed and the scratchpad toggle is on", async () => {
80
- personalAllowed = true;
81
- scratchpadEnabled = true;
82
- const ctx = await buildAdvisorContext(sources);
83
- expect(ctx).toContain("NOW-CONTENT");
84
- expect(ctx).toContain("PKB-CONTENT");
85
- });
86
-
87
- test("withholds NOW.md when the scratchpad toggle is off, PKB still allowed", async () => {
88
- personalAllowed = true;
89
- scratchpadEnabled = false;
90
- const ctx = (await buildAdvisorContext(sources)) ?? "";
91
- expect(ctx).not.toContain("NOW-CONTENT");
92
- expect(ctx).toContain("PKB-CONTENT");
93
- });
94
-
95
- test("feeds the gate the per-turn trust snapshot, not live conversation state", async () => {
96
- personalAllowed = true;
97
- await buildAdvisorContext(sources);
98
- // The gate must see exactly the snapshot threaded from ToolContext —
99
- // trustClass + executionChannel — so a concurrent live-trust change can't
100
- // elevate this invocation.
101
- expect(gateArg).toEqual({
102
- sourceChannel: "telegram",
103
- trustClass: "unknown",
104
- });
105
- });
106
- });
@@ -1,60 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import type { Message } from "../../../../providers/types.js";
4
- import { buildAdvisorContext, deriveRecallQuery } from "../context-pack.js";
5
-
6
- const userMsg = (t: string): Message => ({
7
- role: "user",
8
- content: [{ type: "text", text: t }],
9
- });
10
-
11
- describe("deriveRecallQuery", () => {
12
- test("returns the most recent user message text", () => {
13
- const query = deriveRecallQuery([
14
- userMsg("the original task"),
15
- { role: "assistant", content: [{ type: "text", text: "ok" }] },
16
- userMsg("the latest question"),
17
- ]);
18
- expect(query).toBe("the latest question");
19
- });
20
-
21
- test("returns null when there is no user text", () => {
22
- expect(
23
- deriveRecallQuery([
24
- { role: "assistant", content: [{ type: "text", text: "hi" }] },
25
- ]),
26
- ).toBeNull();
27
- expect(deriveRecallQuery([])).toBeNull();
28
- });
29
- });
30
-
31
- describe("buildAdvisorContext", () => {
32
- test("lists the agent's available tools, skipping the advisor itself", async () => {
33
- const context = await buildAdvisorContext({
34
- conversationId: "ctx-1",
35
- workingDir: "/tmp/does-not-exist",
36
- allowedToolNames: new Set(["bash", "advisor", "read_file"]),
37
- trustClass: "unknown",
38
- transcript: [userMsg("hi")],
39
- });
40
-
41
- expect(context).toContain("## Available tools");
42
- expect(context).toContain("- bash");
43
- expect(context).toContain("- read_file");
44
- // The advisor advises; it never tells the agent to consult itself.
45
- expect(context).not.toContain("- advisor");
46
- });
47
-
48
- test("omits the tools section when no tools are available", async () => {
49
- const context = await buildAdvisorContext({
50
- conversationId: "ctx-2",
51
- workingDir: "/tmp/does-not-exist",
52
- allowedToolNames: new Set(),
53
- trustClass: "unknown",
54
- transcript: [],
55
- });
56
- // Other sources (e.g. the skills catalog) may still contribute, but with no
57
- // allowed tools the tools section must not appear.
58
- if (context !== null) expect(context).not.toContain("## Available tools");
59
- });
60
- });
@@ -1,288 +0,0 @@
1
- /**
2
- * Assemble the runtime context the advisor needs to make grounded
3
- * recommendations — the same situational awareness the executing agent has:
4
- * - the tools available to it this turn,
5
- * - the skills it can load,
6
- * - the loaded workspace / project context, NOW.md, PKB, and open documents,
7
- * - and relevant memory pulled through the recall search.
8
- *
9
- * The advisor already receives the agent's transcript and system prompt; this
10
- * adds the situational context that lives *outside* the prompt (tools and
11
- * skills are passed to the model as a separate catalog, not inlined) plus a
12
- * fresh, task-focused memory recall.
13
- *
14
- * Personal-memory surfaces are gated to the same policy the main agent's
15
- * memory injectors apply: the recall search honors `canAccessMemory` (like the
16
- * `recall` tool), and NOW.md / PKB honor `isPersonalMemoryAllowed` (plus the
17
- * scratchpad-injection toggle for NOW.md). The advisor tool is low-risk and can
18
- * run on remote/trusted-contact turns, so without these gates it could forward
19
- * private content the main agent itself would not receive.
20
- *
21
- * Every section is best-effort: each source is wrapped so a failure or empty
22
- * result drops just that section, never the consult. Daemon- and memory-side
23
- * modules are pulled in via dynamic `import()` so this plugin module — loaded
24
- * at bootstrap through `defaults/index.ts` — never forms a static import cycle
25
- * with them. The result is a single string injected into the advisor's system
26
- * prompt (see `buildAdvisorSystem`), or `null` when nothing could be gathered.
27
- */
28
-
29
- import type { ChannelId } from "../../../channels/types.js";
30
- import type { TrustContext } from "../../../daemon/trust-context.js";
31
- import type { Message } from "../../../providers/types.js";
32
- import type { TrustClass } from "../../../runtime/actor-trust-resolver.js";
33
-
34
- export interface AdvisorContextSources {
35
- conversationId: string;
36
- workingDir: string;
37
- /** The live tool set the executor sees this turn (`ToolContext.allowedToolNames`). */
38
- allowedToolNames?: ReadonlySet<string>;
39
- /**
40
- * Trust class of the turn's actor, from the per-turn `ToolContext.trustClass`
41
- * snapshot. Gates the memory recall and (with {@link sourceChannel}) the
42
- * personal-memory surfaces.
43
- */
44
- trustClass: TrustClass;
45
- /**
46
- * Channel the turn originates on, from the per-turn `ToolContext.executionChannel`
47
- * snapshot. Combined with {@link trustClass} to evaluate personal-memory
48
- * access exactly as the injectors do, off the same per-turn snapshot rather
49
- * than the mutable live conversation trust.
50
- */
51
- sourceChannel?: string;
52
- /** The captured transcript, used to derive the recall query. */
53
- transcript: ReadonlyArray<Message>;
54
- signal?: AbortSignal;
55
- }
56
-
57
- /** Cap a block so the assembled context never balloons the consult prompt. */
58
- function truncate(text: string, max: number): string {
59
- const trimmed = text.trim();
60
- return trimmed.length <= max ? trimmed : `${trimmed.slice(0, max)}…`;
61
- }
62
-
63
- /** First sentence (or a capped prefix) of a tool/skill description. */
64
- function summarize(description: string | undefined, max = 160): string {
65
- if (!description) return "";
66
- const firstSentence = description.split(/(?<=[.!?])\s/)[0] ?? description;
67
- return truncate(firstSentence, max);
68
- }
69
-
70
- /** Pull the most recent user-authored text to seed the memory recall query. */
71
- export function deriveRecallQuery(
72
- transcript: ReadonlyArray<Message>,
73
- ): string | null {
74
- for (let i = transcript.length - 1; i >= 0; i--) {
75
- const message = transcript[i];
76
- if (message.role !== "user") continue;
77
- const text = message.content
78
- .map((block) => (block.type === "text" ? block.text : ""))
79
- .join(" ")
80
- .trim();
81
- if (text.length > 0) return truncate(text, 500);
82
- }
83
- return null;
84
- }
85
-
86
- /** `## Available tools` — the live tool set the agent can act with this turn. */
87
- async function buildToolsSection(
88
- allowedToolNames: ReadonlySet<string> | undefined,
89
- ): Promise<string | null> {
90
- if (!allowedToolNames || allowedToolNames.size === 0) return null;
91
- try {
92
- const { getTool } = await import("../../../tools/registry.js");
93
- const lines: string[] = [];
94
- for (const name of [...allowedToolNames].sort()) {
95
- // The advisor advises; it never recommends consulting itself.
96
- if (name === "advisor") continue;
97
- const summary = summarize(getTool(name)?.description);
98
- lines.push(summary ? `- ${name} — ${summary}` : `- ${name}`);
99
- }
100
- if (lines.length === 0) return null;
101
- return `## Available tools (what the agent can do)\n${lines.join("\n")}`;
102
- } catch {
103
- return null;
104
- }
105
- }
106
-
107
- /** `## Available skills` — the skills the agent can load via `skill_load`. */
108
- async function buildSkillsSection(): Promise<string | null> {
109
- try {
110
- const { loadSkillCatalog } = await import("../../../config/skills.js");
111
- const catalog = loadSkillCatalog();
112
- if (catalog.length === 0) return null;
113
- const lines = catalog.slice(0, 60).map((skill) => {
114
- const summary = summarize(skill.description);
115
- const when = skill.activationHints?.length
116
- ? ` (use when: ${truncate(skill.activationHints.join("; "), 120)})`
117
- : "";
118
- const label = skill.displayName || skill.name || skill.id;
119
- return `- ${label} (${skill.id})${summary ? ` — ${summary}` : ""}${when}`;
120
- });
121
- const more =
122
- catalog.length > 60 ? `\n- …and ${catalog.length - 60} more` : "";
123
- return `## Available skills (load with skill_load)\n${lines.join("\n")}${more}`;
124
- } catch {
125
- return null;
126
- }
127
- }
128
-
129
- /**
130
- * Whether personal-memory surfaces (NOW.md, PKB) may be exposed to the advisor
131
- * — the same `isPersonalMemoryAllowed` gate the runtime memory injectors apply.
132
- *
133
- * Derived from the per-turn trust snapshot (`ToolContext.trustClass` /
134
- * `executionChannel`, threaded in via {@link AdvisorContextSources}), NOT the
135
- * live `findConversation().trustContext`: that conversation state is mutable
136
- * and a concurrent guardian/meta command could flip it to guardian mid-flight,
137
- * granting a remote/non-guardian turn access its own snapshot was never given.
138
- * Fail-closed: if the gate can't be resolved, returns false.
139
- */
140
- async function personalMemoryAllowedForAdvisor(
141
- trustClass: TrustClass,
142
- sourceChannel: string | undefined,
143
- ): Promise<boolean> {
144
- try {
145
- const { isPersonalMemoryAllowed } =
146
- await import("../../../daemon/trust-context.js");
147
- // `isPersonalMemoryAllowed` reads only `sourceChannel` + `trustClass`; build
148
- // a minimal trust context from the per-turn snapshot. The channel may be
149
- // absent (local/internal turns), which the gate treats as non-remote.
150
- const snapshot = {
151
- sourceChannel: sourceChannel as ChannelId | undefined,
152
- trustClass,
153
- } as TrustContext;
154
- return isPersonalMemoryAllowed(snapshot);
155
- } catch {
156
- return false;
157
- }
158
- }
159
-
160
- /** `## Workspace & project context` — the loaded environment around the agent. */
161
- async function buildWorkspaceSection(
162
- sources: AdvisorContextSources,
163
- ): Promise<string | null> {
164
- const { conversationId } = sources;
165
- const parts: string[] = [];
166
-
167
- // The `<workspace>` directory listing is not personal memory — the agent's
168
- // own file tools already operate in this cwd — so it is surfaced ungated, the
169
- // same way the workspace-context injector does.
170
- try {
171
- const { resolveWorkspaceTopLevelContext } =
172
- await import("../../../daemon/conversation-workspace.js");
173
- const workspace = resolveWorkspaceTopLevelContext(conversationId);
174
- if (workspace) parts.push(truncate(workspace, 2500));
175
- } catch {
176
- /* best-effort */
177
- }
178
-
179
- // NOW.md and PKB are personal-memory surfaces. Gate them behind the same
180
- // `isPersonalMemoryAllowed` policy (and, for NOW.md, the scratchpad-injection
181
- // toggle) the runtime injectors use, evaluated off the per-turn trust
182
- // snapshot, so a low-risk advisor consult cannot forward private content the
183
- // main agent would never receive.
184
- if (
185
- await personalMemoryAllowedForAdvisor(
186
- sources.trustClass,
187
- sources.sourceChannel,
188
- )
189
- ) {
190
- try {
191
- const [{ readNowScratchpad }, { getConfig }] = await Promise.all([
192
- import("../../../daemon/now-scratchpad.js"),
193
- import("../../../config/loader.js"),
194
- ]);
195
- if (getConfig().memory.retrieval.scratchpadInjection.enabled) {
196
- const now = readNowScratchpad();
197
- if (now) parts.push(`NOW.md scratchpad:\n${truncate(now, 1500)}`);
198
- }
199
- } catch {
200
- /* best-effort */
201
- }
202
-
203
- try {
204
- const { readPkbContext } = await import("../../../memory/pkb/context.js");
205
- const pkb = readPkbContext();
206
- if (pkb) parts.push(truncate(pkb, 1500));
207
- } catch {
208
- /* best-effort */
209
- }
210
- }
211
-
212
- try {
213
- const { buildActiveDocuments } =
214
- await import("../../../daemon/conversation-runtime-assembly.js");
215
- const docs = buildActiveDocuments(conversationId);
216
- if (docs && docs.length > 0) {
217
- const titles = docs
218
- .slice(0, 20)
219
- .map((doc) => `- ${doc.title} (${doc.wordCount} words)`)
220
- .join("\n");
221
- parts.push(`Open documents:\n${titles}`);
222
- }
223
- } catch {
224
- /* best-effort */
225
- }
226
-
227
- if (parts.length === 0) return null;
228
- return `## Workspace & project context\n${parts.join("\n\n")}`;
229
- }
230
-
231
- /** `## Relevant memory (recall)` — a fresh, task-focused recall search. */
232
- async function buildMemorySection(
233
- sources: AdvisorContextSources,
234
- ): Promise<string | null> {
235
- try {
236
- const { resolveCapabilities } =
237
- await import("../../../runtime/capabilities.js");
238
- // Recall reads sensitive local context; honor the same trust gate the
239
- // `recall` tool applies. Non-guardian turns get no fresh recall here.
240
- if (!resolveCapabilities(sources.trustClass).canAccessMemory) return null;
241
-
242
- const query = deriveRecallQuery(sources.transcript);
243
- if (!query) return null;
244
-
245
- const [{ runDeterministicRecallSearch }, { getConfig }] = await Promise.all(
246
- [
247
- import("../../../memory/context-search/search.js"),
248
- import("../../../config/loader.js"),
249
- ],
250
- );
251
-
252
- const { evidence } = await runDeterministicRecallSearch(
253
- { query, max_results: 8 },
254
- {
255
- workingDir: sources.workingDir,
256
- conversationId: sources.conversationId,
257
- config: getConfig(),
258
- signal: sources.signal,
259
- },
260
- );
261
- if (evidence.length === 0) return null;
262
-
263
- const lines = evidence.slice(0, 8).map((item) => {
264
- const excerpt = truncate(item.excerpt, 220);
265
- return `- [${item.source}] ${item.title} (${item.locator}): ${excerpt}`;
266
- });
267
- return `## Relevant memory (recall: "${truncate(query, 120)}")\n${lines.join("\n")}`;
268
- } catch {
269
- return null;
270
- }
271
- }
272
-
273
- /**
274
- * Gather the advisor's runtime context block, or `null` if nothing is
275
- * available. Sections run concurrently; each is independently best-effort.
276
- */
277
- export async function buildAdvisorContext(
278
- sources: AdvisorContextSources,
279
- ): Promise<string | null> {
280
- const sections = await Promise.all([
281
- buildToolsSection(sources.allowedToolNames),
282
- buildSkillsSection(),
283
- buildWorkspaceSection(sources),
284
- buildMemorySection(sources),
285
- ]);
286
- const present = sections.filter((s): s is string => s !== null);
287
- return present.length > 0 ? present.join("\n\n") : null;
288
- }