@vellumai/assistant 0.4.56 → 0.4.57

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 (450) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +3 -0
  3. package/README.md +11 -11
  4. package/docs/architecture/integrations.md +2 -2
  5. package/docs/architecture/memory.md +3 -4
  6. package/docs/credential-execution-service.md +13 -20
  7. package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +7 -7
  10. package/src/__tests__/anthropic-provider.test.ts +172 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
  12. package/src/__tests__/approval-cascade.test.ts +2 -2
  13. package/src/__tests__/approval-routes-http.test.ts +3 -4
  14. package/src/__tests__/asset-materialize-tool.test.ts +5 -5
  15. package/src/__tests__/asset-search-tool.test.ts +1 -1
  16. package/src/__tests__/assistant-attachments.test.ts +5 -5
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
  19. package/src/__tests__/attachments-store.test.ts +2 -2
  20. package/src/__tests__/avatar-e2e.test.ts +5 -3
  21. package/src/__tests__/browser-skill-endstate.test.ts +0 -1
  22. package/src/__tests__/call-routes-http.test.ts +2 -2
  23. package/src/__tests__/callback-handoff-copy.test.ts +1 -1
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
  25. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  26. package/src/__tests__/channel-readiness-service.test.ts +0 -1
  27. package/src/__tests__/checker.test.ts +31 -32
  28. package/src/__tests__/chrome-cdp.test.ts +47 -18
  29. package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
  30. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  31. package/src/__tests__/config-schema.test.ts +9 -18
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
  34. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
  35. package/src/__tests__/conversation-agent-loop.test.ts +11 -4
  36. package/src/__tests__/conversation-attachments.test.ts +1 -1
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
  38. package/src/__tests__/conversation-error.test.ts +33 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
  40. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  41. package/src/__tests__/conversation-pairing.test.ts +1 -1
  42. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
  44. package/src/__tests__/conversation-queue.test.ts +23 -14
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
  47. package/src/__tests__/conversation-seed-composer.test.ts +1 -1
  48. package/src/__tests__/conversation-slash-queue.test.ts +4 -4
  49. package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
  50. package/src/__tests__/conversation-starter-routes.test.ts +291 -0
  51. package/src/__tests__/conversation-wipe.test.ts +438 -0
  52. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
  53. package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
  54. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
  55. package/src/__tests__/credential-security-e2e.test.ts +20 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault-unit.test.ts +227 -0
  58. package/src/__tests__/credentials-cli.test.ts +3 -0
  59. package/src/__tests__/date-context.test.ts +59 -377
  60. package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
  61. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
  62. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  63. package/src/__tests__/encrypted-store.test.ts +237 -15
  64. package/src/__tests__/ephemeral-permissions.test.ts +4 -5
  65. package/src/__tests__/event-bus.test.ts +3 -3
  66. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  67. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  68. package/src/__tests__/gemini-image-service.test.ts +4 -4
  69. package/src/__tests__/gemini-provider.test.ts +6 -9
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  71. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  72. package/src/__tests__/host-shell-tool.test.ts +6 -6
  73. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  74. package/src/__tests__/intent-routing.test.ts +51 -99
  75. package/src/__tests__/invite-routes-http.test.ts +5 -0
  76. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  77. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  78. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  79. package/src/__tests__/media-generate-image.test.ts +32 -15
  80. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  81. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  82. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  83. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  84. package/src/__tests__/memory-regressions.test.ts +86 -90
  85. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  86. package/src/__tests__/migration-export-http.test.ts +26 -27
  87. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  88. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  89. package/src/__tests__/migration-validate-http.test.ts +16 -16
  90. package/src/__tests__/model-intents.test.ts +1 -1
  91. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  92. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  93. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  94. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  95. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  96. package/src/__tests__/notification-deep-link.test.ts +1 -1
  97. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  98. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  99. package/src/__tests__/oauth-store.test.ts +1 -3
  100. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  101. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  102. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  103. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  104. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  105. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  106. package/src/__tests__/qdrant-manager.test.ts +7 -7
  107. package/src/__tests__/ratelimit.test.ts +0 -74
  108. package/src/__tests__/recording-handler.test.ts +0 -1
  109. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  110. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  111. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  112. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  113. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  114. package/src/__tests__/schema-transforms.test.ts +114 -54
  115. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  116. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  117. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  118. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  119. package/src/__tests__/send-notification-tool.test.ts +2 -2
  120. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  121. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  122. package/src/__tests__/skill-memory.test.ts +547 -0
  123. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  124. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  125. package/src/__tests__/slack-channel-config.test.ts +109 -94
  126. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  127. package/src/__tests__/swarm-recursion.test.ts +2 -2
  128. package/src/__tests__/swarm-tool.test.ts +2 -2
  129. package/src/__tests__/system-prompt.test.ts +19 -66
  130. package/src/__tests__/telegram-config.test.ts +121 -0
  131. package/src/__tests__/terminal-tools.test.ts +1 -1
  132. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  133. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  134. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  135. package/src/__tests__/tool-executor.test.ts +1 -1
  136. package/src/__tests__/trace-emitter.test.ts +8 -1
  137. package/src/__tests__/trust-store.test.ts +7 -8
  138. package/src/__tests__/twilio-routes.test.ts +1 -18
  139. package/src/__tests__/user-reference.test.ts +82 -2
  140. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  141. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  142. package/src/approvals/guardian-request-resolvers.ts +3 -3
  143. package/src/avatar/ascii-renderer.ts +2 -2
  144. package/src/avatar/png-renderer.ts +2 -2
  145. package/src/avatar/resvg-lazy.ts +21 -0
  146. package/src/calls/guardian-dispatch.ts +1 -1
  147. package/src/calls/relay-access-wait.ts +2 -2
  148. package/src/calls/twilio-rest.ts +0 -248
  149. package/src/cli/AGENTS.md +5 -8
  150. package/src/cli/__tests__/notifications.test.ts +5 -5
  151. package/src/cli/commands/avatar.ts +64 -2
  152. package/src/cli/commands/conversations.ts +131 -1
  153. package/src/cli/commands/credentials.ts +2 -0
  154. package/src/cli/commands/notifications.ts +3 -3
  155. package/src/cli.ts +10 -0
  156. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  157. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  158. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  159. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  160. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  161. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  162. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  163. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  164. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  165. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  166. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  167. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  168. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  169. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  170. package/src/config/bundled-skills/document/SKILL.md +4 -4
  171. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  172. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  173. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  174. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  175. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  176. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  177. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  178. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  179. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  180. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  181. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  182. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  183. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  184. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  185. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  186. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  187. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  188. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  189. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  190. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  192. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  193. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  195. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  196. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  197. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  198. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  199. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  200. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  201. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  202. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  203. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  204. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  205. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  206. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  207. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  208. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  209. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  210. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  211. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  212. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  213. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  214. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  215. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  216. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  217. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  218. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  219. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  220. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  221. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  222. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  223. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  224. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  225. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  226. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  227. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  228. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  229. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  230. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  231. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  232. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  233. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  234. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  235. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  236. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  237. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  238. package/src/config/feature-flag-registry.json +33 -17
  239. package/src/config/schemas/sandbox.ts +1 -1
  240. package/src/config/schemas/services.ts +13 -3
  241. package/src/config/schemas/timeouts.ts +0 -10
  242. package/src/contacts/contact-store.ts +63 -0
  243. package/src/contacts/contacts-write.ts +1 -1
  244. package/src/daemon/assistant-attachments.ts +2 -2
  245. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  246. package/src/daemon/conversation-agent-loop.ts +7 -30
  247. package/src/daemon/conversation-error.ts +24 -0
  248. package/src/daemon/conversation-memory.ts +8 -7
  249. package/src/daemon/conversation-runtime-assembly.ts +139 -274
  250. package/src/daemon/conversation-slash.ts +7 -26
  251. package/src/daemon/conversation-surfaces.ts +14 -0
  252. package/src/daemon/conversation-tool-setup.ts +9 -8
  253. package/src/daemon/conversation.ts +2 -0
  254. package/src/daemon/daemon-control.ts +1 -1
  255. package/src/daemon/date-context.ts +10 -83
  256. package/src/daemon/handlers/config-channels.ts +12 -2
  257. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  258. package/src/daemon/handlers/config-telegram.ts +6 -1
  259. package/src/daemon/handlers/conversations.ts +2 -2
  260. package/src/daemon/handlers/skills.ts +4 -0
  261. package/src/daemon/lifecycle.ts +28 -4
  262. package/src/daemon/providers-setup.ts +1 -1
  263. package/src/daemon/server.ts +1 -5
  264. package/src/daemon/shutdown-handlers.ts +9 -3
  265. package/src/daemon/tool-side-effects.ts +40 -0
  266. package/src/daemon/trace-emitter.ts +25 -2
  267. package/src/events/domain-events.ts +1 -1
  268. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  269. package/src/inbound/platform-callback-registration.ts +0 -18
  270. package/src/media/app-icon-generator.ts +15 -8
  271. package/src/media/avatar-router.ts +15 -8
  272. package/src/media/gemini-image-service.ts +125 -21
  273. package/src/memory/attachments-store.ts +3 -3
  274. package/src/memory/channel-verification-sessions.ts +6 -6
  275. package/src/memory/conversation-crud.ts +196 -1
  276. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  277. package/src/memory/conversation-title-service.ts +2 -3
  278. package/src/memory/db-init.ts +25 -1
  279. package/src/memory/invite-store.ts +4 -4
  280. package/src/memory/items-extractor.ts +4 -4
  281. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  282. package/src/memory/jobs-store.ts +3 -2
  283. package/src/memory/jobs-worker.ts +7 -5
  284. package/src/memory/lifecycle-events-store.ts +63 -0
  285. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  286. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  287. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  288. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  289. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  290. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  291. package/src/memory/migrations/index.ts +6 -0
  292. package/src/memory/migrations/registry.ts +13 -0
  293. package/src/memory/retriever.test.ts +223 -96
  294. package/src/memory/retriever.ts +115 -138
  295. package/src/memory/schema/calls.ts +1 -1
  296. package/src/memory/schema/contacts.ts +1 -1
  297. package/src/memory/schema/infrastructure.ts +29 -0
  298. package/src/memory/schema/memory-core.ts +7 -17
  299. package/src/memory/schema/notifications.ts +1 -1
  300. package/src/memory/search/formatting.ts +23 -6
  301. package/src/memory/search/lexical.ts +2 -0
  302. package/src/memory/search/semantic.ts +2 -0
  303. package/src/memory/search/staleness.ts +1 -0
  304. package/src/memory/search/types.ts +4 -0
  305. package/src/memory/task-memory-cleanup.ts +96 -6
  306. package/src/memory/trace-event-store.ts +148 -0
  307. package/src/notifications/README.md +1 -1
  308. package/src/notifications/decision-engine.ts +2 -2
  309. package/src/notifications/emit-signal.ts +4 -4
  310. package/src/notifications/events-store.ts +4 -4
  311. package/src/notifications/signal.ts +1 -1
  312. package/src/oauth/manual-token-connection.ts +49 -25
  313. package/src/permissions/checker.ts +6 -5
  314. package/src/permissions/defaults.ts +4 -4
  315. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  316. package/src/prompts/cache-boundary.ts +8 -0
  317. package/src/prompts/system-prompt.ts +105 -634
  318. package/src/prompts/templates/BOOTSTRAP.md +166 -33
  319. package/src/prompts/templates/IDENTITY.md +8 -23
  320. package/src/prompts/templates/SOUL.md +20 -41
  321. package/src/prompts/templates/USER.md +3 -19
  322. package/src/prompts/user-reference.ts +14 -16
  323. package/src/providers/anthropic/client.ts +46 -2
  324. package/src/providers/gemini/client.ts +6 -9
  325. package/src/providers/managed-proxy/constants.ts +1 -7
  326. package/src/providers/managed-proxy/context.ts +0 -1
  327. package/src/providers/model-intents.ts +5 -5
  328. package/src/providers/openai/client.ts +10 -1
  329. package/src/providers/openrouter/client.ts +1 -0
  330. package/src/providers/ratelimit.ts +0 -35
  331. package/src/providers/registry.ts +3 -5
  332. package/src/providers/retry.ts +18 -1
  333. package/src/runtime/access-request-helper.ts +1 -1
  334. package/src/runtime/auth/route-policy.ts +7 -0
  335. package/src/runtime/channel-verification-service.ts +1 -1
  336. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  337. package/src/runtime/guardian-vellum-migration.ts +63 -1
  338. package/src/runtime/http-server.ts +8 -4
  339. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  340. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  341. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  342. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  343. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  344. package/src/runtime/routes/attachment-routes.ts +2 -2
  345. package/src/runtime/routes/btw-routes.ts +9 -0
  346. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  347. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  348. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  349. package/src/runtime/routes/conversation-routes.ts +49 -5
  350. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  351. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  352. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  353. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  354. package/src/runtime/routes/migration-routes.ts +25 -13
  355. package/src/runtime/routes/secret-routes.ts +18 -0
  356. package/src/runtime/routes/settings-routes.ts +8 -8
  357. package/src/runtime/routes/telemetry-routes.ts +53 -0
  358. package/src/runtime/routes/trace-event-routes.ts +62 -0
  359. package/src/runtime/tool-grant-request-helper.ts +1 -1
  360. package/src/runtime/verification-outbound-actions.ts +47 -31
  361. package/src/security/encrypted-store.ts +263 -78
  362. package/src/skills/catalog-install.ts +10 -0
  363. package/src/skills/managed-store.ts +2 -0
  364. package/src/skills/skill-memory.ts +220 -0
  365. package/src/subagent/manager.ts +1 -4
  366. package/src/telemetry/types.ts +10 -1
  367. package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
  368. package/src/telemetry/usage-telemetry-reporter.ts +51 -4
  369. package/src/tools/AGENTS.md +11 -11
  370. package/src/tools/acp/spawn.ts +1 -1
  371. package/src/tools/apps/executors.ts +8 -8
  372. package/src/tools/apps/registry.ts +1 -1
  373. package/src/tools/assets/materialize.ts +6 -6
  374. package/src/tools/assets/search.ts +10 -10
  375. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  376. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  377. package/src/tools/browser/auth-detector.ts +6 -6
  378. package/src/tools/browser/browser-execution.ts +13 -13
  379. package/src/tools/browser/browser-manager.ts +3 -3
  380. package/src/tools/browser/chrome-cdp.ts +5 -5
  381. package/src/tools/browser/jit-auth.ts +2 -2
  382. package/src/tools/browser/network-recorder.test.ts +2 -2
  383. package/src/tools/browser/network-recorder.ts +3 -3
  384. package/src/tools/browser/runtime-check.ts +3 -3
  385. package/src/tools/claude-code/claude-code.ts +2 -2
  386. package/src/tools/computer-use/definitions.ts +18 -18
  387. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  388. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  389. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  390. package/src/tools/credentials/broker-types.ts +5 -5
  391. package/src/tools/credentials/broker.ts +15 -15
  392. package/src/tools/credentials/metadata-store.ts +2 -2
  393. package/src/tools/credentials/resolve.ts +1 -1
  394. package/src/tools/credentials/selection.ts +1 -1
  395. package/src/tools/credentials/tool-policy.ts +1 -1
  396. package/src/tools/credentials/vault.ts +115 -25
  397. package/src/tools/execution-target.ts +2 -2
  398. package/src/tools/executor.ts +7 -7
  399. package/src/tools/filesystem/edit.ts +2 -2
  400. package/src/tools/filesystem/read.ts +1 -1
  401. package/src/tools/filesystem/write.ts +1 -1
  402. package/src/tools/host-filesystem/edit.ts +2 -1
  403. package/src/tools/host-filesystem/read.ts +2 -1
  404. package/src/tools/host-filesystem/write.ts +1 -1
  405. package/src/tools/host-terminal/host-shell.ts +9 -8
  406. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  407. package/src/tools/memory/definitions.ts +6 -5
  408. package/src/tools/memory/handlers.test.ts +1 -1
  409. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  410. package/src/tools/network/domain-normalize.ts +2 -2
  411. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  412. package/src/tools/network/web-fetch.ts +1 -1
  413. package/src/tools/network/web-search.ts +3 -3
  414. package/src/tools/permission-checker.ts +8 -8
  415. package/src/tools/registry.ts +7 -7
  416. package/src/tools/schedule/list.ts +2 -2
  417. package/src/tools/schema-transforms.ts +31 -21
  418. package/src/tools/secret-detection-handler.ts +1 -1
  419. package/src/tools/sensitive-output-placeholders.ts +1 -1
  420. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  421. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  422. package/src/tools/shared/filesystem/image-read.ts +25 -5
  423. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  424. package/src/tools/shared/shell-output.ts +1 -1
  425. package/src/tools/side-effects.ts +1 -1
  426. package/src/tools/skills/execute.ts +1 -1
  427. package/src/tools/skills/load.ts +3 -3
  428. package/src/tools/skills/sandbox-runner.ts +3 -3
  429. package/src/tools/subagent/read.ts +1 -1
  430. package/src/tools/subagent/spawn.ts +2 -2
  431. package/src/tools/swarm/delegate.ts +3 -3
  432. package/src/tools/system/request-permission.ts +5 -4
  433. package/src/tools/terminal/backends/native.ts +4 -4
  434. package/src/tools/terminal/parser.ts +6 -6
  435. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  436. package/src/tools/terminal/shell.ts +16 -16
  437. package/src/tools/tool-approval-handler.ts +21 -12
  438. package/src/tools/tool-manifest.ts +4 -4
  439. package/src/tools/types.ts +3 -3
  440. package/src/tools/ui-surface/definitions.ts +9 -37
  441. package/src/tools/watcher/list.ts +1 -1
  442. package/src/util/logger.ts +7 -2
  443. package/src/util/retry.ts +29 -1
  444. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  445. package/src/workspace/migrations/registry.ts +2 -0
  446. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  447. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  448. package/src/cli/reference.ts +0 -38
  449. package/src/memory/job-handlers/capability-cards.ts +0 -420
  450. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -1,7 +1,8 @@
