@vellumai/assistant 0.5.16 → 0.6.0

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 (407) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +0 -3
  3. package/knip.json +2 -1
  4. package/openapi.yaml +660 -80
  5. package/package.json +1 -1
  6. package/src/__tests__/actor-token-service.test.ts +68 -0
  7. package/src/__tests__/agent-loop.test.ts +0 -32
  8. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  9. package/src/__tests__/anthropic-provider.test.ts +57 -3
  10. package/src/__tests__/app-compiler.test.ts +120 -0
  11. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  12. package/src/__tests__/call-conversation-messages.test.ts +2 -6
  13. package/src/__tests__/call-domain.test.ts +2 -6
  14. package/src/__tests__/call-pointer-messages.test.ts +2 -14
  15. package/src/__tests__/call-recovery.test.ts +2 -6
  16. package/src/__tests__/call-routes-http.test.ts +2 -6
  17. package/src/__tests__/call-store.test.ts +2 -6
  18. package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
  19. package/src/__tests__/canonical-guardian-store.test.ts +2 -6
  20. package/src/__tests__/channel-delivery-store.test.ts +2 -6
  21. package/src/__tests__/channel-retry-sweep.test.ts +2 -6
  22. package/src/__tests__/checker.test.ts +25 -3
  23. package/src/__tests__/clawhub.test.ts +54 -24
  24. package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
  25. package/src/__tests__/cli-memory.test.ts +74 -69
  26. package/src/__tests__/config-schema.test.ts +1 -1
  27. package/src/__tests__/config-set-platform-guard.test.ts +302 -0
  28. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
  29. package/src/__tests__/contacts-tools.test.ts +31 -0
  30. package/src/__tests__/context-overflow-reducer.test.ts +86 -0
  31. package/src/__tests__/context-token-estimator.test.ts +175 -10
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +9 -0
  34. package/src/__tests__/conversation-attachments.test.ts +2 -6
  35. package/src/__tests__/conversation-attention-store.test.ts +2 -6
  36. package/src/__tests__/conversation-clear-safety.test.ts +2 -6
  37. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
  38. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
  39. package/src/__tests__/conversation-disk-view.test.ts +2 -6
  40. package/src/__tests__/conversation-error.test.ts +33 -2
  41. package/src/__tests__/conversation-fork-crud.test.ts +2 -6
  42. package/src/__tests__/conversation-history-web-search.test.ts +5 -0
  43. package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
  44. package/src/__tests__/conversation-media-retry.test.ts +91 -0
  45. package/src/__tests__/conversation-starter-routes.test.ts +20 -11
  46. package/src/__tests__/conversation-store.test.ts +2 -6
  47. package/src/__tests__/conversation-usage.test.ts +2 -6
  48. package/src/__tests__/conversation-wipe.test.ts +11 -408
  49. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  50. package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
  51. package/src/__tests__/credential-security-e2e.test.ts +2 -0
  52. package/src/__tests__/followup-tools.test.ts +2 -6
  53. package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
  54. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
  55. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
  56. package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
  57. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
  58. package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
  59. package/src/__tests__/guardian-action-store.test.ts +2 -6
  60. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
  61. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
  62. package/src/__tests__/guardian-dispatch.test.ts +2 -6
  63. package/src/__tests__/guardian-grant-minting.test.ts +2 -14
  64. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
  65. package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
  66. package/src/__tests__/guardian-routing-state.test.ts +2 -6
  67. package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
  68. package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
  69. package/src/__tests__/injection-block.test.ts +154 -0
  70. package/src/__tests__/install-meta.test.ts +506 -0
  71. package/src/__tests__/install-skill-routing.test.ts +292 -0
  72. package/src/__tests__/invite-redemption-service.test.ts +2 -6
  73. package/src/__tests__/invite-routes-http.test.ts +2 -6
  74. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
  75. package/src/__tests__/list-messages-attachments.test.ts +2 -6
  76. package/src/__tests__/llm-context-route-provider.test.ts +2 -6
  77. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
  78. package/src/__tests__/llm-usage-store.test.ts +2 -6
  79. package/src/__tests__/log-export-workspace.test.ts +2 -6
  80. package/src/__tests__/managed-store.test.ts +38 -11
  81. package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
  82. package/src/__tests__/memory-recall-log-store.test.ts +2 -6
  83. package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
  84. package/src/__tests__/non-member-access-request.test.ts +2 -6
  85. package/src/__tests__/notification-guardian-path.test.ts +2 -6
  86. package/src/__tests__/oauth-cli.test.ts +364 -2
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
  88. package/src/__tests__/outlook-attachments.test.ts +301 -0
  89. package/src/__tests__/outlook-automation-tools.test.ts +425 -0
  90. package/src/__tests__/outlook-categories.test.ts +212 -0
  91. package/src/__tests__/outlook-client-automation.test.ts +246 -0
  92. package/src/__tests__/outlook-compose-tools.test.ts +325 -0
  93. package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
  94. package/src/__tests__/outlook-email-watcher.test.ts +322 -0
  95. package/src/__tests__/outlook-follow-up.test.ts +196 -0
  96. package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
  97. package/src/__tests__/outlook-trash.test.ts +77 -0
  98. package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
  99. package/src/__tests__/platform-callback-registration.test.ts +4 -4
  100. package/src/__tests__/playbook-execution.test.ts +76 -80
  101. package/src/__tests__/playbook-tools.test.ts +5 -7
  102. package/src/__tests__/provider-error-scenarios.test.ts +21 -0
  103. package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
  104. package/src/__tests__/registry.test.ts +2 -2
  105. package/src/__tests__/require-fresh-approval.test.ts +64 -2
  106. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
  107. package/src/__tests__/runtime-events-sse.test.ts +2 -6
  108. package/src/__tests__/schedule-store.test.ts +2 -6
  109. package/src/__tests__/schedule-tools.test.ts +2 -6
  110. package/src/__tests__/scheduler-recurrence.test.ts +1 -5
  111. package/src/__tests__/scoped-approval-grants.test.ts +2 -6
  112. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
  113. package/src/__tests__/search-skills-unified.test.ts +421 -0
  114. package/src/__tests__/secret-onetime-send.test.ts +2 -0
  115. package/src/__tests__/send-endpoint-busy.test.ts +2 -6
  116. package/src/__tests__/sequence-store.test.ts +2 -6
  117. package/src/__tests__/server-history-render.test.ts +2 -6
  118. package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
  119. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  120. package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
  121. package/src/__tests__/skill-memory.test.ts +140 -98
  122. package/src/__tests__/skills-uninstall.test.ts +2 -2
  123. package/src/__tests__/skills.test.ts +1 -1
  124. package/src/__tests__/slack-inbound-verification.test.ts +2 -6
  125. package/src/__tests__/task-compiler.test.ts +2 -6
  126. package/src/__tests__/task-management-tools.test.ts +2 -6
  127. package/src/__tests__/task-memory-cleanup.test.ts +173 -229
  128. package/src/__tests__/task-runner.test.ts +2 -6
  129. package/src/__tests__/task-scheduler.test.ts +2 -6
  130. package/src/__tests__/test-preload.ts +3 -0
  131. package/src/__tests__/tool-approval-handler.test.ts +2 -6
  132. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
  133. package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
  134. package/src/__tests__/trust-store.test.ts +1 -1
  135. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
  136. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
  137. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
  138. package/src/__tests__/trusted-contact-verification.test.ts +2 -6
  139. package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
  140. package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
  141. package/src/__tests__/usage-routes.test.ts +2 -6
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  143. package/src/__tests__/voice-invite-redemption.test.ts +2 -6
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
  145. package/src/__tests__/voice-session-bridge.test.ts +2 -6
  146. package/src/__tests__/volume-security-guard.test.ts +2 -0
  147. package/src/__tests__/workspace-lifecycle.test.ts +29 -1
  148. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
  149. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
  150. package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
  151. package/src/__tests__/workspace-policy.test.ts +1 -1
  152. package/src/agent/attachments.ts +7 -2
  153. package/src/agent/image-optimize.ts +165 -0
  154. package/src/agent/loop.ts +1 -15
  155. package/src/bundler/app-compiler.ts +179 -2
  156. package/src/bundler/package-resolver.ts +3 -5
  157. package/src/cli/__tests__/notifications.test.ts +1 -2
  158. package/src/cli/cli-memory.ts +67 -64
  159. package/src/cli/commands/avatar.ts +3 -3
  160. package/src/cli/commands/config.ts +26 -13
  161. package/src/cli/commands/doctor.ts +2 -2
  162. package/src/cli/commands/memory.ts +41 -55
  163. package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
  164. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
  165. package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
  166. package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
  167. package/src/cli/commands/oauth/connect.ts +11 -6
  168. package/src/cli/commands/oauth/mode.ts +7 -0
  169. package/src/cli/commands/oauth/shared.ts +39 -3
  170. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  171. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  172. package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
  173. package/src/cli/commands/platform/index.ts +16 -16
  174. package/src/cli/commands/skills.ts +88 -16
  175. package/src/cli/commands/trust.ts +2 -2
  176. package/src/cli/lib/daemon-credential-client.ts +2 -3
  177. package/src/config/bundled-skills/acp/TOOLS.json +1 -1
  178. package/src/config/bundled-skills/contacts/SKILL.md +0 -1
  179. package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
  180. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
  181. package/src/config/bundled-skills/gmail/SKILL.md +2 -10
  182. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
  183. package/src/config/bundled-skills/messaging/SKILL.md +10 -18
  184. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
  185. package/src/config/bundled-skills/outlook/SKILL.md +189 -0
  186. package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
  187. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
  188. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
  189. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
  190. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
  191. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
  192. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
  193. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
  194. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
  195. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
  196. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
  197. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
  198. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
  199. package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
  200. package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
  201. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
  202. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
  203. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
  204. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
  205. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
  206. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
  207. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
  208. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
  209. package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
  210. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
  211. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
  212. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
  213. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
  214. package/src/config/bundled-skills/slack/SKILL.md +1 -7
  215. package/src/config/bundled-tool-registry.ts +56 -4
  216. package/src/config/env-registry.ts +15 -8
  217. package/src/config/feature-flag-registry.json +21 -124
  218. package/src/config/schemas/platform.ts +8 -0
  219. package/src/config/schemas/timeouts.ts +1 -1
  220. package/src/config/skills.ts +18 -7
  221. package/src/context/token-estimator.ts +25 -18
  222. package/src/context/window-manager.ts +6 -2
  223. package/src/credential-execution/process-manager.ts +3 -1
  224. package/src/daemon/context-overflow-reducer.ts +46 -2
  225. package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
  226. package/src/daemon/conversation-agent-loop.ts +96 -61
  227. package/src/daemon/conversation-error.ts +31 -8
  228. package/src/daemon/conversation-lifecycle.ts +33 -0
  229. package/src/daemon/conversation-media-retry.ts +85 -7
  230. package/src/daemon/conversation-notifiers.ts +4 -1
  231. package/src/daemon/conversation-runtime-assembly.ts +5 -0
  232. package/src/daemon/conversation.ts +41 -2
  233. package/src/daemon/daemon-control.ts +8 -2
  234. package/src/daemon/handlers/shared.ts +22 -12
  235. package/src/daemon/handlers/skills.ts +416 -202
  236. package/src/daemon/lifecycle.ts +40 -1
  237. package/src/daemon/main.ts +5 -1
  238. package/src/daemon/message-types/conversations.ts +4 -1
  239. package/src/daemon/message-types/messages.ts +3 -1
  240. package/src/daemon/message-types/skills.ts +97 -36
  241. package/src/daemon/providers-setup.ts +5 -0
  242. package/src/daemon/server.ts +11 -2
  243. package/src/daemon/tool-side-effects.ts +27 -5
  244. package/src/heartbeat/heartbeat-service.ts +1 -0
  245. package/src/hooks/cli.ts +2 -2
  246. package/src/hooks/runner.ts +15 -38
  247. package/src/inbound/platform-callback-registration.ts +14 -14
  248. package/src/memory/admin.ts +11 -45
  249. package/src/memory/conversation-bootstrap.ts +2 -0
  250. package/src/memory/conversation-crud.ts +242 -348
  251. package/src/memory/conversation-group-migration.ts +157 -0
  252. package/src/memory/conversation-queries.ts +4 -2
  253. package/src/memory/db-init.ts +30 -3
  254. package/src/memory/embed.ts +73 -0
  255. package/src/memory/embedding-backend.ts +8 -14
  256. package/src/memory/embedding-runtime-manager.ts +12 -114
  257. package/src/memory/fingerprint.ts +2 -2
  258. package/src/memory/graph/bootstrap.ts +512 -0
  259. package/src/memory/graph/capability-seed.ts +297 -0
  260. package/src/memory/graph/consolidation.ts +691 -0
  261. package/src/memory/graph/conversation-graph-memory.ts +630 -0
  262. package/src/memory/graph/decay.test.ts +208 -0
  263. package/src/memory/graph/decay.ts +195 -0
  264. package/src/memory/graph/extraction-job.ts +69 -0
  265. package/src/memory/graph/extraction.test.ts +936 -0
  266. package/src/memory/graph/extraction.ts +1254 -0
  267. package/src/memory/graph/graph-search.ts +266 -0
  268. package/src/memory/graph/image-ref-utils.ts +29 -0
  269. package/src/memory/graph/injection.test.ts +513 -0
  270. package/src/memory/graph/injection.ts +439 -0
  271. package/src/memory/graph/inspect.ts +534 -0
  272. package/src/memory/graph/narrative.ts +267 -0
  273. package/src/memory/graph/pattern-scan.ts +269 -0
  274. package/src/memory/graph/retriever.ts +1008 -0
  275. package/src/memory/graph/scoring.test.ts +548 -0
  276. package/src/memory/graph/scoring.ts +232 -0
  277. package/src/memory/graph/serendipity.ts +65 -0
  278. package/src/memory/graph/store.test.ts +1050 -0
  279. package/src/memory/graph/store.ts +699 -0
  280. package/src/memory/graph/tool-handlers.ts +426 -0
  281. package/src/memory/graph/tools.ts +141 -0
  282. package/src/memory/graph/triggers.test.ts +487 -0
  283. package/src/memory/graph/triggers.ts +223 -0
  284. package/src/memory/graph/types.ts +271 -0
  285. package/src/memory/group-crud.ts +191 -0
  286. package/src/memory/indexer.ts +37 -19
  287. package/src/memory/job-handlers/cleanup.ts +0 -53
  288. package/src/memory/job-handlers/conversation-starters.ts +91 -53
  289. package/src/memory/job-handlers/embedding.ts +5 -31
  290. package/src/memory/job-handlers/index-maintenance.ts +23 -11
  291. package/src/memory/job-handlers/summarization.ts +32 -17
  292. package/src/memory/job-utils.ts +1 -1
  293. package/src/memory/jobs-store.ts +50 -70
  294. package/src/memory/jobs-worker.ts +147 -112
  295. package/src/memory/message-content.ts +1 -0
  296. package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
  297. package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
  298. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
  299. package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
  300. package/src/memory/migrations/index.ts +4 -0
  301. package/src/memory/migrations/registry.ts +8 -0
  302. package/src/memory/qdrant-client.ts +44 -17
  303. package/src/memory/schema/index.ts +1 -0
  304. package/src/memory/schema/memory-graph.ts +139 -0
  305. package/src/memory/search/semantic.ts +47 -91
  306. package/src/memory/task-memory-cleanup.ts +28 -50
  307. package/src/messaging/providers/outlook/adapter.ts +8 -1
  308. package/src/messaging/providers/outlook/client.ts +299 -0
  309. package/src/messaging/providers/outlook/types.ts +118 -0
  310. package/src/notifications/adapters/macos.ts +1 -0
  311. package/src/notifications/copy-composer.ts +9 -0
  312. package/src/notifications/signal.ts +16 -0
  313. package/src/oauth/seed-providers.ts +2 -1
  314. package/src/permissions/checker.ts +24 -3
  315. package/src/permissions/defaults.ts +4 -4
  316. package/src/permissions/workspace-policy.ts +1 -1
  317. package/src/playbooks/playbook-compiler.ts +19 -18
  318. package/src/playbooks/types.ts +4 -3
  319. package/src/prompts/system-prompt.ts +3 -29
  320. package/src/providers/anthropic/client.ts +47 -19
  321. package/src/providers/gemini/client.ts +1 -1
  322. package/src/providers/openai/client.ts +1 -1
  323. package/src/providers/registry.ts +1 -1
  324. package/src/providers/retry.ts +19 -3
  325. package/src/runtime/actor-trust-resolver.ts +5 -1
  326. package/src/runtime/auth/route-policy.ts +7 -0
  327. package/src/runtime/guardian-reply-router.ts +5 -1
  328. package/src/runtime/http-server.ts +23 -3
  329. package/src/runtime/middleware/auth.ts +20 -0
  330. package/src/runtime/routes/attachment-routes.test.ts +106 -0
  331. package/src/runtime/routes/attachment-routes.ts +106 -16
  332. package/src/runtime/routes/brain-graph-routes.ts +21 -22
  333. package/src/runtime/routes/btw-routes.ts +8 -0
  334. package/src/runtime/routes/conversation-management-routes.ts +2 -0
  335. package/src/runtime/routes/conversation-starter-routes.ts +2 -2
  336. package/src/runtime/routes/debug-routes.ts +1 -1
  337. package/src/runtime/routes/global-search-routes.ts +21 -19
  338. package/src/runtime/routes/group-routes.ts +207 -0
  339. package/src/runtime/routes/guardian-action-routes.ts +21 -10
  340. package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
  341. package/src/runtime/routes/inbound-message-handler.ts +19 -0
  342. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
  343. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +2 -14
  345. package/src/runtime/routes/memory-item-routes.ts +341 -388
  346. package/src/runtime/routes/schedule-routes.ts +2 -0
  347. package/src/runtime/routes/skills-routes.ts +103 -37
  348. package/src/runtime/routes/work-items-routes.test.ts +2 -6
  349. package/src/schedule/scheduler.ts +8 -1
  350. package/src/security/oauth2.ts +1 -1
  351. package/src/security/secure-keys.ts +4 -8
  352. package/src/shared/provider-env-vars.ts +19 -0
  353. package/src/skills/catalog-cache.ts +5 -0
  354. package/src/skills/catalog-install.ts +15 -14
  355. package/src/skills/clawhub.ts +134 -154
  356. package/src/skills/install-meta.ts +208 -0
  357. package/src/skills/managed-store.ts +27 -16
  358. package/src/skills/skill-memory.ts +152 -77
  359. package/src/skills/skillssh-registry.ts +19 -17
  360. package/src/tasks/task-runner.ts +3 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
  362. package/src/tools/browser/runtime-check.ts +3 -1
  363. package/src/tools/memory/register.ts +63 -46
  364. package/src/tools/permission-checker.ts +7 -1
  365. package/src/tools/shared/filesystem/image-read.ts +22 -85
  366. package/src/tools/terminal/safe-env.ts +1 -0
  367. package/src/tools/tool-manifest.ts +3 -3
  368. package/src/util/browser.ts +25 -10
  369. package/src/util/bun-runtime.ts +172 -0
  370. package/src/watcher/providers/outlook-calendar.ts +343 -0
  371. package/src/watcher/providers/outlook.ts +198 -0
  372. package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
  373. package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
  374. package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
  375. package/src/workspace/migrations/registry.ts +6 -0
  376. package/src/__tests__/context-memory-e2e.test.ts +0 -415
  377. package/src/__tests__/journal-context.test.ts +0 -268
  378. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
  379. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
  380. package/src/__tests__/memory-query-builder.test.ts +0 -59
  381. package/src/__tests__/memory-recall-quality.test.ts +0 -1046
  382. package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
  383. package/src/__tests__/memory-regressions.test.ts +0 -3696
  384. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
  385. package/src/daemon/conversation-memory.ts +0 -207
  386. package/src/memory/conversation-starters-cadence.ts +0 -74
  387. package/src/memory/items-extractor.ts +0 -860
  388. package/src/memory/job-handlers/batch-extraction.ts +0 -753
  389. package/src/memory/job-handlers/extraction.ts +0 -40
  390. package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
  391. package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
  392. package/src/memory/journal-memory.ts +0 -224
  393. package/src/memory/query-builder.ts +0 -47
  394. package/src/memory/query-expansion.ts +0 -83
  395. package/src/memory/retriever.test.ts +0 -1592
  396. package/src/memory/retriever.ts +0 -1331
  397. package/src/memory/search/formatting.test.ts +0 -140
  398. package/src/memory/search/formatting.ts +0 -262
  399. package/src/memory/search/mmr.ts +0 -139
  400. package/src/memory/search/ranking.ts +0 -15
  401. package/src/memory/search/staleness.ts +0 -40
  402. package/src/memory/search/tier-classifier.ts +0 -18
  403. package/src/memory/search/types.ts +0 -121
  404. package/src/prompts/journal-context.ts +0 -154
  405. package/src/tools/memory/definitions.ts +0 -69
  406. package/src/tools/memory/handlers.test.ts +0 -562
  407. package/src/tools/memory/handlers.ts +0 -434
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { readFileSync } from "node:fs";
18
18
  import { join, resolve } from "node:path";
