@vellumai/assistant 0.6.5 → 0.6.6

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 (443) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +15 -17
  3. package/Dockerfile +6 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  5. package/docs/architecture/integrations.md +32 -39
  6. package/docs/architecture/memory.md +25 -30
  7. package/docs/architecture/security.md +7 -6
  8. package/docs/browser-use-architecture-phase2.md +63 -20
  9. package/docs/plugins.md +761 -0
  10. package/examples/plugins/echo/README.md +132 -0
  11. package/examples/plugins/echo/package.json +17 -0
  12. package/examples/plugins/echo/register.ts +187 -0
  13. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  14. package/openapi.yaml +212 -68
  15. package/package.json +1 -1
  16. package/src/__tests__/app-compiler.test.ts +57 -0
  17. package/src/__tests__/approval-cascade.test.ts +7 -2
  18. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  19. package/src/__tests__/avatar-generator.test.ts +4 -2
  20. package/src/__tests__/bundled-asset.test.ts +6 -6
  21. package/src/__tests__/catalog-cache.test.ts +69 -0
  22. package/src/__tests__/checker.test.ts +459 -171
  23. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  24. package/src/__tests__/compaction-events.test.ts +501 -0
  25. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  26. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  27. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  28. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  29. package/src/__tests__/config-schema.test.ts +22 -9
  30. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  31. package/src/__tests__/contacts-tools.test.ts +26 -0
  32. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  33. package/src/__tests__/context-window-manager.test.ts +355 -4
  34. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  35. package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
  36. package/src/__tests__/conversation-agent-loop.test.ts +30 -141
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
  38. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
  40. package/src/__tests__/conversation-pairing.test.ts +174 -10
  41. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
  42. package/src/__tests__/conversation-process-callsite.test.ts +3 -0
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
  44. package/src/__tests__/conversation-queue.test.ts +29 -14
  45. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  48. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  49. package/src/__tests__/conversation-slash-queue.test.ts +7 -2
  50. package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
  51. package/src/__tests__/conversation-speed-override.test.ts +6 -1
  52. package/src/__tests__/conversation-title-service.test.ts +116 -0
  53. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  54. package/src/__tests__/conversation-usage.test.ts +1 -1
  55. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  56. package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
  57. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
  58. package/src/__tests__/credential-health-service.test.ts +78 -9
  59. package/src/__tests__/credential-security-invariants.test.ts +2 -2
  60. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  61. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  62. package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
  63. package/src/__tests__/first-greeting.test.ts +247 -5
  64. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  65. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  66. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  67. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  68. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  69. package/src/__tests__/image-credentials.test.ts +137 -0
  70. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  71. package/src/__tests__/injector-chain.test.ts +526 -0
  72. package/src/__tests__/intent-routing.test.ts +0 -26
  73. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  74. package/src/__tests__/llm-schema.test.ts +1 -1
  75. package/src/__tests__/media-generate-image.test.ts +119 -13
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  77. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  78. package/src/__tests__/migration-import-from-url.test.ts +5 -68
  79. package/src/__tests__/model-intents.test.ts +4 -2
  80. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  81. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  82. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  83. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  84. package/src/__tests__/oauth-cli.test.ts +14 -12
  85. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  86. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  87. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  88. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  89. package/src/__tests__/oauth-store.test.ts +41 -76
  90. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  91. package/src/__tests__/openai-image-service.test.ts +368 -0
  92. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  93. package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
  94. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  95. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  96. package/src/__tests__/pipeline-runner.test.ts +565 -0
  97. package/src/__tests__/platform.test.ts +5 -2
  98. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  99. package/src/__tests__/plugin-registry.test.ts +273 -0
  100. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  101. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  102. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  103. package/src/__tests__/plugin-types.test.ts +320 -0
  104. package/src/__tests__/pricing.test.ts +44 -12
  105. package/src/__tests__/proxy-approval-callback.test.ts +69 -8
  106. package/src/__tests__/reaction-persistence.test.ts +1 -0
  107. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  108. package/src/__tests__/registry.test.ts +0 -2
  109. package/src/__tests__/schedule-routes.test.ts +131 -1
  110. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  111. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  112. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  113. package/src/__tests__/shell-identity.test.ts +0 -134
  114. package/src/__tests__/suggestion-routes.test.ts +103 -4
  115. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  116. package/src/__tests__/task-scheduler.test.ts +3 -15
  117. package/src/__tests__/test-preload.ts +11 -0
  118. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  119. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  120. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  121. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  122. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
  123. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  124. package/src/__tests__/tool-executor.test.ts +141 -0
  125. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  126. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  127. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  128. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  129. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  130. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  131. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  132. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  133. package/src/__tests__/workspace-policy.test.ts +21 -3
  134. package/src/agent/loop.ts +340 -102
  135. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  136. package/src/approvals/guardian-request-resolvers.ts +80 -0
  137. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  138. package/src/backup/backup-worker.ts +3 -15
  139. package/src/bundler/app-compiler.ts +84 -1
  140. package/src/calls/call-state.ts +2 -2
  141. package/src/channels/__tests__/types.test.ts +3 -3
  142. package/src/channels/types.ts +6 -4
  143. package/src/cli/__tests__/notifications.test.ts +87 -211
  144. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  145. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  146. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  147. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  148. package/src/cli/commands/backup.ts +2 -2
  149. package/src/cli/commands/clients.ts +138 -0
  150. package/src/cli/commands/completions.ts +2 -9
  151. package/src/cli/commands/conversations.ts +55 -7
  152. package/src/cli/commands/image-generation.ts +33 -34
  153. package/src/cli/commands/notifications.ts +68 -103
  154. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  155. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  156. package/src/cli/commands/oauth/connect.ts +2 -2
  157. package/src/cli/commands/oauth/providers.ts +176 -8
  158. package/src/cli/commands/oauth/status.ts +46 -36
  159. package/src/cli/commands/skills.ts +3 -4
  160. package/src/cli/program.ts +25 -29
  161. package/src/config/__tests__/backup-schema.test.ts +7 -2
  162. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  163. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  164. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  165. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  166. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  170. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  171. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  172. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  174. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  175. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  176. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  177. package/src/config/bundled-tool-registry.ts +0 -15
  178. package/src/config/feature-flag-registry.json +17 -1
  179. package/src/config/schema.ts +19 -0
  180. package/src/config/schemas/backup.ts +1 -1
  181. package/src/config/schemas/conversations.ts +16 -0
  182. package/src/config/schemas/llm.ts +2 -3
  183. package/src/config/schemas/security.ts +6 -6
  184. package/src/config/schemas/tts.ts +11 -0
  185. package/src/config/skill-state.ts +6 -2
  186. package/src/config/skills.ts +94 -5
  187. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  188. package/src/context/prompts/compact.md +26 -12
  189. package/src/context/tool-result-truncation.ts +3 -63
  190. package/src/context/window-manager.ts +190 -16
  191. package/src/credential-health/credential-health-service.ts +19 -6
  192. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  193. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  194. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  195. package/src/daemon/config-watcher.ts +0 -2
  196. package/src/daemon/context-overflow-policy.ts +4 -13
  197. package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
  198. package/src/daemon/conversation-agent-loop.ts +984 -683
  199. package/src/daemon/conversation-history.ts +10 -19
  200. package/src/daemon/conversation-lifecycle.ts +37 -19
  201. package/src/daemon/conversation-notifiers.ts +2 -110
  202. package/src/daemon/conversation-process.ts +14 -7
  203. package/src/daemon/conversation-runtime-assembly.ts +532 -411
  204. package/src/daemon/conversation-tool-setup.ts +41 -4
  205. package/src/daemon/conversation.ts +80 -35
  206. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  207. package/src/daemon/first-greeting.ts +191 -14
  208. package/src/daemon/handlers/config-model.ts +11 -0
  209. package/src/daemon/handlers/skills.ts +5 -1
  210. package/src/daemon/lifecycle.ts +33 -68
  211. package/src/daemon/message-types/computer-use.ts +2 -34
  212. package/src/daemon/message-types/conversations.ts +49 -0
  213. package/src/daemon/message-types/messages.ts +12 -0
  214. package/src/daemon/server.ts +5 -3
  215. package/src/daemon/shutdown-handlers.ts +2 -12
  216. package/src/daemon/tool-side-effects.ts +14 -56
  217. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  218. package/src/heartbeat/heartbeat-service.ts +24 -1
  219. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  220. package/src/home/emit-feed-event.ts +7 -0
  221. package/src/home/feed-types.ts +41 -2
  222. package/src/home/rewrite-command-preview.ts +66 -0
  223. package/src/ipc/__tests__/socket-path.test.ts +11 -50
  224. package/src/ipc/cli-client.ts +1 -1
  225. package/src/ipc/cli-server.ts +3 -3
  226. package/src/ipc/gateway-client.ts +4 -1
  227. package/src/ipc/routes/browser-context.ts +2 -0
  228. package/src/ipc/routes/browser.ts +1 -0
  229. package/src/ipc/routes/get-contact.ts +16 -0
  230. package/src/ipc/routes/index.ts +14 -0
  231. package/src/ipc/routes/list-clients.ts +31 -0
  232. package/src/ipc/routes/merge-contacts.ts +17 -0
  233. package/src/ipc/routes/notification.ts +133 -0
  234. package/src/ipc/routes/rename-conversation.ts +59 -0
  235. package/src/ipc/routes/search-contacts.ts +19 -0
  236. package/src/ipc/routes/upsert-contact.ts +25 -0
  237. package/src/ipc/socket-path.ts +14 -38
  238. package/src/media/app-icon-generator.ts +23 -46
  239. package/src/media/avatar-router.ts +26 -41
  240. package/src/media/gemini-image-service.ts +8 -41
  241. package/src/media/image-credentials.ts +73 -0
  242. package/src/media/image-service.ts +85 -0
  243. package/src/media/openai-image-service.ts +131 -0
  244. package/src/media/types.ts +46 -0
  245. package/src/memory/conversation-crud.ts +48 -18
  246. package/src/memory/conversation-queries.ts +57 -4
  247. package/src/memory/conversation-title-service.ts +25 -0
  248. package/src/memory/db-init.ts +8 -0
  249. package/src/memory/embedding-gemini.test.ts +41 -2
  250. package/src/memory/embedding-gemini.ts +6 -1
  251. package/src/memory/graph/bootstrap.test.ts +282 -0
  252. package/src/memory/graph/bootstrap.ts +8 -5
  253. package/src/memory/graph/extraction.ts +10 -2
  254. package/src/memory/graph/graph-search.test.ts +1 -0
  255. package/src/memory/graph/inspect.ts +2 -2
  256. package/src/memory/graph/retriever.ts +10 -3
  257. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  258. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  259. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  260. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  261. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  262. package/src/memory/migrations/index.ts +4 -0
  263. package/src/memory/pkb/pkb-index.test.ts +1 -0
  264. package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
  265. package/src/memory/pkb/pkb-search.test.ts +65 -4
  266. package/src/memory/pkb/pkb-search.ts +40 -18
  267. package/src/memory/qdrant-client.test.ts +60 -0
  268. package/src/memory/qdrant-client.ts +25 -0
  269. package/src/memory/schema/infrastructure.ts +1 -0
  270. package/src/memory/schema/oauth.ts +4 -1
  271. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  272. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  273. package/src/notifications/conversation-pairing.ts +78 -19
  274. package/src/notifications/copy-composer.ts +0 -5
  275. package/src/notifications/emit-signal.ts +1 -1
  276. package/src/notifications/signal.ts +1 -2
  277. package/src/oauth/AGENTS.md +1 -1
  278. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  279. package/src/oauth/connect-orchestrator.ts +8 -34
  280. package/src/oauth/connect-types.ts +6 -10
  281. package/src/oauth/manual-token-connection.ts +23 -0
  282. package/src/oauth/oauth-store.ts +30 -14
  283. package/src/oauth/provider-serializer.ts +6 -1
  284. package/src/oauth/seed-providers.ts +56 -108
  285. package/src/outbound-proxy/http-forwarder.ts +9 -0
  286. package/src/permissions/approval-policy.test.ts +293 -18
  287. package/src/permissions/approval-policy.ts +110 -58
  288. package/src/permissions/arg-parser.test.ts +161 -0
  289. package/src/permissions/arg-parser.ts +141 -0
  290. package/src/permissions/bash-risk-classifier.test.ts +414 -2
  291. package/src/permissions/bash-risk-classifier.ts +303 -60
  292. package/src/permissions/checker.ts +157 -29
  293. package/src/permissions/command-registry.test.ts +239 -0
  294. package/src/permissions/command-registry.ts +234 -54
  295. package/src/permissions/defaults.ts +5 -4
  296. package/src/permissions/gateway-threshold-reader.ts +196 -0
  297. package/src/permissions/prompter.ts +4 -0
  298. package/src/permissions/risk-types.ts +61 -4
  299. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  300. package/src/permissions/schedule-risk-classifier.ts +85 -0
  301. package/src/permissions/shell-identity.ts +2 -42
  302. package/src/permissions/types.ts +2 -0
  303. package/src/permissions/workspace-policy.ts +8 -3
  304. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  305. package/src/plugins/defaults/compaction.ts +145 -0
  306. package/src/plugins/defaults/empty-response.ts +126 -0
  307. package/src/plugins/defaults/history-repair.ts +85 -0
  308. package/src/plugins/defaults/index.ts +116 -0
  309. package/src/plugins/defaults/injectors.ts +491 -0
  310. package/src/plugins/defaults/llm-call.ts +82 -0
  311. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  312. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  313. package/src/plugins/defaults/persistence.ts +129 -0
  314. package/src/plugins/defaults/title-generate.ts +95 -0
  315. package/src/plugins/defaults/token-estimate.ts +104 -0
  316. package/src/plugins/defaults/tool-error.ts +126 -0
  317. package/src/plugins/defaults/tool-execute.ts +89 -0
  318. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  319. package/src/plugins/pipeline.ts +316 -0
  320. package/src/plugins/plugin-skill-contributions.ts +292 -0
  321. package/src/plugins/registry.ts +241 -0
  322. package/src/plugins/types.ts +1134 -0
  323. package/src/plugins/user-loader.ts +177 -0
  324. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  325. package/src/providers/model-catalog.ts +52 -29
  326. package/src/providers/model-intents.ts +1 -1
  327. package/src/providers/openrouter/client.ts +5 -1
  328. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  329. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  330. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  331. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  332. package/src/runtime/AGENTS.md +25 -16
  333. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  334. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  335. package/src/runtime/client-registry.ts +261 -0
  336. package/src/runtime/http-server.ts +77 -8
  337. package/src/runtime/http-types.ts +0 -2
  338. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  339. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  340. package/src/runtime/routes/approval-routes.ts +17 -0
  341. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  342. package/src/runtime/routes/conversation-routes.ts +223 -116
  343. package/src/runtime/routes/inbound-message-handler.ts +88 -13
  344. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  345. package/src/runtime/routes/migration-routes.ts +0 -3
  346. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  347. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  348. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  349. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  350. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  351. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  352. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  353. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  354. package/src/runtime/routes/playground/deps.ts +56 -0
  355. package/src/runtime/routes/playground/force-compact.ts +73 -0
  356. package/src/runtime/routes/playground/guard.ts +37 -0
  357. package/src/runtime/routes/playground/index.ts +28 -0
  358. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  359. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  360. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  361. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  362. package/src/runtime/routes/playground/state.ts +78 -0
  363. package/src/runtime/routes/schedule-routes.ts +89 -8
  364. package/src/runtime/skill-route-registry.ts +75 -15
  365. package/src/schedule/run-script.ts +68 -0
  366. package/src/schedule/schedule-store.ts +7 -1
  367. package/src/schedule/scheduler.ts +48 -8
  368. package/src/skills/catalog-cache.ts +12 -5
  369. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  370. package/src/tools/browser/browser-execution.ts +88 -19
  371. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  372. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  373. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  374. package/src/tools/browser/cdp-client/factory.ts +15 -4
  375. package/src/tools/executor.ts +126 -74
  376. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  377. package/src/tools/permission-checker.ts +98 -49
  378. package/src/tools/policy-context.ts +4 -0
  379. package/src/tools/registry.ts +140 -3
  380. package/src/tools/schedule/create.ts +23 -8
  381. package/src/tools/schedule/update.ts +3 -1
  382. package/src/tools/secret-detection-handler.ts +0 -51
  383. package/src/tools/system/avatar-generator.ts +6 -2
  384. package/src/tools/types.ts +28 -2
  385. package/src/util/platform.ts +7 -2
  386. package/src/util/pricing.ts +26 -3
  387. package/src/workspace/migrations/006-services-config.ts +2 -4
  388. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  389. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  390. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  391. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  392. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  393. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  394. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  395. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  396. package/src/workspace/migrations/registry.ts +12 -0
  397. package/tsconfig.json +1 -1
  398. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  399. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  400. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  401. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  402. package/src/__tests__/hooks-blocking.test.ts +0 -178
  403. package/src/__tests__/hooks-cli.test.ts +0 -182
  404. package/src/__tests__/hooks-config.test.ts +0 -108
  405. package/src/__tests__/hooks-discovery.test.ts +0 -211
  406. package/src/__tests__/hooks-integration.test.ts +0 -196
  407. package/src/__tests__/hooks-manager.test.ts +0 -226
  408. package/src/__tests__/hooks-runner.test.ts +0 -175
  409. package/src/__tests__/hooks-settings.test.ts +0 -160
  410. package/src/__tests__/hooks-templates.test.ts +0 -169
  411. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  412. package/src/__tests__/hooks-watch.test.ts +0 -112
  413. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  414. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  415. package/src/__tests__/send-notification-tool.test.ts +0 -83
  416. package/src/cli/commands/shotgun.ts +0 -266
  417. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  418. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  419. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  420. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  421. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  422. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  423. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  424. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  425. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  426. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  427. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  428. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  429. package/src/daemon/context-overflow-approval.ts +0 -52
  430. package/src/daemon/watch-handler.ts +0 -399
  431. package/src/hooks/cli.ts +0 -253
  432. package/src/hooks/config.ts +0 -100
  433. package/src/hooks/discovery.ts +0 -135
  434. package/src/hooks/manager.ts +0 -179
  435. package/src/hooks/runner.ts +0 -117
  436. package/src/hooks/templates.ts +0 -77
  437. package/src/hooks/types.ts +0 -75
  438. package/src/oauth/scope-policy.ts +0 -89
  439. package/src/runtime/gateway-internal-client.ts +0 -94
  440. package/src/runtime/routes/watch-routes.ts +0 -156
  441. package/src/signals/shotgun.ts +0 -203
  442. package/src/tools/watch/screen-watch.ts +0 -144
  443. package/src/tools/watch/watch-state.ts +0 -142