1
- import { inArray, sql } from "drizzle-orm";
1
+ import { asc, eq, inArray, sql } from "drizzle-orm";
2
2
 
3
3
  import type { AssistantConfig } from "../config/types.js";
4
4
  import { estimateTextTokens } from "../context/token-estimator.js";
5
+ import type { Message } from "../providers/types.js";
5
6
  import { getLogger } from "../util/logger.js";
6
7
  import {
7
8
  abortableSleep,
@@ -24,8 +25,8 @@ import {
24
25
  } from "./schema.js";
25
26
  import {
26
27
  buildTwoLayerInjection,
28
+ CAPABILITY_KINDS,
27
29
  IDENTITY_KINDS,
28
- MEMORY_CONTEXT_ACK,
29
30
  PREFERENCE_KINDS,
30
31
  } from "./search/formatting.js";
31
32
  import { recencySearch } from "./search/lexical.js";
@@ -354,6 +355,44 @@ export async function buildMemoryRecall(
354
355
  if (c.text.length > existing.text.length) {
355
356
  existing.text = c.text;
356
357
  }
358
+ // Propagate metadata that the first source may lack (e.g. legacy
359
+ // Qdrant points missing conversation_id / message_id). The recency
360
+ // source always has these from the DB, so merging fills the gap.
361
+ if (c.conversationId && !existing.conversationId) {
362
+ existing.conversationId = c.conversationId;
363
+ }
364
+ if (c.messageId && !existing.messageId) {
365
+ existing.messageId = c.messageId;
366
+ }
367
+ }
368
+
369
+ // ── Step 5b: Filter out current-conversation segments still in context ──
370
+ // Segments whose source message is still in the conversation's context
371
+ // window are redundant (already visible to the model). However, segments
372
+ // from messages that were removed by context compaction should be kept —
373
+ // those messages are no longer in the conversation history and memory is
374
+ // the only way they can influence the response.
375
+ if (conversationId) {
376
+ const inContextMessageIds = getInContextMessageIds(conversationId);
377
+ if (inContextMessageIds) {
378
+ for (const [key, c] of candidateMap) {
379
+ if (c.type === "segment") {
380
+ if (c.messageId) {
381
+ // Segment has a known source message — filter only if that
382
+ // message is still in the context window.
383
+ if (inContextMessageIds.has(c.messageId)) {
384
+ candidateMap.delete(key);
385
+ }
386
+ } else if (c.conversationId === conversationId) {
387
+ // Segment from the current conversation but missing messageId
388
+ // (e.g. legacy Qdrant points without message_id payload).
389
+ // We can't determine whether it's compacted, so err on the
390
+ // side of filtering to avoid token bloat from redundant segments.
391
+ candidateMap.delete(key);
392
+ }
393
+ }
394
+ }
395
+ }
357
396
  }
358
397
 
359
398
  // Compute RRF-style final scores for the merged candidates
@@ -427,11 +466,15 @@ export async function buildMemoryRecall(
427
466
  const preferences = afterDemotion.filter(
428
467
  (c) => c.tier === 1 && PREFERENCE_KINDS.has(c.kind),
429
468
  );
469
+ const capabilities = afterDemotion.filter(
470
+ (c) => c.tier === 1 && CAPABILITY_KINDS.has(c.kind),
471
+ );
430
472
  const tier1Candidates = afterDemotion.filter(
431
473
  (c) =>
432
474
  c.tier === 1 &&
433
475
  !IDENTITY_KINDS.has(c.kind) &&
434
- !PREFERENCE_KINDS.has(c.kind),
476
+ !PREFERENCE_KINDS.has(c.kind) &&
477
+ !CAPABILITY_KINDS.has(c.kind),
435
478
  );
436
479
  const tier2Candidates = afterDemotion.filter((c) => c.tier === 2);
437
480
 
@@ -440,6 +483,7 @@ export async function buildMemoryRecall(
440
483
  tier1Candidates,
441
484
  tier2Candidates,
442
485
  preferences,
486
+ capabilities,
443
487
  totalBudgetTokens: maxInjectTokens,
444
488
  });
445
489
 
@@ -448,7 +492,8 @@ export async function buildMemoryRecall(
448
492
  identityItems.length +
449
493
  tier1Candidates.length +
450
494
  tier2Candidates.length +
451
- preferences.length;
495
+ preferences.length +
496
+ capabilities.length;
452
497
 
453
498
  const tier1Count = afterDemotion.filter((c) => c.tier === 1).length;
454
499
  const tier2Count = afterDemotion.filter((c) => c.tier === 2).length;
@@ -528,6 +573,52 @@ export async function buildMemoryRecall(
528
573
  return result;
529
574
  }
530
575
 
576
+ /**
577
+ * Get the set of message IDs that are still in the conversation's context
578
+ * window (i.e., not compacted away). Uses `contextCompactedMessageCount` to
579
+ * determine the offset: messages ordered by createdAt after that count are
580
+ * still visible to the model.
581
+ *
582
+ * Returns `null` if the conversation is not found (deleted, or no DB row).
583
+ */
584
+ function getInContextMessageIds(conversationId: string): Set<string> | null {
585
+ try {
586
+ const db = getDb();
587
+
588
+ // Look up the conversation's compacted message count
589
+ const conv = db
590
+ .select({
591
+ contextCompactedMessageCount:
592
+ conversations.contextCompactedMessageCount,
593
+ })
594
+ .from(conversations)
595
+ .where(eq(conversations.id, conversationId))
596
+ .get();
597
+
598
+ if (!conv) return null;
599
+
600
+ const offset = conv.contextCompactedMessageCount;
601
+
602
+ // Fetch message IDs ordered by creation time, skipping compacted ones
603
+ const rows = db
604
+ .select({ id: messages.id })
605
+ .from(messages)
606
+ .where(eq(messages.conversationId, conversationId))
607
+ .orderBy(asc(messages.createdAt))
608
+ .all();
609
+
610
+ // Messages up to `offset` have been compacted out of context
611
+ const inContextRows = rows.slice(offset);
612
+ return new Set(inContextRows.map((r) => r.id));
613
+ } catch (err) {
614
+ log.warn(
615
+ { err },
616
+ "Failed to fetch in-context message IDs; skipping segment filter",
617
+ );
618
+ return null;
619
+ }
620
+ }
621
+
531
622
  /**
532
623
  * Enrich item candidates with metadata needed for staleness computation:
533
624
  * - firstSeenAt: when the item was first extracted
@@ -671,146 +762,32 @@ function enrichSourceLabels(candidates: TieredCandidate[]): void {
671
762
  }
672
763
 
673
764
  /**
674
- * Strip memory recall messages from the conversation history.
765
+ * Inject memory recall as a text content block prepended to the last user
766
+ * message. This follows the same pattern as workspace, temporal, and other
767
+ * runtime injections — the memory context is a text block in the user
768
+ * message rather than a separate synthetic message pair.
675
769
  *
676
- * Handles both exact text matching and `<memory_context>` XML wrapper
677
- * detection: when the recall text starts with `<memory_context>`, we
678
- * also match user messages whose sole text block starts with the same
679
- * tag (covering cases where the exact text differs slightly due to
680
- * dynamic content).
770
+ * Stripping is handled by `stripUserTextBlocksByPrefix` matching the
771
+ * `<memory_context __injected>` prefix in `RUNTIME_INJECTION_PREFIXES`, so no
772
+ * dedicated strip function is needed.
681
773
  */
682
- export function stripMemoryRecallMessages<
683
- T extends {
684
- role: "user" | "assistant";
685
- content: Array<{ type: string; text?: string }>;
686
- },
687
- >(
688
- messages: T[],
689
- memoryRecallText?: string,
690
- injectionStrategy?: "prepend_user_block" | "separate_context_message",
691
- ): T[] {
692
- const recallText = memoryRecallText ?? "";
693
- if (recallText.trim().length === 0) return messages;
694
-
695
- const isAck = (msg: T) =>
696
- msg.role === "assistant" &&
697
- msg.content.length === 1 &&
698
- msg.content[0].type === "text" &&
699
- msg.content[0].text === MEMORY_CONTEXT_ACK;
700
-
701
- // Check if the recall text uses the <memory_context> XML format
702
- const isMemoryContextFormat = recallText
703
- .trimStart()
704
- .startsWith("<memory_context>");
705
-
706
- // Helper: does a text block match the recall text?
707
- const textMatches = (text: string | undefined): boolean => {
708
- if (!text) return false;
709
- if (text === recallText) return true;
710
- // For <memory_context> format, match any block that starts with the tag
711
- if (
712
- isMemoryContextFormat &&
713
- text.trimStart().startsWith("<memory_context>")
714
- ) {
715
- return true;
716
- }
717
- return false;
718
- };
719
-
720
- // Prefer the canonical separate_context_message pair: a user message whose
721
- // sole text block is the recall text, followed by an assistant ack. This
722
- // must be checked first so that a real user message that happens to contain
723
- // the same text is not incorrectly removed instead of the synthetic one.
724
- if (injectionStrategy !== "prepend_user_block") {
725
- for (let i = messages.length - 1; i >= 0; i--) {
726
- const msg = messages[i];
727
- if (msg.role !== "user") continue;
728
- if (msg.content.length !== 1) continue;
729
- const block = msg.content[0];
730
- if (block.type !== "text" || !textMatches(block.text)) continue;
731
- const next = messages[i + 1];
732
- if (next && isAck(next)) {
733
- return [...messages.slice(0, i), ...messages.slice(i + 2)];
734
- }
735
- }
736
- }
737
-
738
- // Fall back to generic text-match removal: find the last user message
739
- // containing the recall text block.
740
- let targetIndex = -1;
741
- let blockIndex = -1;
742
- for (let i = messages.length - 1; i >= 0; i--) {
743
- const msg = messages[i];
744
- if (msg.role !== "user" || msg.content.length === 0) continue;
745
- for (let bi = msg.content.length - 1; bi >= 0; bi--) {
746
- const block = msg.content[bi];
747
- if (block.type === "text" && textMatches(block.text)) {
748
- targetIndex = i;
749
- blockIndex = bi;
750
- break;
751
- }
752
- }
753
- if (targetIndex !== -1) break;
754
- }
755
- if (targetIndex === -1) return messages;
756
-
757
- // Strip the adjacent assistant ack when the injection strategy used a
758
- // separate context message (or is unknown). This mirrors the canonical
759
- // pair removal above but covers repair-merged cases where the user
760
- // message has multiple content blocks.
761
- const ackIndex =
762
- injectionStrategy !== "prepend_user_block" &&
763
- targetIndex + 1 < messages.length &&
764
- isAck(messages[targetIndex + 1])
765
- ? targetIndex + 1
766
- : -1;
767
-
768
- const cleaned: T[] = [];
769
- for (let i = 0; i < messages.length; i++) {
770
- if (i === ackIndex) continue;
771
- if (i !== targetIndex) {
772
- cleaned.push(messages[i]);
773
- continue;
774
- }
775
- const filteredContent = [
776
- ...messages[i].content.slice(0, blockIndex),
777
- ...messages[i].content.slice(blockIndex + 1),
778
- ];
779
- if (filteredContent.length === 0) continue;
780
- cleaned.push({ ...messages[i], content: filteredContent } as T);
781
- }
782
- return cleaned;
783
- }
784
-
785
- /**
786
- * Inject memory recall as a separate user+assistant message pair before the
787
- * last user message. This separates memory context from the user's actual
788
- * query, making it clearer to the model that the memory is background context.
789
- */
790
- export function injectMemoryRecallAsSeparateMessage<
791
- T extends {
792
- role: "user" | "assistant";
793
- content: Array<{ type: string; text?: string }>;
794
- },
795
- >(messages: T[], memoryRecallText: string): T[] {
774
+ export function injectMemoryRecallAsUserBlock(
775
+ messages: Message[],
776
+ memoryRecallText: string,
777
+ ): Message[] {
796
778
  if (memoryRecallText.trim().length === 0) return messages;
797
779
  if (messages.length === 0) return messages;
798
- // These synthetic messages satisfy the structural constraint T extends { role; content }
799
- // but may lack extra fields present on T. In practice T is always Message which has
800
- // only role and content, so the cast is safe.
801
- const contextMessage = {
802
- role: "user" as const,
803
- content: [{ type: "text" as const, text: memoryRecallText }],
804
- } as T;
805
- const ackMessage = {
806
- role: "assistant" as const,
807
- content: [{ type: "text" as const, text: MEMORY_CONTEXT_ACK }],
808
- } as T;
780
+ const userTail = messages[messages.length - 1];
781
+ if (!userTail || userTail.role !== "user") return messages;
809
782
  return [
810
783
  ...messages.slice(0, -1),
811
- contextMessage,
812
- ackMessage,
813
- messages[messages.length - 1],
784
+ {
785
+ ...userTail,
786
+ content: [
787
+ { type: "text" as const, text: memoryRecallText },
788
+ ...userTail.content,
789
+ ],
790
+ },
814
791
  ];
815
792
  }
816
793
 
@@ -95,7 +95,7 @@ export const channelVerificationSessions = sqliteTable(
95
95
  challengeHash: text("challenge_hash").notNull(),
96
96
  expiresAt: integer("expires_at").notNull(),
97
97
  status: text("status").notNull().default("pending"),
98
- createdByConversationId: text("created_by_session_id"),
98
+ sourceConversationId: text("source_conversation_id"),
99
99
  consumedByExternalUserId: text("consumed_by_external_user_id"),
100
100
  consumedByChatId: text("consumed_by_chat_id"),
101
101
  // Outbound session: expected-identity binding
@@ -69,7 +69,7 @@ export const assistantIngressInvites = sqliteTable(
69
69
  id: text("id").primaryKey(),
70
70
  sourceChannel: text("source_channel").notNull(),
71
71
  tokenHash: text("token_hash").notNull(),
72
- createdByConversationId: text("created_by_session_id"),
72
+ sourceConversationId: text("source_conversation_id"),
73
73
  note: text("note"),
74
74
  maxUses: integer("max_uses").notNull().default(1),
75
75
  useCount: integer("use_count").notNull().default(0),
@@ -138,6 +138,12 @@ export const llmUsageEvents = sqliteTable(
138
138
  ],
139
139
  );
140
140
 
141
+ export const lifecycleEvents = sqliteTable("lifecycle_events", {
142
+ id: text("id").primaryKey(),
143
+ eventName: text("event_name").notNull(), // 'app_open' | 'hatch'
144
+ createdAt: integer("created_at").notNull(),
145
+ });
146
+
141
147
  export const actorTokenRecords = sqliteTable("actor_token_records", {
142
148
  id: text("id").primaryKey(),
143
149
  tokenHash: text("token_hash").notNull(),
@@ -151,6 +157,29 @@ export const actorTokenRecords = sqliteTable("actor_token_records", {
151
157
  updatedAt: integer("updated_at").notNull(),
152
158
  });
153
159
 
160
+ export const traceEvents = sqliteTable(
161
+ "trace_events",
162
+ {
163
+ eventId: text("event_id").primaryKey(),
164
+ conversationId: text("conversation_id").notNull(),
165
+ requestId: text("request_id"),
166
+ timestampMs: integer("timestamp_ms").notNull(),
167
+ sequence: integer("sequence").notNull(),
168
+ kind: text("kind").notNull(),
169
+ status: text("status"),
170
+ summary: text("summary").notNull(),
171
+ attributesJson: text("attributes_json"), // JSON-serialized attributes
172
+ createdAt: integer("created_at").notNull(),
173
+ },
174
+ (table) => [
175
+ index("idx_trace_events_conversation_id").on(table.conversationId),
176
+ index("idx_trace_events_conversation_timestamp").on(
177
+ table.conversationId,
178
+ table.timestampMs,
179
+ ),
180
+ ],
181
+ );
182
+
154
183
  export const actorRefreshTokenRecords = sqliteTable(
155
184
  "actor_refresh_token_records",
156
185
  {
@@ -2,7 +2,6 @@ import {
2
2
  blob,
3
3
  index,
4
4
  integer,
5
- primaryKey,
6
5
  real,
7
6
  sqliteTable,
8
7
  text,
@@ -150,8 +149,8 @@ export const memoryCheckpoints = sqliteTable("memory_checkpoints", {
150
149
  updatedAt: integer("updated_at").notNull(),
151
150
  });
152
151
 
153
- export const threadStarters = sqliteTable(
154
- "thread_starters",
152
+ export const conversationStarters = sqliteTable(
153
+ "conversation_starters",
155
154
  {
156
155
  id: text("id").primaryKey(),
157
156
  label: text("label").notNull(),
@@ -167,22 +166,13 @@ export const threadStarters = sqliteTable(
167
166
  createdAt: integer("created_at").notNull(),
168
167
  },
169
168
  (table) => [
170
- index("idx_thread_starters_batch").on(
169
+ index("idx_conversation_starters_batch").on(
171
170
  table.generationBatch,
172
171
  table.createdAt,
173
172
  ),
174
- index("idx_thread_starters_card_type").on(table.cardType, table.scopeId),
173
+ index("idx_conversation_starters_card_type").on(
174
+ table.cardType,
175
+ table.scopeId,
176
+ ),
175
177
  ],
176
178
  );
177
-
178
- export const capabilityCardCategories = sqliteTable(
179
- "capability_card_categories",
180
- {
181
- scopeId: text("scope_id").notNull(),
182
- category: text("category").notNull(),
183
- relevance: real("relevance").notNull(),
184
- generationBatch: integer("generation_batch").notNull(),
185
- createdAt: integer("created_at").notNull(),
186
- },
187
- (table) => [primaryKey({ columns: [table.scopeId, table.category] })],
188
- );
@@ -13,7 +13,7 @@ export const notificationEvents = sqliteTable("notification_events", {
13
13
  id: text("id").primaryKey(),
14
14
  sourceEventName: text("source_event_name").notNull(),
15
15
  sourceChannel: text("source_channel").notNull(),
16
- sourceSessionId: text("source_session_id").notNull(),
16
+ sourceContextId: text("source_context_id").notNull(),
17
17
  attentionHintsJson: text("attention_hints_json").notNull().default("{}"),
18
18
  payloadJson: text("payload_json").notNull().default("{}"),
19
19
  dedupeKey: text("dedupe_key"),
@@ -1,9 +1,6 @@
1
1
  import { estimateTextTokens } from "../../context/token-estimator.js";
2
2
  import type { TieredCandidate } from "./tier-classifier.js";
3
3
 
4
- /** Marker text used in the assistant acknowledgment of a separate context message. */
5
- export const MEMORY_CONTEXT_ACK = "[Memory context loaded.]";
6
-
7
4
  /**
8
5
  * Escape XML-like tag sequences in recalled text to prevent delimiter injection.
9
6
  * Recalled content is interpolated verbatim inside `<memory>` wrapper tags,
@@ -80,6 +77,9 @@ export const IDENTITY_KINDS = new Set(["identity"]);
80
77
  /** Kinds classified as preferences for the <applicable_preferences> section. */
81
78
  export const PREFERENCE_KINDS = new Set(["preference", "constraint"]);
82
79
 
80
+ /** Kinds classified as capabilities for the <available_capabilities> section. */
81
+ export const CAPABILITY_KINDS = new Set(["capability"]);
82
+
83
83
  /** Per-item token budget for tier 1 items. */
84
84
  const TIER1_PER_ITEM_TOKENS = 150;
85
85
 
@@ -105,6 +105,7 @@ export function buildTwoLayerInjection(params: {
105
105
  tier1Candidates: TieredCandidate[];
106
106
  tier2Candidates: TieredCandidate[];
107
107
  preferences: TieredCandidate[];
108
+ capabilities: TieredCandidate[];
108
109
  totalBudgetTokens?: number;
109
110
  }): string {
110
111
  const {
@@ -112,6 +113,7 @@ export function buildTwoLayerInjection(params: {
112
113
  tier1Candidates,
113
114
  tier2Candidates,
114
115
  preferences,
116
+ capabilities,
115
117
  totalBudgetTokens,
116
118
  } = params;
117
119
 
@@ -120,7 +122,8 @@ export function buildTwoLayerInjection(params: {
120
122
  identityItems.length === 0 &&
121
123
  tier1Candidates.length === 0 &&
122
124
  tier2Candidates.length === 0 &&
123
- preferences.length === 0
125
+ preferences.length === 0 &&
126
+ capabilities.length === 0
124
127
  ) {
125
128
  return "";
126
129
  }
@@ -129,7 +132,7 @@ export function buildTwoLayerInjection(params: {
129
132
  // Reserve tokens for XML wrapper overhead (<memory_context>, section tags,
130
133
  // newlines between sections) so the final assembled text stays within budget.
131
134
  const WRAPPER_OVERHEAD_TOKENS = estimateTextTokens(
132
- "<memory_context>\n\n\n\n</memory_context>",
135
+ "<memory_context __injected>\n\n\n\n</memory_context>",
133
136
  );
134
137
  const SECTION_TAG_TOKENS = estimateTextTokens(
135
138
  "<possibly_relevant>\n\n</possibly_relevant>",
@@ -139,6 +142,7 @@ export function buildTwoLayerInjection(params: {
139
142
  tier1Candidates.length,
140
143
  tier2Candidates.length,
141
144
  preferences.length,
145
+ capabilities.length,
142
146
  ].filter((n) => n > 0).length;
143
147
  const structuralOverhead =
144
148
  WRAPPER_OVERHEAD_TOKENS + sectionCount * SECTION_TAG_TOKENS;
@@ -168,6 +172,13 @@ export function buildTwoLayerInjection(params: {
168
172
  );
169
173
  remainingTokens -= estimateTextTokens(preferenceLines.join("\n"));
170
174
 
175
+ const capabilityLines = renderPlainStatements(
176
+ capabilities,
177
+ TIER1_PER_ITEM_TOKENS,
178
+ remainingTokens,
179
+ );
180
+ remainingTokens -= estimateTextTokens(capabilityLines.join("\n"));
181
+
171
182
  // Tier 2 uses remaining budget
172
183
  const possiblyRelevantEpisodes = renderEpisodesWithStaleness(
173
184
  tier2Candidates,
@@ -196,6 +207,12 @@ export function buildTwoLayerInjection(params: {
196
207
  );
197
208
  }
198
209
 
210
+ if (capabilityLines.length > 0) {
211
+ sections.push(
212
+ `<available_capabilities>\n${capabilityLines.join("\n")}\n</available_capabilities>`,
213
+ );
214
+ }
215
+
199
216
  if (possiblyRelevantEpisodes.length > 0) {
200
217
  sections.push(
201
218
  `<possibly_relevant>\n${possiblyRelevantEpisodes.join("\n")}\n</possibly_relevant>`,
@@ -204,7 +221,7 @@ export function buildTwoLayerInjection(params: {
204
221
 
205
222
  if (sections.length === 0) return "";
206
223
 
207
- return `<memory_context>\n\n${sections.join("\n\n")}\n\n</memory_context>`;
224
+ return `<memory_context __injected>\n\n${sections.join("\n\n")}\n\n</memory_context>`;
208
225
  }
209
226
 
210
227
  /**
@@ -36,6 +36,8 @@ export function recencySearch(
36
36
  source: "recency",
37
37
  text: row.text,
38
38
  kind: "segment",
39
+ conversationId: row.conversationId,
40
+ messageId: row.messageId,
39
41
  confidence: 0.55,
40
42
  importance: 0.5,
41
43
  createdAt: row.createdAt,
@@ -245,6 +245,8 @@ export async function semanticSearch(
245
245
  source: "semantic",
246
246
  text: payload.text,
247
247
  kind: "segment",
248
+ conversationId: payload.conversation_id,
249
+ messageId: payload.message_id,
248
250
  confidence: 0.55,
249
251
  importance: 0.5,
250
252
  createdAt,
@@ -8,6 +8,7 @@ const BASE_LIFETIME_MS: Record<string, number> = {
8
8
  project: 14 * 86_400_000, // 2 weeks
9
9
  decision: 14 * 86_400_000, // 2 weeks
10
10
  event: 3 * 86_400_000, // 3 days
11
+ capability: Infinity,
11
12
  };
12
13
 
13
14
  const DEFAULT_LIFETIME_MS = 30 * 86_400_000;
@@ -11,6 +11,10 @@ export interface Candidate {
11
11
  text: string;
12
12
  kind: string;
13
13
  modality?: "text" | "image" | "audio" | "video";
14
+ /** The conversation this candidate originated from (segments only). */
15
+ conversationId?: string;
16
+ /** The source message ID this candidate was extracted from (segments only). */
17
+ messageId?: string;
14
18
  confidence: number;
15
19
  importance: number;
16
20
  createdAt: number;