19
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
19
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
20
20
 
21
21
  mock.module("../config/env.js", () => ({ isHttpAuthDisabled: () => true }));
22
22
 
@@ -38,12 +38,17 @@ import {
38
38
  createCanonicalGuardianRequest,
39
39
  getCanonicalGuardianRequest,
40
40
  } from "../memory/canonical-guardian-store.js";
41
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
41
+ import { getDb, initializeDb } from "../memory/db.js";
42
+ import {
43
+ buildDecisionActions,
44
+ GUARDIAN_DECISION_ACTIONS,
45
+ } from "../runtime/guardian-decision-types.js";
42
46
  import {
43
47
  type GuardianReplyContext,
44
48
  routeGuardianReply,
45
49
  } from "../runtime/guardian-reply-router.js";
46
50
  import * as pendingInteractions from "../runtime/pending-interactions.js";
51
+ import { listGuardianDecisionPrompts } from "../runtime/routes/guardian-action-routes.js";
47
52
 
48
53
  initializeDb();
49
54
 
@@ -55,10 +60,6 @@ function resetTables(): void {
55
60
  pendingInteractions.clear();
56
61
  }
57
62
 
58
- afterAll(() => {
59
- resetDb();
60
- });
61
-
62
63
  // ---------------------------------------------------------------------------
