@vellumai/assistant 0.5.10 → 0.5.12

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 (395) hide show
  1. package/AGENTS.md +8 -0
  2. package/ARCHITECTURE.md +43 -43
  3. package/Dockerfile +3 -0
  4. package/docs/architecture/integrations.md +37 -42
  5. package/docs/architecture/memory.md +7 -12
  6. package/docs/credential-execution-service.md +9 -9
  7. package/docs/skills.md +1 -1
  8. package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
  9. package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
  10. package/node_modules/@vellumai/credential-storage/src/index.ts +3 -3
  11. package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
  12. package/openapi.yaml +7208 -0
  13. package/package.json +2 -1
  14. package/scripts/generate-openapi.ts +562 -0
  15. package/src/__tests__/acp-session.test.ts +239 -44
  16. package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
  17. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
  19. package/src/__tests__/browser-skill-endstate.test.ts +1 -1
  20. package/src/__tests__/btw-routes.test.ts +8 -0
  21. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
  22. package/src/__tests__/catalog-cache.test.ts +164 -0
  23. package/src/__tests__/catalog-search.test.ts +61 -0
  24. package/src/__tests__/channel-approvals.test.ts +7 -7
  25. package/src/__tests__/channel-readiness-service.test.ts +41 -0
  26. package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
  27. package/src/__tests__/config-schema.test.ts +10 -2
  28. package/src/__tests__/context-memory-e2e.test.ts +2 -6
  29. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
  30. package/src/__tests__/conversation-error.test.ts +3 -2
  31. package/src/__tests__/conversation-skill-tools.test.ts +1 -3
  32. package/src/__tests__/conversation-title-service.test.ts +2 -15
  33. package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
  34. package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
  35. package/src/__tests__/credential-security-e2e.test.ts +4 -4
  36. package/src/__tests__/credential-security-invariants.test.ts +12 -18
  37. package/src/__tests__/credential-vault-unit.test.ts +32 -34
  38. package/src/__tests__/credential-vault.test.ts +25 -33
  39. package/src/__tests__/credentials-cli.test.ts +3 -3
  40. package/src/__tests__/daemon-credential-client.test.ts +2 -2
  41. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
  42. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  43. package/src/__tests__/heartbeat-service.test.ts +35 -0
  44. package/src/__tests__/host-bash-proxy.test.ts +79 -0
  45. package/src/__tests__/host-cu-proxy.test.ts +90 -0
  46. package/src/__tests__/host-file-proxy.test.ts +89 -0
  47. package/src/__tests__/host-shell-tool.test.ts +1 -1
  48. package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
  49. package/src/__tests__/integration-status.test.ts +5 -5
  50. package/src/__tests__/list-messages-attachments.test.ts +171 -0
  51. package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
  52. package/src/__tests__/log-export-workspace.test.ts +1 -1
  53. package/src/__tests__/mcp-abort-signal.test.ts +205 -0
  54. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  55. package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
  56. package/src/__tests__/memory-recall-log-store.test.ts +182 -0
  57. package/src/__tests__/memory-recall-quality.test.ts +6 -8
  58. package/src/__tests__/memory-regressions.test.ts +53 -42
  59. package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
  60. package/src/__tests__/messaging-send-tool.test.ts +5 -5
  61. package/src/__tests__/messaging-skill-split.test.ts +2 -17
  62. package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
  63. package/src/__tests__/oauth-cli.test.ts +203 -649
  64. package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
  65. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  66. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  67. package/src/__tests__/platform-callback-registration.test.ts +119 -0
  68. package/src/__tests__/secret-ingress-channel.test.ts +261 -0
  69. package/src/__tests__/secret-ingress-cli.test.ts +201 -0
  70. package/src/__tests__/secret-ingress-http.test.ts +312 -0
  71. package/src/__tests__/secret-ingress.test.ts +283 -0
  72. package/src/__tests__/secret-onetime-send.test.ts +4 -4
  73. package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
  74. package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
  75. package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
  76. package/src/__tests__/skill-feature-flags.test.ts +11 -19
  77. package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
  78. package/src/__tests__/skill-load-inline-command.test.ts +3 -3
  79. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  80. package/src/__tests__/skill-memory.test.ts +2 -4
  81. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
  82. package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
  83. package/src/__tests__/skills-uninstall.test.ts +2 -2
  84. package/src/__tests__/skills.test.ts +16 -2
  85. package/src/__tests__/slack-channel-config.test.ts +1 -1
  86. package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
  87. package/src/__tests__/slack-share-routes.test.ts +5 -5
  88. package/src/__tests__/slack-skill.test.ts +5 -69
  89. package/src/__tests__/system-prompt.test.ts +39 -0
  90. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
  91. package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
  92. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
  93. package/src/acp/client-handler.ts +113 -31
  94. package/src/acp/session-manager.ts +29 -27
  95. package/src/approvals/guardian-request-resolvers.ts +1 -1
  96. package/src/cli/AGENTS.md +113 -0
  97. package/src/cli/commands/autonomy.ts +3 -5
  98. package/src/cli/commands/browser-relay.ts +2 -17
  99. package/src/cli/commands/contacts.ts +6 -4
  100. package/src/cli/commands/conversations.ts +13 -1
  101. package/src/cli/commands/credential-execution.ts +17 -3
  102. package/src/cli/commands/credentials.ts +2 -8
  103. package/src/cli/commands/memory.ts +2 -3
  104. package/src/cli/commands/oauth/__tests__/connect.test.ts +706 -0
  105. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +686 -0
  106. package/src/cli/commands/oauth/__tests__/mode.test.ts +625 -0
  107. package/src/cli/commands/oauth/__tests__/ping.test.ts +631 -0
  108. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
  109. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
  110. package/src/cli/commands/oauth/__tests__/status.test.ts +551 -0
  111. package/src/cli/commands/oauth/__tests__/token.test.ts +420 -0
  112. package/src/cli/commands/oauth/apps.ts +87 -50
  113. package/src/cli/commands/oauth/connect.ts +405 -0
  114. package/src/cli/commands/oauth/disconnect.ts +285 -0
  115. package/src/cli/commands/oauth/index.ts +62 -20
  116. package/src/cli/commands/oauth/mode.ts +251 -0
  117. package/src/cli/commands/oauth/ping.ts +196 -0
  118. package/src/cli/commands/oauth/providers.ts +589 -55
  119. package/src/cli/commands/oauth/request.ts +564 -0
  120. package/src/cli/commands/oauth/shared.ts +114 -0
  121. package/src/cli/commands/oauth/status.ts +191 -0
  122. package/src/cli/commands/oauth/token.ts +150 -0
  123. package/src/cli/commands/platform/connect.ts +104 -0
  124. package/src/cli/commands/platform/disconnect.ts +118 -0
  125. package/src/cli/commands/platform/index.ts +252 -0
  126. package/src/cli/commands/sequence.ts +5 -4
  127. package/src/cli/commands/shotgun.ts +16 -0
  128. package/src/cli/commands/skills.ts +173 -41
  129. package/src/cli/commands/usage.ts +5 -11
  130. package/src/cli/lib/daemon-credential-client.ts +22 -38
  131. package/src/cli/program.ts +1 -1
  132. package/src/cli.ts +82 -17
  133. package/src/config/assistant-feature-flags.ts +77 -18
  134. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  135. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
  136. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
  137. package/src/config/bundled-skills/conversations/SKILL.md +20 -0
  138. package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
  139. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
  140. package/src/config/bundled-skills/gmail/SKILL.md +13 -13
  141. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
  142. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
  143. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
  144. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
  145. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
  146. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
  147. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
  148. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
  149. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
  150. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  151. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
  152. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
  153. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
  154. package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
  155. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  156. package/src/config/bundled-skills/messaging/SKILL.md +19 -42
  157. package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
  158. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  159. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
  160. package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
  161. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  162. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  163. package/src/config/bundled-skills/settings/SKILL.md +5 -3
  164. package/src/config/bundled-skills/settings/TOOLS.json +17 -0
  165. package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
  166. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
  167. package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
  168. package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
  169. package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
  170. package/src/config/bundled-skills/slack/SKILL.md +58 -44
  171. package/src/config/bundled-tool-registry.ts +7 -19
  172. package/src/config/env.ts +5 -1
  173. package/src/config/feature-flag-registry.json +58 -42
  174. package/src/config/loader.ts +4 -0
  175. package/src/config/schemas/platform.ts +0 -8
  176. package/src/config/schemas/security.ts +9 -1
  177. package/src/config/schemas/services.ts +1 -1
  178. package/src/config/skill-state.ts +1 -3
  179. package/src/config/skills.ts +2 -4
  180. package/src/credential-execution/client.ts +1 -1
  181. package/src/credential-execution/feature-gates.ts +9 -16
  182. package/src/credential-execution/process-manager.ts +12 -0
  183. package/src/daemon/config-watcher.ts +4 -0
  184. package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
  185. package/src/daemon/conversation-agent-loop.ts +51 -2
  186. package/src/daemon/conversation-error.ts +36 -6
  187. package/src/daemon/conversation-memory.ts +0 -1
  188. package/src/daemon/conversation-messaging.ts +9 -0
  189. package/src/daemon/conversation-runtime-assembly.ts +33 -0
  190. package/src/daemon/conversation-surfaces.ts +120 -14
  191. package/src/daemon/conversation.ts +5 -0
  192. package/src/daemon/handlers/config-slack-channel.ts +43 -1
  193. package/src/daemon/handlers/conversations.ts +41 -33
  194. package/src/daemon/handlers/skills.ts +148 -3
  195. package/src/daemon/host-bash-proxy.ts +16 -0
  196. package/src/daemon/host-cu-proxy.ts +16 -0
  197. package/src/daemon/host-file-proxy.ts +16 -0
  198. package/src/daemon/lifecycle.ts +73 -3
  199. package/src/daemon/message-types/acp.ts +0 -15
  200. package/src/daemon/message-types/conversations.ts +1 -0
  201. package/src/daemon/message-types/guardian-actions.ts +2 -0
  202. package/src/daemon/message-types/host-bash.ts +6 -1
  203. package/src/daemon/message-types/host-cu.ts +6 -1
  204. package/src/daemon/message-types/host-file.ts +6 -1
  205. package/src/daemon/message-types/integrations.ts +0 -1
  206. package/src/daemon/message-types/memory.ts +0 -1
  207. package/src/daemon/message-types/messages.ts +9 -1
  208. package/src/daemon/message-types/schedules.ts +9 -0
  209. package/src/daemon/server.ts +48 -9
  210. package/src/email/feature-gate.ts +3 -3
  211. package/src/heartbeat/heartbeat-service.ts +48 -0
  212. package/src/hooks/cli.ts +74 -0
  213. package/src/inbound/platform-callback-registration.ts +68 -19
  214. package/src/mcp/client.ts +6 -1
  215. package/src/mcp/manager.ts +2 -1
  216. package/src/mcp/mcp-oauth-provider.ts +3 -3
  217. package/src/memory/app-store.ts +3 -3
  218. package/src/memory/conversation-crud.ts +213 -0
  219. package/src/memory/conversation-key-store.ts +26 -0
  220. package/src/memory/conversation-title-service.ts +7 -17
  221. package/src/memory/db-init.ts +24 -0
  222. package/src/memory/embedding-local.ts +47 -2
  223. package/src/memory/indexer.ts +13 -10
  224. package/src/memory/items-extractor.ts +12 -4
  225. package/src/memory/job-utils.ts +5 -0
  226. package/src/memory/jobs-store.ts +10 -2
  227. package/src/memory/journal-memory.ts +6 -2
  228. package/src/memory/llm-request-log-store.ts +88 -21
  229. package/src/memory/memory-recall-log-store.ts +128 -0
  230. package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
  231. package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
  232. package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
  233. package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
  234. package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
  235. package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
  236. package/src/memory/migrations/index.ts +6 -0
  237. package/src/memory/migrations/registry.ts +8 -0
  238. package/src/memory/retriever.test.ts +4 -5
  239. package/src/memory/schema/infrastructure.ts +31 -0
  240. package/src/memory/schema/oauth.ts +14 -0
  241. package/src/messaging/provider.ts +13 -12
  242. package/src/messaging/providers/gmail/adapter.ts +44 -35
  243. package/src/messaging/providers/slack/adapter.ts +63 -33
  244. package/src/messaging/providers/telegram-bot/adapter.ts +7 -9
  245. package/src/messaging/providers/whatsapp/adapter.ts +6 -8
  246. package/src/notifications/adapters/telegram.ts +78 -2
  247. package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
  248. package/src/oauth/byo-connection.test.ts +22 -24
  249. package/src/oauth/connect-orchestrator.ts +79 -64
  250. package/src/oauth/connect-types.ts +7 -65
  251. package/src/oauth/connection-resolver.test.ts +13 -13
  252. package/src/oauth/connection-resolver.ts +3 -4
  253. package/src/oauth/identity-verifier.ts +177 -0
  254. package/src/oauth/manual-token-connection.ts +5 -5
  255. package/src/oauth/oauth-store.ts +251 -5
  256. package/src/oauth/platform-connection.test.ts +56 -6
  257. package/src/oauth/platform-connection.ts +8 -1
  258. package/src/oauth/seed-providers.ts +256 -34
  259. package/src/permissions/checker.ts +129 -3
  260. package/src/permissions/trust-client.ts +2 -2
  261. package/src/platform/client.ts +2 -2
  262. package/src/prompts/journal-context.ts +6 -1
  263. package/src/prompts/system-prompt.ts +43 -9
  264. package/src/prompts/templates/BOOTSTRAP.md +16 -5
  265. package/src/providers/anthropic/client.ts +139 -28
  266. package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
  267. package/src/runtime/auth/route-policy.ts +0 -1
  268. package/src/runtime/btw-sidechain.ts +7 -1
  269. package/src/runtime/channel-approvals.ts +2 -2
  270. package/src/runtime/channel-readiness-service.ts +30 -7
  271. package/src/runtime/guardian-action-service.ts +7 -2
  272. package/src/runtime/http-router.ts +31 -0
  273. package/src/runtime/http-server.ts +26 -7
  274. package/src/runtime/http-types.ts +9 -0
  275. package/src/runtime/pending-interactions.ts +21 -3
  276. package/src/runtime/routes/acp-routes.ts +46 -28
  277. package/src/runtime/routes/app-management-routes.ts +123 -0
  278. package/src/runtime/routes/app-routes.ts +31 -0
  279. package/src/runtime/routes/approval-routes.ts +108 -3
  280. package/src/runtime/routes/attachment-routes.ts +45 -0
  281. package/src/runtime/routes/avatar-routes.ts +16 -0
  282. package/src/runtime/routes/brain-graph-routes.ts +18 -0
  283. package/src/runtime/routes/btw-routes.ts +20 -0
  284. package/src/runtime/routes/call-routes.ts +81 -0
  285. package/src/runtime/routes/channel-readiness-routes.ts +48 -7
  286. package/src/runtime/routes/channel-routes.ts +18 -0
  287. package/src/runtime/routes/channel-verification-routes.ts +49 -1
  288. package/src/runtime/routes/contact-routes.ts +77 -0
  289. package/src/runtime/routes/conversation-attention-routes.ts +37 -0
  290. package/src/runtime/routes/conversation-management-routes.ts +125 -0
  291. package/src/runtime/routes/conversation-query-routes.ts +78 -0
  292. package/src/runtime/routes/conversation-routes.ts +191 -39
  293. package/src/runtime/routes/conversation-starter-routes.ts +29 -0
  294. package/src/runtime/routes/debug-routes.ts +23 -0
  295. package/src/runtime/routes/diagnostics-routes.ts +30 -0
  296. package/src/runtime/routes/documents-routes.ts +42 -0
  297. package/src/runtime/routes/events-routes.ts +10 -0
  298. package/src/runtime/routes/global-search-routes.ts +35 -0
  299. package/src/runtime/routes/guardian-action-routes.ts +61 -3
  300. package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
  301. package/src/runtime/routes/heartbeat-routes.ts +278 -0
  302. package/src/runtime/routes/host-bash-routes.ts +16 -1
  303. package/src/runtime/routes/host-cu-routes.ts +23 -1
  304. package/src/runtime/routes/host-file-routes.ts +18 -1
  305. package/src/runtime/routes/identity-routes.ts +35 -0
  306. package/src/runtime/routes/inbound-message-handler.ts +46 -25
  307. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
  308. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
  309. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
  310. package/src/runtime/routes/integrations/slack/share.ts +1 -1
  311. package/src/runtime/routes/integrations/twilio.ts +32 -22
  312. package/src/runtime/routes/invite-routes.ts +83 -0
  313. package/src/runtime/routes/log-export-routes.ts +14 -0
  314. package/src/runtime/routes/memory-item-routes.ts +99 -1
  315. package/src/runtime/routes/migration-rollback-routes.ts +25 -0
  316. package/src/runtime/routes/migration-routes.ts +40 -0
  317. package/src/runtime/routes/notification-routes.ts +20 -0
  318. package/src/runtime/routes/oauth-apps.ts +13 -4
  319. package/src/runtime/routes/pairing-routes.ts +15 -0
  320. package/src/runtime/routes/recording-routes.ts +72 -0
  321. package/src/runtime/routes/schedule-routes.ts +77 -5
  322. package/src/runtime/routes/secret-routes.ts +99 -14
  323. package/src/runtime/routes/settings-routes.ts +102 -19
  324. package/src/runtime/routes/skills-routes.ts +141 -18
  325. package/src/runtime/routes/subagents-routes.ts +38 -3
  326. package/src/runtime/routes/surface-action-routes.ts +66 -24
  327. package/src/runtime/routes/surface-content-routes.ts +20 -0
  328. package/src/runtime/routes/telemetry-routes.ts +12 -0
  329. package/src/runtime/routes/trace-event-routes.ts +25 -0
  330. package/src/runtime/routes/trust-rules-routes.ts +46 -0
  331. package/src/runtime/routes/tts-routes.ts +15 -4
  332. package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
  333. package/src/runtime/routes/usage-routes.ts +59 -0
  334. package/src/runtime/routes/watch-routes.ts +28 -0
  335. package/src/runtime/routes/work-items-routes.ts +59 -0
  336. package/src/runtime/routes/workspace-commit-routes.ts +12 -0
  337. package/src/runtime/routes/workspace-routes.ts +102 -0
  338. package/src/schedule/integration-status.ts +2 -2
  339. package/src/schedule/scheduler.ts +7 -1
  340. package/src/security/AGENTS.md +7 -0
  341. package/src/security/ces-rpc-credential-backend.ts +19 -16
  342. package/src/security/credential-backend.ts +1 -1
  343. package/src/security/encrypted-store.ts +3 -3
  344. package/src/security/oauth-completion-page.ts +153 -0
  345. package/src/security/oauth2.ts +58 -17
  346. package/src/security/secret-ingress.ts +174 -0
  347. package/src/security/secret-patterns.ts +133 -0
  348. package/src/security/secret-scanner.ts +28 -117
  349. package/src/security/secure-keys.ts +207 -7
  350. package/src/security/token-manager.ts +3 -6
  351. package/src/signals/bash.ts +6 -1
  352. package/src/signals/confirm.ts +12 -8
  353. package/src/signals/user-message.ts +18 -3
  354. package/src/skills/catalog-cache.ts +44 -0
  355. package/src/skills/catalog-search.ts +18 -0
  356. package/src/skills/skill-memory.ts +1 -2
  357. package/src/tasks/task-runner.ts +7 -1
  358. package/src/tools/credentials/broker.ts +1 -1
  359. package/src/tools/credentials/metadata-store.ts +1 -1
  360. package/src/tools/credentials/post-connect-hooks.ts +1 -1
  361. package/src/tools/credentials/vault.ts +36 -48
  362. package/src/tools/host-terminal/host-shell.ts +16 -3
  363. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  364. package/src/tools/memory/definitions.ts +1 -1
  365. package/src/tools/memory/handlers.test.ts +2 -4
  366. package/src/tools/skills/load.ts +1 -1
  367. package/src/tools/skills/sandbox-runner.ts +16 -3
  368. package/src/tools/terminal/safe-env.ts +7 -0
  369. package/src/tools/terminal/shell.ts +16 -3
  370. package/src/tools/tool-manifest.ts +1 -1
  371. package/src/util/log-redact.ts +9 -34
  372. package/src/util/logger.ts +11 -1
  373. package/src/util/sentry-log-stream.ts +51 -0
  374. package/src/watcher/providers/github.ts +2 -2
  375. package/src/watcher/providers/gmail.ts +1 -1
  376. package/src/watcher/providers/google-calendar.ts +1 -1
  377. package/src/watcher/providers/linear.ts +2 -2
  378. package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
  379. package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
  380. package/src/workspace/migrations/registry.ts +2 -0
  381. package/docs/architecture/keychain-broker.md +0 -68
  382. package/src/cli/commands/oauth/connections.ts +0 -734
  383. package/src/cli/commands/oauth/platform.ts +0 -525
  384. package/src/cli/commands/platform.ts +0 -176
  385. package/src/config/bundled-skills/slack/TOOLS.json +0 -272
  386. package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
  387. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
  388. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
  389. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
  390. package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
  391. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
  392. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
  393. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
  394. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
  395. package/src/oauth/provider-behaviors.ts +0 -634