@@ -0,0 +1,491 @@
1
+ /**
2
+ * Default runtime injector plugin — the canonical chain of injectors that
3
+ * drives the per-turn injection sequence consumed by
4
+ * `applyRuntimeInjections`.
5
+ *
6
+ * Each of the eight default injectors reads its per-turn inputs from
7
+ * `ctx.injectionInputs` (see {@link TurnInjectionInputs}), runs its gating
8
+ * conditions (injection mode, feature flags, channel type, null-input
9
+ * short-circuits), and returns an {@link InjectionBlock} with a
10
+ * {@link InjectionPlacement} that yields the canonical positional
11
+ * semantics expected by the assembly pipeline:
12
+ *
13
+ * | name | order | placement |
14
+ * | ------------------------ | ----- | ----------------------- |
15
+ * | `workspace-context` | 10 | prepend-user-tail |
16
+ * | `unified-turn-context` | 20 | prepend-user-tail |
17
+ * | `pkb-context` | 30 | after-memory-prefix |
18
+ * | `pkb-reminder` | 35 | after-memory-prefix |
19
+ * | `now-md` | 40 | after-memory-prefix |
20
+ * | `subagent-status` | 50 | append-user-tail |
21
+ * | `slack-messages` | 60 | replace-run-messages |
22
+ * | `thread-focus` | 70 | append-user-tail |
23
+ *
24
+ * `order` matches the intended final-content ordering: lower `order` ends
25
+ * up closer to the top of the user message's content (for prepends), and
26
+ * within `after-memory-prefix` each successive splice lands at the memory
27
+ * boundary — so higher-`order` blocks push earlier splices away and end up
28
+ * closer to the memory prefix themselves. For appends, ascending `order` is
29
+ * the natural left-to-right append sequence. The runtime-injection applier
30
+ * sorts and applies blocks declaratively so this invariant holds even when
31
+ * third-party injectors slot additional blocks at fractional order values.
32
+ *
33
+ * Third-party plugins may register additional {@link Injector}s at any
34
+ * `order` value; the registry's `getInjectors()` returns all injectors
35
+ * sorted ascending, so a plugin-registered injector at `order: 25`
36
+ * reliably slots between `unified-turn-context` (20) and `pkb` (30).
37
+ *
38
+ * Registration happens via a module-load side effect at the bottom of this
39
+ * file — importing the module is enough to populate the registry. The
40
+ * explicit `registerDefaultPlugins()` call in `plugins/defaults/index.ts`
41
+ * (invoked from `daemon/external-plugins-bootstrap.ts`) re-registers the
42
+ * same plugin idempotently, so either entry point alone is sufficient.
43
+ */
44
+
45
+ import { resolve } from "node:path";
46
+
47
+ import { getInContextPkbPaths } from "../../daemon/pkb-context-tracker.js";
48
+ import { buildPkbReminder } from "../../daemon/pkb-reminder-builder.js";
49
+ import { searchPkbFiles } from "../../memory/pkb/pkb-search.js";
50
+ import { getLogger } from "../../util/logger.js";
51
+ import { registerPlugin } from "../registry.js";
52
+ import {
53
+ type InjectionBlock,
54
+ type Injector,
55
+ type Plugin,
56
+ PluginExecutionError,
57
+ type TurnContext,
58
+ type TurnInjectionInputs,
59
+ } from "../types.js";
60
+
61
+ const pkbReminderLog = getLogger("pkb-reminder");
62
+
63
+ /** Minimum hybrid-search score for a PKB path to surface as an injection hint. */
64
+ const PKB_HINT_THRESHOLD = 0.5;
65
+
66
+ /**
67
+ * Stricter hint threshold for PKB entries under `archive/`. Archive files are
68
+ * date-indexed dumps of older notes — they match loosely and are rarely the
69
+ * most relevant read, so require a higher bar before recommending them.
70
+ */
71
+ const PKB_HINT_ARCHIVE_THRESHOLD = 0.7;
72
+
73
+ /**
74
+ * Fixed order values for the seven default injectors. Exported so tests —
75
+ * and any future integration code — can assert ordering without re-deriving
76
+ * the constants.
77
+ *
78
+ * Gaps of 10 between slots leave room for third-party injectors to slot in
79
+ * at granular positions (e.g. `25` between unified-turn-context and pkb)
80
+ * without renumbering the defaults.
81
+ */
82
+ export const DEFAULT_INJECTOR_ORDER = {
83
+ workspaceContext: 10,
84
+ unifiedTurnContext: 20,
85
+ pkbContext: 30,
86
+ pkbReminder: 35,
87
+ nowMd: 40,
88
+ subagentStatus: 50,
89
+ slackMessages: 60,
90
+ threadFocus: 70,
91
+ } as const satisfies Record<string, number>;
92
+
93
+ function readInjectionInputs(ctx: TurnContext): TurnInjectionInputs {
94
+ return ctx.injectionInputs ?? {};
95
+ }
96
+
97
+ /**
98
+ * `workspace-context` injector — order 10, prepend-user-tail.
99
+ *
100
+ * Injects the workspace top-level directory context at the very top of the
101
+ * user tail's content so the assistant sees a workspace grounding block
102
+ * before any other per-turn context.
103
+ *
104
+ * Gating:
105
+ * - `mode === "full"` (skipped in minimal mode).
106
+ * - `workspaceTopLevelContext` is a non-null, non-empty string.
107
+ */
108
+ export const workspaceContextInjector: Injector = {
109
+ name: "workspace-context",
110
+ order: DEFAULT_INJECTOR_ORDER.workspaceContext,
111
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
112
+ const inputs = readInjectionInputs(ctx);
113
+ const mode = inputs.mode ?? "full";
114
+ if (mode !== "full") return null;
115
+ const text = inputs.workspaceTopLevelContext;
116
+ if (!text) return null;
117
+ return {
118
+ id: "workspace-context",
119
+ text,
120
+ placement: "prepend-user-tail",
121
+ };
122
+ },
123
+ };
124
+
125
+ /**
126
+ * `unified-turn-context` injector — order 20, prepend-user-tail.
127
+ *
128
+ * Injects the pre-built `<turn_context>` block that combines temporal,
129
+ * actor, channel, and interface context. The orchestrator builds the text
130
+ * via `buildUnifiedTurnContextBlock` before the chain runs and hands it in
131
+ * via `ctx.injectionInputs.unifiedTurnContext`.
132
+ *
133
+ * Active in both `full` and `minimal` mode — unified turn context is
134
+ * safety-critical grounding that must survive injection downgrade.
135
+ */
136
+ export const unifiedTurnContextInjector: Injector = {
137
+ name: "unified-turn-context",
138
+ order: DEFAULT_INJECTOR_ORDER.unifiedTurnContext,
139
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
140
+ const inputs = readInjectionInputs(ctx);
141
+ const text = inputs.unifiedTurnContext;
142
+ if (!text) return null;
143
+ return {
144
+ id: "unified-turn-context",
145
+ text,
146
+ placement: "prepend-user-tail",
147
+ };
148
+ },
149
+ };
150
+
151
+ /**
152
+ * `pkb-context` injector — order 30, after-memory-prefix.
153
+ *
154
+ * Emits the `<knowledge_base>` block (auto-injected PKB content) as its own
155
+ * after-memory-prefix splice. Lower `order` than `pkb-reminder` so when both
156
+ * fire, the reminder splices second and lands closer to the memory prefix,
157
+ * yielding `[...memory, <system_reminder>, <knowledge_base>, ...user text]`.
158
+ *
159
+ * Emitting context and reminder as two separate blocks (rather than a single
160
+ * concatenated text) preserves the pre-migration two-ContentBlock shape that
161
+ * the rehydration path in `conversation-lifecycle.ts` recreates — keeping
162
+ * fresh-injection and rehydrated-history structurally identical so
163
+ * Anthropic's prefix cache matches across reloads.
164
+ *
165
+ * Gating:
166
+ * - `mode === "full"`.
167
+ * - Non-null, non-empty `pkbContext`.
168
+ */
169
+ export const pkbContextInjector: Injector = {
170
+ name: "pkb-context",
171
+ order: DEFAULT_INJECTOR_ORDER.pkbContext,
172
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
173
+ const inputs = readInjectionInputs(ctx);
174
+ const mode = inputs.mode ?? "full";
175
+ if (mode !== "full") return null;
176
+ if (!inputs.pkbContext) return null;
177
+ return {
178
+ id: "pkb-context",
179
+ text: buildPkbContextBlock(inputs.pkbContext),
180
+ placement: "after-memory-prefix",
181
+ };
182
+ },
183
+ };
184
+
185
+ /**
186
+ * `pkb-reminder` injector — order 35, after-memory-prefix.
187
+ *
188
+ * Emits the PKB `<system_reminder>` (behavioural nudge + hybrid-search
189
+ * hints) as its own after-memory-prefix splice. Higher `order` than
190
+ * `pkb-context` so the reminder splices second and ends up immediately
191
+ * after the memory prefix, pushing `<knowledge_base>` one slot further
192
+ * down — matching the pre-migration [reminder, context] ordering.
193
+ *
194
+ * Gating:
195
+ * - `mode === "full"`.
196
+ * - `pkbActive === true`.
197
+ */
198
+ export const pkbReminderInjector: Injector = {
199
+ name: "pkb-reminder",
200
+ order: DEFAULT_INJECTOR_ORDER.pkbReminder,
201
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
202
+ const inputs = readInjectionInputs(ctx);
203
+ const mode = inputs.mode ?? "full";
204
+ if (mode !== "full") return null;
205
+ if (!inputs.pkbActive) return null;
206
+ const reminder = await buildPkbReminderWithHints(inputs);
207
+ return {
208
+ id: "pkb-reminder",
209
+ text: reminder,
210
+ placement: "after-memory-prefix",
211
+ };
212
+ },
213
+ };
214
+
215
+ /**
216
+ * Render the PKB context block — wraps the raw content in
217
+ * `<knowledge_base>...</knowledge_base>` while escaping any closing tags
218
+ * inside the content that would break out of the XML wrapper. Mirrors the
219
+ * body of the pre-migration `injectPkbContext` helper exactly so the emitted
220
+ * bytes match.
221
+ */
222
+ function buildPkbContextBlock(content: string): string {
223
+ const escaped = content.replace(
224
+ /<\/knowledge_base\s*>/gi,
225
+ "&lt;/knowledge_base&gt;",
226
+ );
227
+ return `<knowledge_base>\n${escaped}\n</knowledge_base>`;
228
+ }
229
+
230
+ /**
231
+ * Build the PKB `<system_reminder>` text. When a dense query vector plus
232
+ * enough scope metadata is available, run the hybrid PKB search to
233
+ * surface up to three relevance hints; fall back to the flat static
234
+ * reminder on empty results or any error.
235
+ */
236
+ async function buildPkbReminderWithHints(
237
+ inputs: TurnInjectionInputs,
238
+ ): Promise<string> {
239
+ let hints: string[] = [];
240
+ const queryVector = inputs.pkbQueryVector;
241
+ if (
242
+ queryVector &&
243
+ queryVector.length > 0 &&
244
+ inputs.pkbScopeId &&
245
+ inputs.pkbConversation &&
246
+ inputs.pkbRoot
247
+ ) {
248
+ try {
249
+ const results = await searchPkbFiles(
250
+ queryVector,
251
+ inputs.pkbSparseVector,
252
+ 8,
253
+ [inputs.pkbScopeId],
254
+ );
255
+ const workingDir = inputs.pkbWorkingDir ?? inputs.pkbRoot;
256
+ const inContext = getInContextPkbPaths(
257
+ inputs.pkbConversation,
258
+ inputs.pkbAutoInjectList ?? [],
259
+ inputs.pkbRoot,
260
+ workingDir,
261
+ );
262
+ const pkbRoot = inputs.pkbRoot;
263
+ // Gate on `denseScore` (cosine, [0, 1]) so the quality bar is stable
264
+ // regardless of whether sparse was provided. Rank by `hybridScore`
265
+ // (RRF) when available — that captures the sparse signal for
266
+ // re-ordering eligible hits. hybridScore and denseScore live on
267
+ // different scales, so items with hybridScore are ordered together
268
+ // and placed ahead of items that only have denseScore.
269
+ hints = results
270
+ .filter((r) => {
271
+ const abs = resolve(pkbRoot, r.path);
272
+ if (inContext.has(abs)) return false;
273
+ const threshold = r.path.replace(/\\/g, "/").startsWith("archive/")
274
+ ? PKB_HINT_ARCHIVE_THRESHOLD
275
+ : PKB_HINT_THRESHOLD;
276
+ return r.denseScore >= threshold;
277
+ })
278
+ .sort((a, b) => {
279
+ const aHasHybrid = a.hybridScore !== undefined;
280
+ const bHasHybrid = b.hybridScore !== undefined;
281
+ if (aHasHybrid && !bHasHybrid) return -1;
282
+ if (!aHasHybrid && bHasHybrid) return 1;
283
+ if (aHasHybrid && bHasHybrid) {
284
+ return b.hybridScore! - a.hybridScore!;
285
+ }
286
+ return b.denseScore - a.denseScore;
287
+ })
288
+ .slice(0, 3)
289
+ .map((r) => r.path);
290
+ } catch (err) {
291
+ pkbReminderLog.warn(
292
+ { err: err instanceof Error ? err.message : String(err) },
293
+ "PKB hint search failed — falling back to flat reminder",
294
+ );
295
+ hints = [];
296
+ }
297
+ }
298
+ return buildPkbReminder(hints);
299
+ }
300
+
301
+ /**
302
+ * `now-md` injector — order 40, after-memory-prefix.
303
+ *
304
+ * Injects the NOW.md scratchpad content as
305
+ * `<NOW.md Always keep this up to date; keep under 10 lines>...` after any
306
+ * memory-prefix blocks.
307
+ *
308
+ * Gating:
309
+ * - `mode === "full"` (skipped in minimal mode).
310
+ * - `nowScratchpad` is a non-null, non-empty string.
311
+ */
312
+ export const nowMdInjector: Injector = {
313
+ name: "now-md",
314
+ order: DEFAULT_INJECTOR_ORDER.nowMd,
315
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
316
+ const inputs = readInjectionInputs(ctx);
317
+ const mode = inputs.mode ?? "full";
318
+ if (mode !== "full") return null;
319
+ const content = inputs.nowScratchpad;
320
+ if (!content) return null;
321
+ const text = `<NOW.md Always keep this up to date; keep under 10 lines>\n${content}\n</NOW.md>`;
322
+ return {
323
+ id: "now-md",
324
+ text,
325
+ placement: "after-memory-prefix",
326
+ };
327
+ },
328
+ };
329
+
330
+ /**
331
+ * `subagent-status` injector — order 50, append-user-tail.
332
+ *
333
+ * Appends a pre-built `<active_subagents>` block to the tail user message
334
+ * so the parent LLM has visibility into active/completed child subagents.
335
+ *
336
+ * The orchestrator builds the block via `buildSubagentStatusBlock` before
337
+ * the chain runs; this injector is a thin passthrough that applies gating
338
+ * and positioning.
339
+ *
340
+ * Gating:
341
+ * - `mode === "full"`.
342
+ * - `subagentStatusBlock` is a non-null, non-empty string.
343
+ */
344
+ export const subagentStatusInjector: Injector = {
345
+ name: "subagent-status",
346
+ order: DEFAULT_INJECTOR_ORDER.subagentStatus,
347
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
348
+ const inputs = readInjectionInputs(ctx);
349
+ const mode = inputs.mode ?? "full";
350
+ if (mode !== "full") return null;
351
+ const block = inputs.subagentStatusBlock;
352
+ if (!block) return null;
353
+ return {
354
+ id: "subagent-status",
355
+ text: block,
356
+ placement: "append-user-tail",
357
+ };
358
+ },
359
+ };
360
+
361
+ /**
362
+ * `slack-messages` injector — order 60, replace-run-messages.
363
+ *
364
+ * Swaps the conversation's `runMessages` array with a pre-rendered
365
+ * chronological Slack transcript built from the persisted message rows.
366
+ * Applied to every Slack conversation (channels and DMs alike). The
367
+ * orchestrator builds the transcript via `loadSlackChronologicalMessages`
368
+ * before the chain runs.
369
+ *
370
+ * The injector preserves the pre-migration memory-block prepending
371
+ * behaviour: `extractMemoryPrefixBlocks` is re-applied to the Slack
372
+ * transcript's tail user message inside `applyRuntimeInjections` when the
373
+ * replacement fires.
374
+ *
375
+ * Active in both `full` and `minimal` mode — Slack transcript replacement
376
+ * is not a high-token optional block, it's the canonical view of Slack
377
+ * history for the model.
378
+ *
379
+ * Gating:
380
+ * - `channelCapabilities.channel === "slack"`.
381
+ * - `slackChronologicalMessages` has at least one entry.
382
+ */
383
+ export const slackMessagesInjector: Injector = {
384
+ name: "slack-messages",
385
+ order: DEFAULT_INJECTOR_ORDER.slackMessages,
386
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
387
+ const inputs = readInjectionInputs(ctx);
388
+ if (inputs.channelCapabilities?.channel !== "slack") return null;
389
+ const messages = inputs.slackChronologicalMessages;
390
+ if (!messages || messages.length === 0) return null;
391
+ return {
392
+ id: "slack-messages",
393
+ // `text` is informational only — `replace-run-messages` placements
394
+ // bypass the tail-user-message splice path. Kept non-empty so
395
+ // `composeInjectorChain` (text-only consumers) still counts this
396
+ // injector as contributing content.
397
+ text: "[slack-chronological-transcript]",
398
+ placement: "replace-run-messages",
399
+ messagesOverride: messages,
400
+ };
401
+ },
402
+ };
403
+
404
+ /**
405
+ * `thread-focus` injector — order 70, append-user-tail.
406
+ *
407
+ * Appends a non-persisted `<active_thread>` block listing the parent +
408
+ * replies of the thread the current inbound user message belongs to, so
409
+ * the model can orient even when the channel-wide chronological transcript
410
+ * is long and interleaved.
411
+ *
412
+ * The orchestrator builds the block via `loadSlackActiveThreadFocusBlock`
413
+ * (which short-circuits for DMs). This injector wraps the value so the
414
+ * block is applied declaratively through the chain.
415
+ *
416
+ * Gating:
417
+ * - `mode === "full"`.
418
+ * - `channelCapabilities.channel === "slack"` and `chatType === "channel"`
419
+ * (non-DM Slack conversation).
420
+ * - `slackActiveThreadFocusBlock` is a non-empty string.
421
+ */
422
+ export const threadFocusInjector: Injector = {
423
+ name: "thread-focus",
424
+ order: DEFAULT_INJECTOR_ORDER.threadFocus,
425
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
426
+ const inputs = readInjectionInputs(ctx);
427
+ const mode = inputs.mode ?? "full";
428
+ if (mode !== "full") return null;
429
+ const caps = inputs.channelCapabilities;
430
+ if (!caps || caps.channel !== "slack" || caps.chatType !== "channel") {
431
+ return null;
432
+ }
433
+ const block = inputs.slackActiveThreadFocusBlock;
434
+ if (typeof block !== "string" || block.length === 0) return null;
435
+ return {
436
+ id: "thread-focus",
437
+ text: block,
438
+ placement: "append-user-tail",
439
+ };
440
+ },
441
+ };
442
+
443
+ /**
444
+ * Bundle every default injector into a single first-party plugin. Registered
445
+ * at daemon startup via `external-plugins-bootstrap.ts`.
446
+ *
447
+ * Using one plugin per injector would inflate the registry and create
448
+ * spurious registration-order dependencies; a single plugin keeps the
449
+ * ordering contract entirely in the `order` field.
450
+ */
451
+ export const defaultInjectorsPlugin: Plugin = {
452
+ manifest: {
453
+ name: "default-injectors",
454
+ version: "1.0.0",
455
+ requires: {
456
+ pluginRuntime: "v1",
457
+ },
458
+ },
459
+ injectors: [
460
+ workspaceContextInjector,
461
+ unifiedTurnContextInjector,
462
+ pkbContextInjector,
463
+ pkbReminderInjector,
464
+ nowMdInjector,
465
+ subagentStatusInjector,
466
+ slackMessagesInjector,
467
+ threadFocusInjector,
468
+ ],
469
+ };
470
+
471
+ // Module-load side effect: register this default at import time so
472
+ // downstream consumers (including tests that skip `bootstrapPlugins()`)
473
+ // observe a populated registry by default. Idempotent via the swallowed
474
+ // duplicate-name check. Kept local to this module (rather than iterating
475
+ // an array in `defaults/index.ts`) so the registration only references
476
+ // the already-initialized `defaultInjectorsPlugin` identifier —
477
+ // avoiding a TDZ crash when tests `mock.module(...)` a dependency of any
478
+ // other default plugin and directly import this file.
479
+ try {
480
+ registerPlugin(defaultInjectorsPlugin);
481
+ } catch (err) {
482
+ if (
483
+ err instanceof PluginExecutionError &&
484
+ err.message.includes("already registered")
485
+ ) {
486
+ // already registered — expected when both index.ts and the direct
487
+ // file are imported in the same process
488
+ } else {
489
+ throw err;
490
+ }
491
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Default `llmCall` plugin — a true passthrough that declares the pipeline
3
+ * surface and always yields to downstream middleware.
4
+ *
5
+ * The plugin system wraps every LLM request in the `llmCall` pipeline. The
6
+ * actual call to {@link Provider.sendMessage} lives in the `runPipeline`
7
+ * terminal at the call site (`agent/loop.ts`); this default's only job is to
8
+ * contribute the manifest (`provides.llmCall: "v1"`) so other plugins can
9
+ * negotiate against the pipeline surface.
10
+ *
11
+ * Because this plugin is registered at module load — BEFORE user plugins are
12
+ * loaded by `bootstrapPlugins()` — it sits at the outermost layer in
13
+ * `composeMiddleware`'s onion ordering. If its middleware called
14
+ * `provider.sendMessage` directly (instead of `next(args)`) it would
15
+ * short-circuit the chain and silently disable every user-registered
16
+ * `llmCall` middleware in production. The middleware therefore just forwards
17
+ * to `next(args)`.
18
+ *
19
+ * Registered from `daemon/external-plugins-bootstrap.ts` via a side-effect
20
+ * import so the plugin is present in the registry before
21
+ * {@link bootstrapPlugins} walks it.
22
+ *
23
+ * Design doc: `.private/plans/agent-plugin-system.md` (PR 15).
24
+ */
25
+
26
+ import { registerPlugin } from "../registry.js";
27
+ import {
28
+ type LLMCallArgs,
29
+ type LLMCallResult,
30
+ type Plugin,
31
+ PluginExecutionError,
32
+ } from "../types.js";
33
+
34
+ /**
35
+ * The default LLM-call plugin. Its `llmCall` middleware is a passthrough that
36
+ * forwards to `next(args)` unchanged so any user-registered middleware
37
+ * (registered later, inner in the onion) still runs and the terminal at the
38
+ * call site performs the actual `provider.sendMessage(...)` call.
39
+ *
40
+ * Manifest declares `provides.llmCall: "v1"` so other plugins can negotiate
41
+ * against the pipeline surface and `requires.pluginRuntime: "v1"` to satisfy
42
+ * the registry's mandatory capability check.
43
+ */
44
+ export const defaultLlmCallPlugin: Plugin = {
45
+ manifest: {
46
+ name: "default-llm-call",
47
+ version: "1.0.0",
48
+ provides: { llmCall: "v1" },
49
+ requires: { pluginRuntime: "v1" },
50
+ },
51
+ middleware: {
52
+ llmCall: async function defaultLlmCall(
53
+ args: LLMCallArgs,
54
+ next,
55
+ _ctx,
56
+ ): Promise<LLMCallResult> {
57
+ return next(args);
58
+ },
59
+ },
60
+ };
61
+
62
+ // Module-load side effect: register this default at import time so
63
+ // downstream consumers (including tests that skip `bootstrapPlugins()`)
64
+ // observe a populated registry by default. Idempotent via the swallowed
65
+ // duplicate-name check. Kept local to this module (rather than iterating
66
+ // an array in `defaults/index.ts`) so the registration only references
67
+ // the already-initialized `defaultLlmCallPlugin` identifier —
68
+ // avoiding a TDZ crash when tests `mock.module(...)` a dependency of any
69
+ // other default plugin and directly import this file.
70
+ try {
71
+ registerPlugin(defaultLlmCallPlugin);
72
+ } catch (err) {
73
+ if (
74
+ err instanceof PluginExecutionError &&
75
+ err.message.includes("already registered")
76
+ ) {
77
+ // already registered — expected when both index.ts and the direct
78
+ // file are imported in the same process
79
+ } else {
80
+ throw err;
81
+ }
82
+ }