63
64
  // Helpers
64
65
  // ---------------------------------------------------------------------------
@@ -734,6 +735,62 @@ describe("routing invariant: channel formatting delimiters stripped from code pa
734
735
  const resolved = getCanonicalGuardianRequest(req.id);
735
736
  expect(resolved!.status).toBe("approved");
736
737
  });
738
+
739
+ test("*CODE* action — formatting wraps only the code portion", async () => {
740
+ const req = createCanonicalGuardianRequest({
741
+ kind: "tool_approval",
742
+ sourceType: "channel",
743
+ conversationId: "conv-1",
744
+ guardianExternalUserId: "guardian-1",
745
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
746
+ requestCode: "A1B2C3",
747
+ toolName: "shell",
748
+ inputDigest: "sha256:abc",
749
+ expiresAt: Date.now() + 60_000,
750
+ });
751
+ registerPendingToolApprovalInteraction(req.id, "conv-1", "shell");
752
+
753
+ const result = await routeGuardianReply(
754
+ replyCtx({
755
+ messageText: "*A1B2C3* approve",
756
+ conversationId: "conv-1",
757
+ }),
758
+ );
759
+
760
+ expect(result.consumed).toBe(true);
761
+ expect(result.type).toBe("canonical_decision_applied");
762
+ expect(result.decisionApplied).toBe(true);
763
+
764
+ const resolved = getCanonicalGuardianRequest(req.id);
765
+ expect(resolved!.status).toBe("approved");
766
+ });
767
+
768
+ test("**CODE** action — double-asterisk formatting wraps only the code portion", async () => {
769
+ const req = createCanonicalGuardianRequest({
770
+ kind: "tool_approval",
771
+ sourceType: "channel",
772
+ conversationId: "conv-1",
773
+ guardianExternalUserId: "guardian-1",
774
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
775
+ requestCode: "D4E5F6",
776
+ expiresAt: Date.now() + 60_000,
777
+ });
778
+ registerPendingToolApprovalInteraction(req.id, "conv-1", "shell");
779
+
780
+ const result = await routeGuardianReply(
781
+ replyCtx({
782
+ messageText: "**D4E5F6** reject",
783
+ conversationId: "conv-1",
784
+ }),
785
+ );
786
+
787
+ expect(result.consumed).toBe(true);
788
+ expect(result.type).toBe("canonical_decision_applied");
789
+ expect(result.decisionApplied).toBe(true);
790
+
791
+ const resolved = getCanonicalGuardianRequest(req.id);
792
+ expect(resolved!.status).toBe("denied");
793
+ });
737
794
  });