@@ -13,6 +13,7 @@ import {
13
13
  } from "../../channels/types.js";
14
14
  import { touchContactInteraction } from "../../contacts/contacts-write.js";
15
15
  import type { TrustContext } from "../../daemon/conversation-runtime-assembly.js";
16
+ import type { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
16
17
  import * as attachmentsStore from "../../memory/attachments-store.js";
17
18
  import {
18
19
  recordConversationSeenSignal,
@@ -20,6 +21,7 @@ import {
20
21
  } from "../../memory/conversation-attention-store.js";
21
22
  import * as deliveryChannels from "../../memory/delivery-channels.js";
22
23
  import * as deliveryCrud from "../../memory/delivery-crud.js";
24
+ import * as deliveryStatus from "../../memory/delivery-status.js";
23
25
  import * as externalConversationStore from "../../memory/external-conversation-store.js";
24
26
  import { canonicalizeInboundIdentity } from "../../util/canonicalize-identity.js";
25
27
  import { getLogger } from "../../util/logger.js";
@@ -57,6 +59,7 @@ export async function handleChannelInbound(
57
59
  approvalConversationGenerator?: ApprovalConversationGenerator,
58
60
  _guardianActionCopyGenerator?: GuardianActionCopyGenerator,
59
61
  _guardianFollowUpConversationGenerator?: GuardianFollowUpConversationGenerator,
62
+ heartbeatService?: HeartbeatService,
60
63
  ): Promise<Response> {
61
64
  // Gateway-origin proof is enforced by route-policy middleware (svc_gateway
62
65
  // principal type required) before this handler runs. The exchange JWT
@@ -641,7 +644,7 @@ export async function handleChannelInbound(
641
644
  // For new (non-duplicate) messages, run the secret ingress check
642
645
  // synchronously, then fire off the agent loop in the background.
643
646
  if (!result.duplicate && processMessage) {
644
- runSecretIngressCheck({
647
+ const ingressResult = runSecretIngressCheck({
645
648
  eventId: result.eventId,
646
649
  sourceChannel,
647
650
  conversationExternalId,
@@ -659,30 +662,48 @@ export async function handleChannelInbound(
659
662
  canonicalAssistantId,
660
663
  });
661
664
 
662
- // Fire-and-forget: process the message and deliver the reply in the background.
663
- // The HTTP response returns immediately so the gateway webhook is not blocked.
664
- // The onEvent callback in processMessage registers pending interactions, and
665
- // approval interception (above) handles decisions via the pending-interactions tracker.
666
- processChannelMessageInBackground({
667
- processMessage,
668
- conversationId: result.conversationId,
669
- eventId: result.eventId,
670
- content: trimmedContent,
671
- attachmentIds: hasAttachments ? attachmentIds : undefined,
672
- sourceChannel,
673
- sourceInterface,
674
- externalChatId: conversationExternalId,
675
- trustCtx,
676
- metadataHints,
677
- metadataUxBrief,
678
- commandIntent,
679
- sourceLanguageCode,
680
- replyCallbackUrl,
681
- mintBearerToken,
682
- assistantId: canonicalAssistantId,
683
- approvalCopyGenerator,
684
- chatType: sourceChatType,
685
- });
665
+ if (ingressResult.blocked) {
666
+ // Intentional block mark the event as processed (not failed/dead-lettered).
667
+ deliveryStatus.markProcessed(result.eventId);
668
+ log.info(
669
+ {
670
+ eventId: result.eventId,
671
+ detectedTypes: ingressResult.detectedTypes,
672
+ },
673
+ "Channel message blocked at ingress: contains secrets",
674
+ );
675
+ } else {
676
+ // Guardian messages reset the heartbeat timer so the next heartbeat
677
+ // fires a full interval after this interaction.
678
+ if (trustCtx.trustClass === "guardian") {
679
+ heartbeatService?.resetTimer();
680
+ }
681
+
682
+ // Fire-and-forget: process the message and deliver the reply in the background.
683
+ // The HTTP response returns immediately so the gateway webhook is not blocked.
684
+ // The onEvent callback in processMessage registers pending interactions, and
685
+ // approval interception (above) handles decisions via the pending-interactions tracker.
686
+ processChannelMessageInBackground({
687
+ processMessage,
688
+ conversationId: result.conversationId,
689
+ eventId: result.eventId,
690
+ content: trimmedContent,
691
+ attachmentIds: hasAttachments ? attachmentIds : undefined,
692
+ sourceChannel,
693
+ sourceInterface,
694
+ externalChatId: conversationExternalId,
695
+ trustCtx,
696
+ metadataHints,
697
+ metadataUxBrief,
698
+ commandIntent,
699
+ sourceLanguageCode,
700
+ replyCallbackUrl,
701
+ mintBearerToken,
702
+ assistantId: canonicalAssistantId,
703
+ approvalCopyGenerator,
704
+ chatType: sourceChatType,
705
+ });
706
+ }
686
707
  }
687
708
 
688
709
  return Response.json({
@@ -447,10 +447,11 @@ export async function enforceIngressAcl(
447
447
  );
448
448
  }
449
449
 
450
+ const replyText = guardianNotified
451
+ ? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you tried talking to me and get back to you.`
452
+ : "Sorry, you haven't been approved to message this assistant.";
453
+ let replyDelivered = false;
450
454
  if (replyCallbackUrl) {
451
- const replyText = guardianNotified
452
- ? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you tried talking to me and get back to you.`
453
- : "Sorry, you haven't been approved to message this assistant.";
454
455
  const replyPayload: Parameters<typeof deliverChannelReply>[1] = {
455
456
  chatId: conversationExternalId,
456
457
  text: replyText,
@@ -467,6 +468,7 @@ export async function enforceIngressAcl(
467
468
  replyPayload,
468
469
  mintBearerToken(),
469
470
  );
471
+ replyDelivered = true;
470
472
  } catch (err) {
471
473
  log.error(
472
474
  { err, conversationExternalId },
@@ -481,6 +483,9 @@ export async function enforceIngressAcl(
481
483
  accepted: true,
482
484
  denied: true,
483
485
  reason: "not_a_member",
486
+ // Include reply text so the gateway can deliver directly when
487
+ // callback delivery failed (e.g. signing-key mismatch → 401).
488
+ ...(!replyDelivered && { replyText }),
484
489
  }),
485
490
  guardianVerifyCode,
486
491
  };
@@ -714,15 +719,16 @@ export async function enforceIngressAcl(
714
719
  }
715
720
  }
716
721
 
722
+ const inactiveReplyText = guardianNotified
723
+ ? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you tried talking to me and get back to you.`
724
+ : "Sorry, you haven't been approved to message this assistant.";
725
+ let inactiveReplyDelivered = false;
717
726
  if (replyCallbackUrl) {
718
- const replyText = guardianNotified
719
- ? `Hmm looks like you don't have access to talk to me. I'll let ${resolveGuardianLabel(sourceChannel, canonicalAssistantId)} know you tried talking to me and get back to you.`
720
- : "Sorry, you haven't been approved to message this assistant.";
721
727
  const inactiveReplyPayload: Parameters<
722
728
  typeof deliverChannelReply
723
729
  >[1] = {
724
730
  chatId: conversationExternalId,
725
- text: replyText,
731
+ text: inactiveReplyText,
726
732
  assistantId,
727
733
  };
728
734
  // On Slack, send as ephemeral so only the requester sees the rejection
@@ -739,6 +745,7 @@ export async function enforceIngressAcl(
739
745
  inactiveReplyPayload,
740
746
  mintBearerToken(),
741
747
  );
748
+ inactiveReplyDelivered = true;
742
749
  } catch (err) {
743
750
  log.error(
744
751
  { err, conversationExternalId },
@@ -752,6 +759,7 @@ export async function enforceIngressAcl(
752
759
  accepted: true,
753
760
  denied: true,
754
761
  reason: `member_${channelStatusToMemberStatus(resolvedMember.channel.status)}`,
762
+ ...(!inactiveReplyDelivered && { replyText: inactiveReplyText }),
755
763
  }),
756
764
  guardianVerifyCode,
757
765
  };
@@ -763,10 +771,13 @@ export async function enforceIngressAcl(
763
771
  { sourceChannel, channelId: resolvedMember.channel.id },
764
772
  "Ingress ACL: member policy deny",
765
773
  );
774
+ const denyReplyText =
775
+ "Sorry, you haven't been approved to message this assistant.";
776
+ let denyReplyDelivered = false;
766
777
  if (replyCallbackUrl) {
767
778
  const denyPayload: Parameters<typeof deliverChannelReply>[1] = {
768
779
  chatId: conversationExternalId,
769
- text: "Sorry, you haven't been approved to message this assistant.",
780
+ text: denyReplyText,
770
781
  assistantId,
771
782
  };
772
783
  if (sourceChannel === "slack" && (canonicalSenderId ?? rawSenderId)) {
@@ -779,6 +790,7 @@ export async function enforceIngressAcl(
779
790
  denyPayload,
780
791
  mintBearerToken(),
781
792
  );
793
+ denyReplyDelivered = true;
782
794
  } catch (err) {
783
795
  log.error(
784
796
  { err, conversationExternalId },
@@ -792,6 +804,7 @@ export async function enforceIngressAcl(
792
804
  accepted: true,
793
805
  denied: true,
794
806
  reason: "policy_deny",
807
+ ...(!denyReplyDelivered && { replyText: denyReplyText }),
795
808
  }),
796
809
  guardianVerifyCode,
797
810
  };
@@ -9,6 +9,7 @@ import type { ChannelId } from "../../../channels/types.js";
9
9
  import type { TrustContext } from "../../../daemon/conversation-runtime-assembly.js";
10
10
  import { recordConversationSeenSignal } from "../../../memory/conversation-attention-store.js";
11
11
  import * as deliveryCrud from "../../../memory/delivery-crud.js";
12
+ import { checkIngressForSecrets } from "../../../security/secret-ingress.js";
12
13
  import { getLogger } from "../../../util/logger.js";
13
14
 
14
15
  const log = getLogger("runtime-http");
@@ -35,10 +36,21 @@ export interface SecretIngressCheckParams {
35
36
  canonicalAssistantId: string;
36
37
  }
37
38
 
39
+ export interface SecretIngressCheckResult {
40
+ blocked: boolean;
41
+ detectedTypes?: string[];
42
+ }
43
+
38
44
  /**
39
- * Persist the raw payload and record a Telegram seen signal.
45
+ * Persist the raw payload, scan for secrets, and record a Telegram seen signal.
46
+ *
47
+ * Returns `{ blocked: true, detectedTypes }` when the message contains
48
+ * known-format secrets — the caller should skip background dispatch and mark
49
+ * the event as processed (not failed/dead-lettered).
40
50
  */
41
- export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
51
+ export function runSecretIngressCheck(
52
+ params: SecretIngressCheckParams,
53
+ ): SecretIngressCheckResult {
42
54
  const {
43
55
  eventId,
44
56
  sourceChannel,
@@ -73,6 +85,20 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
73
85
  assistantId: canonicalAssistantId,
74
86
  });
75
87
 
88
+ // ── Secret ingress scan ──
89
+ // Scan trimmedContent (post-transcription) so secrets introduced via
90
+ // transcribed audio are also caught.
91
+ const ingressResult = checkIngressForSecrets(trimmedContent);
92
+ if (ingressResult.blocked) {
93
+ // Clear stored payload to prevent secret-bearing content on disk.
94
+ deliveryCrud.clearPayload(eventId);
95
+ log.warn(
96
+ { eventId, detectedTypes: ingressResult.detectedTypes },
97
+ "Channel message blocked at ingress: secret detected",
98
+ );
99
+ return { blocked: true, detectedTypes: ingressResult.detectedTypes };
100
+ }
101
+
76
102
  // Record inferred seen signal for non-duplicate Telegram inbound messages
77
103
  if (sourceChannel === "telegram") {
78
104
  try {
@@ -99,4 +125,6 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
99
125
  );
100
126
  }
101
127
  }
128
+
129
+ return { blocked: false };
102
130
  }
@@ -15,8 +15,7 @@ import { getLogger } from "../../../util/logger.js";
15
15
 
16
16
  const log = getLogger("transcribe-audio");
17
17
 
18
- const VOICE_TRANSCRIPTION_FLAG_KEY =
19
- "feature_flags.channel-voice-transcription.enabled" as const;
18
+ const VOICE_TRANSCRIPTION_FLAG_KEY = "channel-voice-transcription" as const;
20
19
 
21
20
  /** Timeout for the entire transcription pipeline (all attachments). */
22
21
  const TRANSCRIPTION_TIMEOUT_MS = 30_000;
@@ -28,7 +28,7 @@ const log = getLogger("slack-share");
28
28
  * Resolve the Slack bot token from the OAuth connection store.
29
29
  */
30
30
  async function resolveSlackToken(): Promise<string | undefined> {
31
- const conn = getConnectionByProvider("integration:slack");
31
+ const conn = getConnectionByProvider("slack");
32
32
  return conn
33
33
  ? await getSecureKeyAsync(`oauth_connection/${conn.id}/access_token`)
34
34
  : undefined;
@@ -271,17 +271,22 @@ export async function handleProvisionTwilioNumber(
271
271
  }
272
272
 
273
273
  let body: { country?: string; areaCode?: string };
274
- try {
275
- body = (await req.json()) as typeof body;
276
- } catch {
277
- return Response.json(
278
- {
279
- success: false,
280
- hasCredentials: await hasTwilioCredentials(),
281
- error: "Invalid JSON in request body",
282
- },
283
- { status: 400 },
284
- );
274
+ const provisionText = await req.text();
275
+ if (!provisionText.trim()) {
276
+ body = {};
277
+ } else {
278
+ try {
279
+ body = JSON.parse(provisionText) as typeof body;
280
+ } catch {
281
+ return Response.json(
282
+ {
283
+ success: false,
284
+ hasCredentials: await hasTwilioCredentials(),
285
+ error: "Invalid JSON in request body",
286
+ },
287
+ { status: 400 },
288
+ );
289
+ }
285
290
  }
286
291
  const { accountSid, authToken } = await getTwilioCredentials();
287
292
  const country = body.country ?? "US";
@@ -406,17 +411,22 @@ export async function handleReleaseTwilioNumber(
406
411
  }
407
412
 
408
413
  let body: { phoneNumber?: string };
409
- try {
410
- body = (await req.json()) as typeof body;
411
- } catch {
412
- return Response.json(
413
- {
414
- success: false,
415
- hasCredentials: await hasTwilioCredentials(),
416
- error: "Invalid JSON in request body",
417
- },
418
- { status: 400 },
419
- );
414
+ const releaseText = await req.text();
415
+ if (!releaseText.trim()) {
416
+ body = {};
417
+ } else {
418
+ try {
419
+ body = JSON.parse(releaseText) as typeof body;
420
+ } catch {
421
+ return Response.json(
422
+ {
423
+ success: false,
424
+ hasCredentials: await hasTwilioCredentials(),
425
+ error: "Invalid JSON in request body",
426
+ },
427
+ { status: 400 },
428
+ );
429
+ }
420
430
  }
421
431
  const raw = loadRawConfig();
422
432
  const twilio = (raw?.twilio ?? {}) as Record<string, unknown>;
@@ -9,6 +9,8 @@
9
9
  * POST /v1/contacts/invites/:id/call — trigger an outbound call for a phone invite
10
10
  */
11
11
 
12
+ import { z } from "zod";
13
+
12
14
  import type { RouteDefinition } from "../http-router.js";
13
15
  import {
14
16
  createIngressInvite,
@@ -169,28 +171,109 @@ export function inviteRouteDefinitions(): RouteDefinition[] {
169
171
  {
170
172
  endpoint: "contacts/invites",
171
173
  method: "GET",
174
+ summary: "List invites",
175
+ description:
176
+ "Return all invites, optionally filtered by sourceChannel or status.",
177
+ tags: ["contacts"],
178
+ queryParams: [
179
+ {
180
+ name: "sourceChannel",
181
+ schema: { type: "string" },
182
+ description: "Filter by source channel",
183
+ },
184
+ {
185
+ name: "status",
186
+ schema: { type: "string" },
187
+ description: "Filter by invite status",
188
+ },
189
+ ],
190
+ responseBody: z.object({
191
+ ok: z.boolean(),
192
+ invites: z.array(z.unknown()).describe("Invite objects"),
193
+ }),
172
194
  handler: ({ url }) => handleListInvites(url),
173
195
  },
174
196
  {
175
197
  endpoint: "contacts/invites",
176
198
  method: "POST",
199
+ summary: "Create an invite",
200
+ description:
201
+ 'Create a new invite. Supports voice invites when sourceChannel is "phone".',
202
+ tags: ["contacts"],
203
+ requestBody: z.object({
204
+ contactId: z.string().describe("Contact to invite"),
205
+ sourceChannel: z
206
+ .string()
207
+ .describe("Source channel (e.g. phone)")
208
+ .optional(),
209
+ note: z.string().describe("Optional note").optional(),
210
+ maxUses: z.number().describe("Max redemptions").optional(),
211
+ expiresInMs: z.number().describe("Expiry duration in ms").optional(),
212
+ contactName: z.string().describe("Contact display name").optional(),
213
+ expectedExternalUserId: z
214
+ .string()
215
+ .describe("Expected user ID (E.164 for phone)")
216
+ .optional(),
217
+ friendName: z
218
+ .string()
219
+ .describe("Friend name for the invite")
220
+ .optional(),
221
+ guardianName: z.string().describe("Guardian name").optional(),
222
+ }),
223
+ responseBody: z.object({
224
+ ok: z.boolean(),
225
+ invite: z.object({}).passthrough().describe("Created invite"),
226
+ }),
177
227
  handler: async ({ req }) => handleCreateInvite(req),
178
228
  },
179
229
  {
180
230
  endpoint: "contacts/invites/redeem",
181
231
  method: "POST",
232
+ summary: "Redeem an invite",
233
+ description: "Redeem an invite by token or voice code.",
234
+ tags: ["contacts"],
235
+ requestBody: z.object({
236
+ token: z.string().describe("Invite token (token-based redemption)"),
237
+ code: z.string().describe("Voice code (voice-code redemption)"),
238
+ callerExternalUserId: z
239
+ .string()
240
+ .describe("Caller E.164 phone (voice-code)"),
241
+ externalUserId: z.string().describe("External user ID (token-based)"),
242
+ externalChatId: z.string().describe("External chat ID (token-based)"),
243
+ sourceChannel: z.string().describe("Source channel (token-based)"),
244
+ assistantId: z.string().describe("Assistant ID (voice-code)"),
245
+ }),
246
+ responseBody: z.object({
247
+ ok: z.boolean(),
248
+ invite: z
249
+ .object({})
250
+ .passthrough()
251
+ .describe("Redeemed invite (token path)"),
252
+ type: z.string().describe("Redemption type (voice path)"),
253
+ memberId: z.string().describe("Member ID (voice path)"),
254
+ }),
182
255
  handler: async ({ req }) => handleRedeemInvite(req),
183
256
  },
184
257
  {
185
258
  endpoint: "contacts/invites/:id",
186
259
  method: "DELETE",
187
260
  policyKey: "contacts/invites",
261
+ summary: "Revoke an invite",
262
+ description: "Revoke an invite by ID.",
263
+ tags: ["contacts"],
188
264
  handler: ({ params }) => handleRevokeInvite(params.id),
189
265
  },
190
266
  {
191
267
  endpoint: "contacts/invites/:id/call",
192
268
  method: "POST",
193
269
  policyKey: "contacts/invites",
270
+ summary: "Trigger invite call",
271
+ description: "Trigger an outbound call for a phone invite.",
272
+ tags: ["contacts"],
273
+ responseBody: z.object({
274
+ ok: z.boolean(),
275
+ callSid: z.string().describe("Call SID from the provider"),
276
+ }),
194
277
  handler: async ({ params }) => handleTriggerInviteCall(params.id),
195
278
  },
196
279
  ];
@@ -22,6 +22,7 @@ import { tmpdir } from "node:os";
22
22
  import { join, relative } from "node:path";
23
23
 
24
24
  import { and, desc, eq, gte, lte } from "drizzle-orm";
25
+ import { z } from "zod";
25
26
 
26
27
  import { getDb } from "../../memory/db.js";
27
28
  import {
@@ -670,6 +671,19 @@ export function logExportRouteDefinitions(): RouteDefinition[] {
670
671
  endpoint: "export",
671
672
  method: "POST",
672
673
  policyKey: "export",
674
+ summary: "Export logs and audit data",
675
+ description:
676
+ "Export audit records, daemon logs, workspace contents, and config as a tar.gz archive.",
677
+ tags: ["export"],
678
+ requestBody: z.object({
679
+ auditLimit: z
680
+ .number()
681
+ .int()
682
+ .describe("Max audit records (default 1000)"),
683
+ conversationId: z.string().describe("Scope to a single conversation"),
684
+ startTime: z.number().describe("Lower bound epoch ms"),
685
+ endTime: z.number().describe("Upper bound epoch ms"),
686
+ }),
673
687
  handler: async ({ req }) => {
674
688
  const body = (await req.json()) as ExportRequestBody;
675
689
  return handleExport(body);
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { and, asc, count, desc, eq, inArray, like, ne, or } from "drizzle-orm";
12
12
  import { v4 as uuid } from "uuid";
13
+ import { z } from "zod";
13
14
 
14
15
  import { getConfig } from "../../config/loader.js";
15
16
  import { getDb } from "../../memory/db.js";
@@ -597,7 +598,11 @@ export async function handleUpdateMemoryItem(
597
598
  // If sourceType was set (either directly or via mapping), also write verificationState
598
599
  if (body.sourceType !== undefined && body.verificationState === undefined) {
599
600
  set.verificationState =
600
- body.sourceType === "tool" ? "user_confirmed" : "assistant_inferred";
601
+ body.sourceType === "tool"
602
+ ? "user_confirmed"
603
+ : existing.verificationState === "user_reported"
604
+ ? "user_reported"
605
+ : "assistant_inferred";
601
606
  }
602
607
 
603
608
  // If subject, statement, or kind changed, recompute fingerprint
@@ -717,29 +722,122 @@ export function memoryItemRouteDefinitions(): RouteDefinition[] {
717
722
  {
718
723
  endpoint: "memory-items",
719
724
  method: "GET",
725
+ summary: "List memory items",
726
+ description:
727
+ "Return memory items with filtering, search, sorting, and pagination.",
728
+ tags: ["memory"],
729
+ queryParams: [
730
+ {
731
+ name: "kind",
732
+ schema: { type: "string" },
733
+ description: "Filter by kind",
734
+ },
735
+ {
736
+ name: "status",
737
+ schema: { type: "string" },
738
+ description: "Filter by status (default active)",
739
+ },
740
+ {
741
+ name: "search",
742
+ schema: { type: "string" },
743
+ description: "Full-text search query",
744
+ },
745
+ {
746
+ name: "sort",
747
+ schema: { type: "string" },
748
+ description: "Sort field (default lastSeenAt)",
749
+ },
750
+ {
751
+ name: "order",
752
+ schema: { type: "string" },
753
+ description: "asc or desc (default desc)",
754
+ },
755
+ {
756
+ name: "limit",
757
+ schema: { type: "integer" },
758
+ description: "Max results (default 100)",
759
+ },
760
+ {
761
+ name: "offset",
762
+ schema: { type: "integer" },
763
+ description: "Pagination offset",
764
+ },
765
+ ],
766
+ responseBody: z.object({
767
+ items: z.array(z.unknown()).describe("Memory item objects"),
768
+ total: z.number(),
769
+ }),
720
770
  handler: (ctx) => handleListMemoryItems(ctx.url),
721
771
  },
722
772
  {
723
773
  endpoint: "memory-items/:id",
724
774
  method: "GET",
725
775
  policyKey: "memory-items",
776
+ summary: "Get a memory item",
777
+ description:
778
+ "Return a single memory item by ID with supersession metadata.",
779
+ tags: ["memory"],
780
+ responseBody: z.object({
781
+ item: z
782
+ .object({})
783
+ .passthrough()
784
+ .describe("Memory item with scopeLabel and supersession info"),
785
+ }),
726
786
  handler: (ctx) => handleGetMemoryItem(ctx),
727
787
  },
728
788
  {
729
789
  endpoint: "memory-items",
730
790
  method: "POST",
791
+ summary: "Create a memory item",
792
+ description: "Create a new memory item and enqueue embedding.",
793
+ tags: ["memory"],
794
+ requestBody: z.object({
795
+ kind: z
796
+ .string()
797
+ .describe("Memory kind (identity, preference, project, etc.)"),
798
+ subject: z.string().describe("Subject line"),
799
+ statement: z.string().describe("Statement content"),
800
+ importance: z
801
+ .number()
802
+ .describe("Importance score (default 0.8)")
803
+ .optional(),
804
+ }),
805
+ responseBody: z.object({
806
+ item: z.object({}).passthrough().describe("Created memory item"),
807
+ }),
731
808
  handler: (ctx) => handleCreateMemoryItem(ctx),
732
809
  },
733
810
  {
734
811
  endpoint: "memory-items/:id",
735
812
  method: "PATCH",
736
813
  policyKey: "memory-items",
814
+ summary: "Update a memory item",
815
+ description: "Partially update fields on an existing memory item.",
816
+ tags: ["memory"],
817
+ requestBody: z.object({
818
+ subject: z.string(),
819
+ statement: z.string(),
820
+ kind: z.string(),
821
+ status: z.string(),
822
+ importance: z.number(),
823
+ sourceType: z.string(),
824
+ verificationState: z.string(),
825
+ }),
826
+ responseBody: z.object({
827
+ item: z.object({}).passthrough().describe("Updated memory item"),
828
+ }),
737
829
  handler: (ctx) => handleUpdateMemoryItem(ctx),
738
830
  },
739
831
  {
740
832
  endpoint: "memory-items/:id",
741
833
  method: "DELETE",
742
834
  policyKey: "memory-items",
835
+ summary: "Delete a memory item",
836
+ description: "Delete a memory item and its embeddings.",
837
+ tags: ["memory"],
838
+ responseBody: z.object({
839
+ ok: z.boolean(),
840
+ }),
743
841
  handler: (ctx) => handleDeleteMemoryItem(ctx),
744
842
  },
745
843
  ];