738
795
 
739
796
  // ===========================================================================
@@ -1649,3 +1706,132 @@ describe("routing invariant: expired requests are excluded from pending discover
1649
1706
  expect(result.decisionApplied).toBe(false);
1650
1707
  });
1651
1708
  });
1709
+
1710
+ // ===========================================================================
1711
+ // SECTION 12: Kind-specific action sets in prompt mapping
1712
+ // ===========================================================================
1713
+
1714
+ describe("routing invariant: kind-specific action sets in prompt mapping", () => {
1715
+ beforeEach(() => resetTables());
1716
+
1717
+ test("buildDecisionActions({ forGuardianOnBehalf: true }) includes temporal actions", () => {
1718
+ const actions = buildDecisionActions({ forGuardianOnBehalf: true });
1719
+ const actionIds = actions.map((a) => a.action);
1720
+ expect(actionIds).toContain("approve_once");
1721
+ expect(actionIds).toContain("approve_10m");
1722
+ expect(actionIds).toContain("approve_conversation");
1723
+ expect(actionIds).toContain("reject");
1724
+ // approve_always must NOT be present for guardian-on-behalf
1725
+ expect(actionIds).not.toContain("approve_always");
1726
+ });
1727
+
1728
+ test("non-tool-approval action set is approve_once + reject only", () => {
1729
+ const actions = [
1730
+ GUARDIAN_DECISION_ACTIONS.approve_once,
1731
+ GUARDIAN_DECISION_ACTIONS.reject,
1732
+ ];
1733
+ expect(actions).toHaveLength(2);
1734
+ expect(actions[0].action).toBe("approve_once");
1735
+ expect(actions[1].action).toBe("reject");
1736
+ });
1737
+
1738
+ test("source-code invariant: guardian-action-routes.ts contains kind guard", () => {
1739
+ const srcRoot = resolve(__dirname, "..");
1740
+ const fullPath = join(srcRoot, "runtime/routes/guardian-action-routes.ts");
1741
+ const source = readFileSync(fullPath, "utf-8");
1742
+ expect(source).toContain('req.kind === "tool_approval"');
1743
+ });
1744
+
1745
+ // Integration tests: verify listGuardianDecisionPrompts returns correct
1746
+ // action sets for each canonical request kind.
1747
+
1748
+ test("tool_approval prompt includes temporal actions (approve_10m, approve_conversation)", () => {
1749
+ const convId = "conv-kind-tool-approval";
1750
+ createCanonicalGuardianRequest({
1751
+ kind: "tool_approval",
1752
+ sourceType: "channel",
1753
+ conversationId: convId,
1754
+ guardianExternalUserId: "guardian-1",
1755
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
1756
+ toolName: "shell",
1757
+ expiresAt: Date.now() + 60_000,
1758
+ });
1759
+
1760
+ const prompts = listGuardianDecisionPrompts({ conversationId: convId });
1761
+ expect(prompts).toHaveLength(1);
1762
+
1763
+ const actionIds = prompts[0].actions.map((a) => a.action);
1764
+ expect(actionIds).toContain("approve_once");
1765
+ expect(actionIds).toContain("approve_10m");
1766
+ expect(actionIds).toContain("approve_conversation");
1767
+ expect(actionIds).toContain("reject");
1768
+ expect(actionIds).not.toContain("approve_always");
1769
+ });
1770
+
1771
+ test("pending_question prompt has approve_once + reject only (no temporal actions)", () => {
1772
+ const convId = "conv-kind-pending-question";
1773
+ createCanonicalGuardianRequest({
1774
+ kind: "pending_question",
1775
+ sourceType: "voice",
1776
+ sourceChannel: "phone",
1777
+ conversationId: convId,
1778
+ guardianExternalUserId: "guardian-1",
1779
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
1780
+ callSessionId: "call-pq",
1781
+ pendingQuestionId: "pq-kind-test",
1782
+ questionText: "What time works best?",
1783
+ expiresAt: Date.now() + 60_000,
1784
+ });
1785
+
1786
+ const prompts = listGuardianDecisionPrompts({ conversationId: convId });
1787
+ expect(prompts).toHaveLength(1);
1788
+
1789
+ const actionIds = prompts[0].actions.map((a) => a.action);
1790
+ expect(actionIds).toEqual(["approve_once", "reject"]);
1791
+ expect(actionIds).not.toContain("approve_10m");
1792
+ expect(actionIds).not.toContain("approve_conversation");
1793
+ });
1794
+
1795
+ test("access_request prompt has approve_once + reject only (no temporal actions)", () => {
1796
+ const convId = "conv-kind-access-request";
1797
+ createCanonicalGuardianRequest({
1798
+ kind: "access_request",
1799
+ sourceType: "channel",
1800
+ sourceChannel: "telegram",
1801
+ conversationId: convId,
1802
+ guardianExternalUserId: "guardian-1",
1803
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
1804
+ toolName: "ingress_access_request",
1805
+ expiresAt: Date.now() + 60_000,
1806
+ });
1807
+
1808
+ const prompts = listGuardianDecisionPrompts({ conversationId: convId });
1809
+ expect(prompts).toHaveLength(1);
1810
+
1811
+ const actionIds = prompts[0].actions.map((a) => a.action);
1812
+ expect(actionIds).toEqual(["approve_once", "reject"]);
1813
+ expect(actionIds).not.toContain("approve_10m");
1814
+ expect(actionIds).not.toContain("approve_conversation");
1815
+ });
1816
+
1817
+ test("tool_grant_request prompt has approve_once + reject only (no temporal actions)", () => {
1818
+ const convId = "conv-kind-tool-grant-request";
1819
+ createCanonicalGuardianRequest({
1820
+ kind: "tool_grant_request",
1821
+ sourceType: "channel",
1822
+ conversationId: convId,
1823
+ guardianExternalUserId: "guardian-1",
1824
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
1825
+ toolName: "file_write",
1826
+ expiresAt: Date.now() + 60_000,
1827
+ });
1828
+
1829
+ const prompts = listGuardianDecisionPrompts({ conversationId: convId });
1830
+ expect(prompts).toHaveLength(1);
1831
+
1832
+ const actionIds = prompts[0].actions.map((a) => a.action);
1833
+ expect(actionIds).toEqual(["approve_once", "reject"]);
1834
+ expect(actionIds).not.toContain("approve_10m");
1835
+ expect(actionIds).not.toContain("approve_conversation");
1836
+ });
1837
+ });
@@ -1,4 +1,4 @@
1
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  mock.module("../config/env.js", () => ({ isHttpAuthDisabled: () => true }));
4
4
  import { eq } from "drizzle-orm";
@@ -17,7 +17,7 @@ mock.module("../util/logger.js", () => ({
17
17
  import { upsertContact } from "../contacts/contact-store.js";
18
18
  import { createGuardianBinding } from "../contacts/contacts-write.js";
19
19
  import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
20
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
20
+ import { getDb, initializeDb } from "../memory/db.js";
21
21
  import * as deliveryCrud from "../memory/delivery-crud.js";
22
22
  import { channelInboundEvents, messages } from "../memory/schema.js";
23
23
  import { sweepFailedEvents } from "../runtime/channel-retry-sweep.js";
@@ -29,10 +29,6 @@ import {
29
29
 
30
30
  initializeDb();
31
31
 
32
- afterAll(() => {
33
- resetDb();
34
- });
35
-
36
32
  function resetTables(): void {
37
33
  const db = getDb();
38
34
  db.run("DELETE FROM channel_inbound_events");
@@ -2,7 +2,7 @@
2
2
  * Regression test: guardian verification calls must create a voice channel
3
3
  * binding so the conversation never appears as an unbound desktop conversation.
4
4
  */
5
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
5
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
6
6
 
7
7
  mock.module("../util/logger.js", () => ({
8
8
  getLogger: () =>
@@ -90,15 +90,11 @@ mock.module("../memory/conversation-title-service.js", () => ({
90
90
 
91
91
  import { startVerificationCall } from "../calls/call-domain.js";
92
92
  import { getOrCreateConversation } from "../memory/conversation-key-store.js";
93
- import { initializeDb, resetDb } from "../memory/db.js";
93
+ import { initializeDb } from "../memory/db.js";
94
94
  import { getBindingByConversation } from "../memory/external-conversation-store.js";
95
95
 
96
96
  initializeDb();
97
97
 
98
- afterAll(() => {
99
- resetDb();
100
- });
101
-
102
98
  describe("startVerificationCall — voice binding", () => {
103
99
  beforeEach(() => {
104
100
  mockPreflightResult = {
@@ -5,7 +5,7 @@
5
5
  * granted access without guardian approval, and that invalid/expired/revoked
6
6
  * tokens produce the correct deterministic refusal messages.
7
7
  */
8
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
8
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
9
9
 
10
10
  // ---------------------------------------------------------------------------
11
11
  // Test isolation: in-memory SQLite via temp directory
@@ -69,16 +69,12 @@ import {
69
69
  upsertContact,
70
70
  } from "../contacts/contact-store.js";
71
71
  import { upsertContactChannel } from "../contacts/contacts-write.js";
72
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
72
+ import { getDb, initializeDb } from "../memory/db.js";
73
73
  import { createInvite, revokeInvite } from "../memory/invite-store.js";
74
74
  import { handleChannelInbound } from "../runtime/routes/channel-routes.js";
75
75
 
76
76
  initializeDb();
77
77
 
78
- afterAll(() => {
79
- resetDb();
80
- });
81
-
82
78
  // ---------------------------------------------------------------------------
83
79
  // Helpers
84
80
  // ---------------------------------------------------------------------------
@@ -0,0 +1,154 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ assembleContextBlock,
5
+ assembleInjectionBlock,
6
+ } from "../memory/graph/injection.js";
7
+ import type { MemoryNode, ScoredNode } from "../memory/graph/types.js";
8
+
9
+ function makeScoredNode(
10
+ overrides: Partial<MemoryNode> & { content: string },
11
+ ): ScoredNode {
12
+ return {
13
+ node: {
14
+ id: "test-id",
15
+ content: overrides.content,
16
+ type: overrides.type ?? "episodic",
17
+ created: overrides.created ?? Date.now() - 3 * 24 * 60 * 60 * 1000,
18
+ lastAccessed: overrides.lastAccessed ?? Date.now(),
19
+ lastConsolidated: overrides.lastConsolidated ?? Date.now(),
20
+ eventDate: overrides.eventDate ?? null,
21
+ emotionalCharge: overrides.emotionalCharge ?? {
22
+ valence: 0,
23
+ intensity: 0,
24
+ decayCurve: "linear",
25
+ decayRate: 0,
26
+ originalIntensity: 0,
27
+ },
28
+ fidelity: overrides.fidelity ?? "clear",
29
+ confidence: overrides.confidence ?? 1,
30
+ significance: overrides.significance ?? 0.5,
31
+ stability: overrides.stability ?? 1,
32
+ reinforcementCount: overrides.reinforcementCount ?? 0,
33
+ lastReinforced: overrides.lastReinforced ?? Date.now(),
34
+ sourceConversations: overrides.sourceConversations ?? [],
35
+ sourceType: overrides.sourceType ?? "direct",
36
+ narrativeRole: overrides.narrativeRole ?? null,
37
+ partOfStory: overrides.partOfStory ?? null,
38
+ imageRefs: overrides.imageRefs ?? null,
39
+ scopeId: overrides.scopeId ?? "default",
40
+ },
41
+ score: 0.8,
42
+ scoreBreakdown: {
43
+ semanticSimilarity: 0.5,
44
+ effectiveSignificance: 0.5,
45
+ emotionalIntensity: 0,
46
+ temporalBoost: 0,
47
+ recencyBoost: 0,
48
+ triggerBoost: 0,
49
+ activationBoost: 0,
50
+ },
51
+ };
52
+ }
53
+
54
+ describe("assembleInjectionBlock", () => {
55
+ test("returns empty string for empty array", () => {
56
+ expect(assembleInjectionBlock([])).toBe("");
57
+ });
58
+
59
+ test("uses creation age format for nodes without eventDate", () => {
60
+ const node = makeScoredNode({ content: "Regular memory node" });
61
+ const result = assembleInjectionBlock([node]);
62
+ // Should show relative age like "(3d ago)"
63
+ expect(result).toMatch(/^\- \(\d+[dhm]o? ago\) Regular memory node$/);
64
+ });
65
+
66
+ test("uses event date format for nodes with eventDate", () => {
67
+ // Set eventDate to 5 days from now
68
+ const futureDate = Date.now() + 5 * 24 * 60 * 60 * 1000;
69
+ const node = makeScoredNode({
70
+ content: "Flight to NYC",
71
+ eventDate: futureDate,
72
+ });
73
+ const result = assembleInjectionBlock([node]);
74
+ // Should show event date like "Tue Apr 8, 6:00 PM (in 5d) — Flight to NYC"
75
+ expect(result).toContain("Flight to NYC");
76
+ expect(result).toMatch(/\(in \d+d\)/);
77
+ // Should NOT contain the age-based format
78
+ expect(result).not.toMatch(/\(\d+[dhm]o? ago\)/);
79
+ });
80
+
81
+ test("mixes both formats when nodes have mixed eventDate presence", () => {
82
+ const regularNode = makeScoredNode({
83
+ id: "regular",
84
+ content: "Had coffee with friend",
85
+ } as Partial<MemoryNode> & { content: string });
86
+ const eventNode = makeScoredNode({
87
+ id: "event",
88
+ content: "Dentist appointment",
89
+ eventDate: Date.now() + 2 * 24 * 60 * 60 * 1000,
90
+ } as Partial<MemoryNode> & { content: string });
91
+
92
+ const result = assembleInjectionBlock([regularNode, eventNode]);
93
+ const lines = result.split("\n");
94
+
95
+ expect(lines).toHaveLength(2);
96
+ // First line: regular node with age format
97
+ expect(lines[0]).toMatch(/\(\d+[dhm]o? ago\) Had coffee with friend/);
98
+ // Second line: event node with event date format
99
+ expect(lines[1]).toContain("Dentist appointment");
100
+ expect(lines[1]).toMatch(/\(in \d+d\)/);
101
+ });
102
+
103
+ test("uses event date format for past event-dated nodes", () => {
104
+ // Set eventDate to 3 days ago (past event)
105
+ const pastDate = Date.now() - 3 * 24 * 60 * 60 * 1000;
106
+ const node = makeScoredNode({
107
+ content: "Team standup",
108
+ eventDate: pastDate,
109
+ });
110
+ const result = assembleInjectionBlock([node]);
111
+ // Should show event date format with past indicator, not creation age
112
+ expect(result).toContain("Team standup");
113
+ expect(result).toMatch(/\(\d+d ago\)/);
114
+ // The line should use the event date format (date — content) not age format ((age) content)
115
+ expect(result).toContain(" — Team standup");
116
+ });
117
+
118
+ test("assembleInjectionBlock formats procedural nodes with [skill] prefix", () => {
119
+ const node = makeScoredNode({
120
+ type: "procedural",
121
+ content:
122
+ 'skill:telegram-setup\nThe "Telegram Setup" skill (telegram-setup) is available.',
123
+ });
124
+ const result = assembleInjectionBlock([node]);
125
+ expect(result).toContain("[skill]");
126
+ expect(result).toContain("→ use skill_load to activate");
127
+ });
128
+ });
129
+
130
+ describe("assembleContextBlock — procedural nodes", () => {
131
+ test("puts procedural nodes under Skills You Can Use", () => {
132
+ const node = makeScoredNode({
133
+ type: "procedural",
134
+ content:
135
+ 'skill:telegram-setup\nThe "Telegram Setup" skill (telegram-setup) is available.',
136
+ });
137
+ const result = assembleContextBlock([node]);
138
+ expect(result).toContain("### Skills You Can Use");
139
+ expect(result).toContain("use skill_load to activate");
140
+ });
141
+
142
+ test("strips skill: prefix from old-format content", () => {
143
+ const node = makeScoredNode({
144
+ type: "procedural",
145
+ content:
146
+ 'skill:telegram-setup\nThe "Telegram Setup" skill (telegram-setup) is available.',
147
+ });
148
+ const result = assembleContextBlock([node]);
149
+ // The "skill:telegram-setup" prefix line should be stripped from the rendered output
150
+ expect(result).not.toContain("skill:telegram-setup\n");
151
+ // But the skill name in the description should remain
152
+ expect(result).toContain('The "Telegram Setup" skill');
153
+ });
154
+ });