@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,207 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { EmitFeedEventParams } from "../../../../../home/emit-feed-event.js";
4
+
5
+ // Capture all emitFeedEvent calls
6
+ const emittedEvents: EmitFeedEventParams[] = [];
7
+
8
+ mock.module("../../../../../home/emit-feed-event.js", () => ({
9
+ emitFeedEvent: async (params: EmitFeedEventParams) => {
10
+ emittedEvents.push(params);
11
+ return {
12
+ id: `emit:${params.source}:${params.dedupKey ?? "random"}`,
13
+ type: "action",
14
+ source: params.source,
15
+ title: params.title,
16
+ summary: params.summary,
17
+ priority: 50,
18
+ status: "new",
19
+ author: "assistant",
20
+ timestamp: new Date().toISOString(),
21
+ createdAt: new Date().toISOString(),
22
+ };
23
+ },
24
+ }));
25
+
26
+ // Stub messaging provider dependencies
27
+ const mockCreateDraft = mock(async () => ({ id: "draft-123" }));
28
+ const mockCreateDraftRaw = mock(async () => ({ id: "draft-raw-456" }));
29
+ const mockGetThread = mock(async () => ({
30
+ messages: [
31
+ {
32
+ payload: {
33
+ headers: [
34
+ { name: "From", value: "sender@example.com" },
35
+ { name: "To", value: "user@example.com" },
36
+ { name: "Subject", value: "Test Subject" },
37
+ { name: "Message-ID", value: "<msg-1@example.com>" },
38
+ ],
39
+ },
40
+ },
41
+ ],
42
+ }));
43
+ const mockGetProfile = mock(async () => ({
44
+ emailAddress: "user@example.com",
45
+ }));
46
+
47
+ mock.module("../../../../../messaging/providers/gmail/client.js", () => ({
48
+ createDraft: mockCreateDraft,
49
+ createDraftRaw: mockCreateDraftRaw,
50
+ getThread: mockGetThread,
51
+ getProfile: mockGetProfile,
52
+ }));
53
+
54
+ mock.module("../../../../../messaging/providers/gmail/mime-builder.js", () => ({
55
+ buildMultipartMime: () => "mock-raw-mime",
56
+ }));
57
+
58
+ const mockSendMessage = mock(async () => ({
59
+ id: "msg-sent-789",
60
+ threadId: "thread-1",
61
+ }));
62
+ const mockArchiveByQuery = mock(async () => ({
63
+ archived: 3,
64
+ truncated: false,
65
+ }));
66
+
67
+ mock.module("../shared.js", () => ({
68
+ resolveProvider: async (platform?: string) => ({
69
+ id: platform === "slack" ? "slack" : "gmail",
70
+ displayName: platform === "slack" ? "Slack" : "Gmail",
71
+ sendMessage: mockSendMessage,
72
+ archiveByQuery: mockArchiveByQuery,
73
+ }),
74
+ getProviderConnection: async () => ({ token: "mock-token" }),
75
+ ok: (msg: string) => ({ status: "ok", output: msg }),
76
+ err: (msg: string) => ({ status: "error", output: msg }),
77
+ extractEmail: (addr: string) => addr.replace(/.*<(.+)>/, "$1").toLowerCase(),
78
+ extractHeader: (
79
+ headers: Array<{ name: string; value: string }>,
80
+ name: string,
81
+ ) => headers.find((h: { name: string }) => h.name === name)?.value ?? null,
82
+ parseAddressList: (addrs: string | null) =>
83
+ addrs ? addrs.split(",").map((a: string) => a.trim()) : [],
84
+ }));
85
+
86
+ // Stub gmail-mime-helpers
87
+ mock.module("../gmail-mime-helpers.js", () => ({
88
+ guessMimeType: () => "application/octet-stream",
89
+ }));
90
+
91
+ // Stub conversation dependencies used by messaging-send cross-post
92
+ mock.module("../../../../../memory/conversation-crud.js", () => ({
93
+ addMessage: async () => ({ id: "msg-1" }),
94
+ getConversation: () => null,
95
+ }));
96
+ mock.module("../../../../../memory/conversation-disk-view.js", () => ({
97
+ syncMessageToDisk: () => {},
98
+ }));
99
+ mock.module("../../../../../memory/external-conversation-store.js", () => ({
100
+ getBindingByChannelChat: () => null,
101
+ }));
102
+
103
+ const { run: runSend } = await import("../messaging-send.js");
104
+ const { run: runArchive } = await import("../messaging-archive-by-sender.js");
105
+
106
+ const baseContext = {
107
+ workingDir: "/tmp/test",
108
+ conversationId: "conv-xyz",
109
+ assistantId: "assistant-1",
110
+ triggeredBySurfaceAction: true,
111
+ batchAuthorizedByTask: false,
112
+ approvedViaPrompt: false,
113
+ trustClass: "guardian" as const,
114
+ };
115
+
116
+ beforeEach(() => {
117
+ emittedEvents.length = 0;
118
+ mockCreateDraft.mockClear();
119
+ mockCreateDraftRaw.mockClear();
120
+ mockSendMessage.mockClear();
121
+ mockArchiveByQuery.mockClear();
122
+ });
123
+
124
+ afterEach(() => {
125
+ emittedEvents.length = 0;
126
+ });
127
+
128
+ describe("messaging-send feed events", () => {
129
+ test("Gmail draft creation emits with source 'gmail' and dedup by draft ID", async () => {
130
+ await runSend(
131
+ {
132
+ platform: "gmail",
133
+ conversation_id: "recipient@example.com",
134
+ text: "Hello there",
135
+ subject: "Test",
136
+ },
137
+ baseContext,
138
+ );
139
+
140
+ expect(emittedEvents).toHaveLength(1);
141
+ expect(emittedEvents[0].source).toBe("gmail");
142
+ expect(emittedEvents[0].title).toBe("Email Draft Created");
143
+ expect(emittedEvents[0].dedupKey).toBe("email-draft:draft-123");
144
+ });
145
+
146
+ test("Slack sends emit with source 'slack'", async () => {
147
+ await runSend(
148
+ {
149
+ platform: "slack",
150
+ conversation_id: "#general",
151
+ text: "Hello Slack",
152
+ },
153
+ baseContext,
154
+ );
155
+
156
+ expect(emittedEvents).toHaveLength(1);
157
+ expect(emittedEvents[0].source).toBe("slack");
158
+ expect(emittedEvents[0].title).toBe("Slack Message Sent");
159
+ expect(emittedEvents[0].dedupKey).toBe("message-sent:msg-sent-789");
160
+ });
161
+
162
+ test("Non-Gmail, non-Slack sends emit with source 'gmail' as fallback", async () => {
163
+ await runSend(
164
+ {
165
+ platform: "other-email",
166
+ conversation_id: "user@example.com",
167
+ text: "Hello",
168
+ },
169
+ baseContext,
170
+ );
171
+
172
+ expect(emittedEvents).toHaveLength(1);
173
+ expect(emittedEvents[0].source).toBe("gmail");
174
+ // Mock resolveProvider returns id:"gmail" for non-slack platforms,
175
+ // so this goes through the Gmail draft path
176
+ expect(emittedEvents[0].title).toBe("Email Draft Created");
177
+ expect(emittedEvents[0].summary).toBe("Created an email draft.");
178
+ });
179
+ });
180
+
181
+ describe("messaging-archive feed events", () => {
182
+ test("Archive emits only when result.archived > 0", async () => {
183
+ await runArchive(
184
+ { platform: "gmail", query: "from:sender@example.com" },
185
+ baseContext,
186
+ );
187
+
188
+ expect(emittedEvents).toHaveLength(1);
189
+ expect(emittedEvents[0].source).toBe("gmail");
190
+ expect(emittedEvents[0].title).toBe("Messages Archived");
191
+ expect(emittedEvents[0].summary).toContain("Archived 3 message(s)");
192
+ });
193
+
194
+ test("Zero-match archives do NOT emit", async () => {
195
+ mockArchiveByQuery.mockResolvedValueOnce({
196
+ archived: 0,
197
+ truncated: false,
198
+ });
199
+
200
+ await runArchive(
201
+ { platform: "gmail", query: "from:nobody@example.com" },
202
+ baseContext,
203
+ );
204
+
205
+ expect(emittedEvents).toHaveLength(0);
206
+ });
207
+ });
@@ -1,9 +1,13 @@
1
+ import { emitFeedEvent } from "../../../../home/emit-feed-event.js";
1
2
  import type {
2
3
  ToolContext,
3
4
  ToolExecutionResult,
4
5
  } from "../../../../tools/types.js";
6
+ import { getLogger } from "../../../../util/logger.js";
5
7
  import { err, getProviderConnection, ok, resolveProvider } from "./shared.js";
6
8
 
9
+ const log = getLogger("messaging-archive-by-sender");
10
+
7
11
  export async function run(
8
12
  input: Record<string, unknown>,
9
13
  context: ToolContext,
@@ -46,6 +50,14 @@ export async function run(
46
50
  }
47
51
 
48
52
  const summary = `Archived ${result.archived} message(s) matching query: ${query}`;
53
+ void emitFeedEvent({
54
+ source: "gmail",
55
+ title: "Messages Archived",
56
+ summary,
57
+ dedupKey: `email-archive:${Date.now()}`,
58
+ }).catch((err) => {
59
+ log.warn({ err }, "Failed to emit email archive feed event");
60
+ });
49
61
  if (result.truncated) {
50
62
  return ok(
51
63
  `${summary}\n\nNote: this operation was capped at 5000 messages. Additional messages matching the query may remain in the inbox. Run the command again to archive more.`,
@@ -1,6 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { basename } from "node:path";
3
3
 
4
+ import { emitFeedEvent } from "../../../../home/emit-feed-event.js";
4
5
  import {
5
6
  addMessage,
6
7
  getConversation,
@@ -149,6 +150,14 @@ export async function run(
149
150
  ccList.length > 0
150
151
  ? `To: ${toList.join(", ")}; Cc: ${ccList.join(", ")}`
151
152
  : `To: ${toList.join(", ")}`;
153
+ void emitFeedEvent({
154
+ source: "gmail",
155
+ title: "Email Draft Created",
156
+ summary: `Drafted reply to ${recipientSummary}.`,
157
+ dedupKey: `email-draft:${draft.id}`,
158
+ }).catch((err) => {
159
+ log.warn({ err }, "Failed to emit email draft feed event");
160
+ });
152
161
  return ok(
153
162
  `Gmail draft created with ${attachments.length} attachment(s): ${filenames} (Draft ID: ${draft.id}). ${recipientSummary}. Review in Gmail Drafts, then tell me to send it or send it yourself.`,
154
163
  );
@@ -169,6 +178,14 @@ export async function run(
169
178
  ccList.length > 0
170
179
  ? `To: ${toList.join(", ")}; Cc: ${ccList.join(", ")}`
171
180
  : `To: ${toList.join(", ")}`;
181
+ void emitFeedEvent({
182
+ source: "gmail",
183
+ title: "Email Draft Created",
184
+ summary: `Drafted reply to ${recipientSummary}.`,
185
+ dedupKey: `email-draft:${draft.id}`,
186
+ }).catch((err) => {
187
+ log.warn({ err }, "Failed to emit email draft feed event");
188
+ });
172
189
  return ok(
173
190
  `Gmail draft created (ID: ${draft.id}). ${recipientSummary}. Review in Gmail Drafts, then tell me to send it or send it yourself.`,
174
191
  );
@@ -195,6 +212,14 @@ export async function run(
195
212
  const draft = await createDraftRaw(gmailConn, raw, threadId);
196
213
 
197
214
  const filenames = attachments.map((a) => a.filename).join(", ");
215
+ void emitFeedEvent({
216
+ source: "gmail",
217
+ title: "Email Draft Created",
218
+ summary: "Created an email draft.",
219
+ dedupKey: `email-draft:${draft.id}`,
220
+ }).catch((err) => {
221
+ log.warn({ err }, "Failed to emit email draft feed event");
222
+ });
198
223
  return ok(
199
224
  `Gmail draft created with ${attachments.length} attachment(s): ${filenames} (Draft ID: ${draft.id}). Review in Gmail Drafts, then tell me to send it or send it yourself.`,
200
225
  );
@@ -211,6 +236,14 @@ export async function run(
211
236
  undefined,
212
237
  threadId,
213
238
  );
239
+ void emitFeedEvent({
240
+ source: "gmail",
241
+ title: "Email Draft Created",
242
+ summary: "Created an email draft.",
243
+ dedupKey: `email-draft:${draft.id}`,
244
+ }).catch((err) => {
245
+ log.warn({ err }, "Failed to emit email draft feed event");
246
+ });
214
247
  return ok(
215
248
  `Gmail draft created (ID: ${draft.id}). Review it in your Gmail Drafts, then tell me to send it or send it yourself from Gmail.`,
216
249
  );
@@ -224,6 +257,31 @@ export async function run(
224
257
  assistantId: context.assistantId,
225
258
  });
226
259
 
260
+ const sendSummary =
261
+ provider.id === "slack"
262
+ ? "Sent a Slack message."
263
+ : provider.id === "telegram"
264
+ ? "Sent a Telegram message."
265
+ : "Sent an email.";
266
+ void emitFeedEvent({
267
+ source:
268
+ provider.id === "slack"
269
+ ? "slack"
270
+ : provider.id === "telegram"
271
+ ? "telegram"
272
+ : "gmail",
273
+ title:
274
+ provider.id === "slack"
275
+ ? "Slack Message Sent"
276
+ : provider.id === "telegram"
277
+ ? "Telegram Message Sent"
278
+ : "Email Sent",
279
+ summary: sendSummary,
280
+ dedupKey: `message-sent:${result.id}`,
281
+ }).catch((err) => {
282
+ log.warn({ err }, "Failed to emit message send feed event");
283
+ });
284
+
227
285
  const threadSuffix = result.threadId
228
286
  ? `, "thread_id": "${result.threadId}"`
229
287
  : "";
@@ -8,7 +8,7 @@ metadata:
8
8
  display-name: "Schedule"
9
9
  ---
10
10
 
11
- Manage scheduled automations. Schedules can be **recurring** (cron or RRULE expression) or **one-shot** (a single `fire_at` timestamp). Both recurring and one-shot schedules support two modes: **execute** (run a message through the assistant) and **notify** (send a notification to the user).
11
+ Manage scheduled automations. Schedules can be **recurring** (cron or RRULE expression) or **one-shot** (a single `fire_at` timestamp). Schedules support three modes: **execute** (run a message through the assistant), **notify** (send a notification to the user), and **script** (run a shell command directly without LLM involvement).
12
12
 
13
13
  ## Schedule Syntax
14
14
 
@@ -70,11 +70,13 @@ Exclusions (EXDATE, EXRULE) always take precedence over inclusions (RRULE, RDATE
70
70
  To create a one-time schedule that fires once and is done, pass `fire_at` (an ISO 8601 timestamp) instead of an `expression`. This replaces the old reminder concept - "remind me at 3pm" becomes a one-shot schedule with `fire_at`.
71
71
 
72
72
  One-shot schedules:
73
+
73
74
  - Fire once at the specified time, then are marked as `fired` and disabled.
74
75
  - Support both `execute` and `notify` modes (see below).
75
76
  - Can be cancelled before they fire.
76
77
 
77
78
  Examples:
79
+
78
80
  - "remind me at 3pm" → `schedule_create` with `fire_at: "2025-03-15T15:00:00-05:00"`, `mode: "notify"`
79
81
  - "at 5pm, check my email and summarize it" → `schedule_create` with `fire_at`, `mode: "execute"`
80
82
 
@@ -84,8 +86,9 @@ The `mode` parameter controls what happens when a schedule fires:
84
86
 
85
87
  - **execute** (default) - sends the schedule's message to a background assistant conversation for autonomous handling. The assistant processes the message as if the user sent it.
86
88
  - **notify** - sends a notification to the user via the notification pipeline. No assistant processing occurs.
89
+ - **script** - runs the `script` field as a shell command directly. No LLM invoked, no conversation created. stdout/stderr are captured in the schedule run record. Exit code 0 = success, non-zero = error. Commands run in the workspace directory with a 60-second timeout.
87
90
 
88
- Use `notify` for simple reminders ("remind me to take medicine at 9am") and `execute` for tasks that need assistant action ("check my calendar at 8am and send me a digest").
91
+ Use `notify` for simple reminders ("remind me to take medicine at 9am"), `execute` for tasks that need assistant action ("check my calendar at 8am and send me a digest"), and `script` for lightweight shell automations that don't need LLM involvement ("refresh a cache", "poll an API", "rotate logs").
89
92
 
90
93
  ## Conversation Reuse
91
94
 
@@ -121,6 +124,7 @@ Optionally pass `routing_hints` (a JSON object) to influence routing decisions (
121
124
  3. If neither field is present or the interface is `cli`, omit `preferred_channels`.
122
125
 
123
126
  When a channel is determined, include it as a routing hint:
127
+
124
128
  ```
125
129
  routing_hints: { preferred_channels: ["<resolved channel>"] }
126
130
  routing_intent: "all_channels"
@@ -157,6 +161,7 @@ Phrases like "at the 45 minute mark", "at the top of the hour", "at noon", or "2
157
161
  - "noon" / "midnight" → 12:00 PM or 12:00 AM today; if past, tomorrow
158
162
 
159
163
  3. **Ask only if truly ambiguous** - if neither rule resolves, ask for clarification. Never silently default to "from now."
164
+
160
165
  - Timezones default to the system timezone if omitted. Use IANA timezone identifiers (e.g. "America/Los_Angeles").
161
166
  - Prefer RRULE for complex patterns that cron cannot express (e.g. "every other Tuesday", "last weekday of the month").
162
167
 
@@ -184,7 +189,7 @@ Scheduled messages run without user interaction. If the task produces output tha
184
189
  Choose the right delivery tool based on the content:
185
190
 
186
191
  - **Rich content** (digests, summaries, reports): For Gmail, use `messaging_send` with the target platform and conversation ID. For Slack, use the Slack Web API directly via CLI (`chat.postMessage`). This preserves the full content and posts directly.
187
- - **Short alerts** (status updates, completion notices): Use `send_notification` to let the notification router pick the best channel. Note: the router's decision engine rewrites content into short alerts, so it is not suitable for rich content.
192
+ - **Short alerts** (status updates, completion notices): Use `assistant notifications send` via `bash` to let the notification router pick the best channel. Note: the router's decision engine rewrites content into short alerts, so it is not suitable for rich content.
188
193
 
189
194
  Example schedule message for a Slack digest:
190
195
 
@@ -32,7 +32,11 @@
32
32
  },
33
33
  "message": {
34
34
  "type": "string",
35
- "description": "The message to send to the assistant when the schedule triggers"
35
+ "description": "The message to send to the assistant when the schedule triggers. Required for execute and notify modes."
36
+ },
37
+ "script": {
38
+ "type": "string",
39
+ "description": "The shell command to run when the schedule triggers. Required for script mode. Runs in the workspace directory with a sanitized environment."
36
40
  },
37
41
  "enabled": {
38
42
  "type": "boolean",
@@ -40,8 +44,8 @@
40
44
  },
41
45
  "mode": {
42
46
  "type": "string",
43
- "enum": ["notify", "execute"],
44
- "description": "Whether to notify the user or execute autonomously when triggered. Defaults to \"execute\"."
47
+ "enum": ["notify", "execute", "script"],
48
+ "description": "Whether to notify the user, execute autonomously via LLM, or run a shell command directly. Defaults to \"execute\"."
45
49
  },
46
50
  "routing_intent": {
47
51
  "type": "string",
@@ -65,7 +69,7 @@
65
69
  "description": "Brief non-technical explanation of why this tool is being called"
66
70
  }
67
71
  },
68
- "required": ["name", "message"]
72
+ "required": ["name"]
69
73
  },
70
74
  "executor": "tools/schedule-create.ts",
71
75
  "execution_target": "host"
@@ -127,7 +131,11 @@
127
131
  },
128
132
  "message": {
129
133
  "type": "string",
130
- "description": "New message to send when triggered"
134
+ "description": "New message to send when triggered (for execute/notify modes)"
135
+ },
136
+ "script": {
137
+ "type": "string",
138
+ "description": "New shell command to run when triggered (for script mode)"
131
139
  },
132
140
  "enabled": {
133
141
  "type": "boolean",
@@ -135,8 +143,8 @@
135
143
  },
136
144
  "mode": {
137
145
  "type": "string",
138
- "enum": ["notify", "execute"],
139
- "description": "Whether to notify the user or execute autonomously when triggered"
146
+ "enum": ["notify", "execute", "script"],
147
+ "description": "Whether to notify the user, execute autonomously via LLM, or run a shell command directly"
140
148
  },
141
149
  "routing_intent": {
142
150
  "type": "string",
@@ -0,0 +1,59 @@
1
+ # Script Mode Patterns
2
+
3
+ Script mode runs shell commands without LLM involvement, but scripts can escalate to the assistant or notify the user via CLI commands when conditions are met. This makes script mode a building block for conditional automation — fast, deterministic checks that only invoke heavier machinery when needed.
4
+
5
+ ## Script → LLM Escalation
6
+
7
+ Use `assistant conversations wake` to hand off to the assistant when a script detects something that needs LLM reasoning. The wake command injects a hint into an existing conversation without creating a user-visible message.
8
+
9
+ Example: poll an API and wake the assistant only when results change.
10
+
11
+ ```sh
12
+ LAST="/workspace/data/api-status-last.txt"
13
+ CURRENT=$(curl -sf https://api.example.com/status)
14
+ [ "$CURRENT" = "$(cat "$LAST" 2>/dev/null)" ] && exit 0
15
+ echo "$CURRENT" > "$LAST"
16
+ assistant conversations wake CONVERSATION_ID --hint "API status changed: $CURRENT" --source scheduled-poll
17
+ ```
18
+
19
+ Example: check disk usage and escalate to the assistant when it's high.
20
+
21
+ ```sh
22
+ USAGE=$(df /workspace --output=pcent | tail -1 | tr -d ' %')
23
+ [ "$USAGE" -lt 90 ] && exit 0
24
+ assistant conversations wake CONVERSATION_ID --hint "Disk usage at ${USAGE}% — investigate and clean up" --source disk-monitor
25
+ ```
26
+
27
+ ## Script → Notification
28
+
29
+ Use `assistant notifications send` to alert the user directly when a script detects something noteworthy. No LLM is involved — the notification goes straight to the user's connected channels.
30
+
31
+ Example: check if a service is down and notify the user.
32
+
33
+ ```sh
34
+ if ! curl -sf --max-time 5 https://myapp.example.com/health > /dev/null; then
35
+ assistant notifications send \
36
+ --source-channel scheduler \
37
+ --source-event-name schedule.notify \
38
+ --message "myapp.example.com health check failed" \
39
+ --urgency high \
40
+ --dedupe-key "myapp-health-$(date +%Y%m%d)"
41
+ fi
42
+ ```
43
+
44
+ Example: notify when a long-running background job finishes.
45
+
46
+ ```sh
47
+ if [ -f /workspace/data/export-complete.flag ]; then
48
+ assistant notifications send \
49
+ --source-channel scheduler \
50
+ --source-event-name schedule.notify \
51
+ --message "Data export finished — file ready at /workspace/data/export.csv" \
52
+ --no-requires-action
53
+ rm /workspace/data/export-complete.flag
54
+ fi
55
+ ```
56
+
57
+ ## Tips
58
+
59
+ Both patterns keep scheduled runs fast and cheap — the script exits immediately when nothing interesting happens, and only reaches for the assistant or notification system when there's something to act on. Use `quiet: true` on the schedule to suppress the per-run completion noise.
@@ -39,8 +39,6 @@ import * as contactMerge from "./bundled-skills/contacts/tools/contact-merge.js"
39
39
  import * as contactSearch from "./bundled-skills/contacts/tools/contact-search.js";
40
40
  import * as contactUpsert from "./bundled-skills/contacts/tools/contact-upsert.js";
41
41
  import * as googleContacts from "./bundled-skills/contacts/tools/google-contacts.js";
42
- // ── conversations ──────────────────────────────────────────────────────────────
43
- import * as renameConversation from "./bundled-skills/conversations/tools/rename-conversation.js";
44
42
  // ── document ───────────────────────────────────────────────────────────────────
45
43
  import * as documentCreate from "./bundled-skills/document/tools/document-create.js";
46
44
  import * as documentUpdate from "./bundled-skills/document/tools/document-update.js";
@@ -68,8 +66,6 @@ import * as messagingRead from "./bundled-skills/messaging/tools/messaging-read.
68
66
  import * as messagingSearch from "./bundled-skills/messaging/tools/messaging-search.js";
69
67
  import * as messagingSend from "./bundled-skills/messaging/tools/messaging-send.js";
70
68
  import * as messagingSenderDigest from "./bundled-skills/messaging/tools/messaging-sender-digest.js";
71
- // ── notifications ──────────────────────────────────────────────────────────────
72
- import * as sendNotification from "./bundled-skills/notifications/tools/send-notification.js";
73
69
  // ── phone-calls ────────────────────────────────────────────────────────────────
74
70
  import * as callEnd from "./bundled-skills/phone-calls/tools/call-end.js";
75
71
  import * as callStart from "./bundled-skills/phone-calls/tools/call-start.js";
@@ -84,8 +80,6 @@ import * as scheduleCreate from "./bundled-skills/schedule/tools/schedule-create
84
80
  import * as scheduleDelete from "./bundled-skills/schedule/tools/schedule-delete.js";
85
81
  import * as scheduleList from "./bundled-skills/schedule/tools/schedule-list.js";
86
82
  import * as scheduleUpdate from "./bundled-skills/schedule/tools/schedule-update.js";
87
- // ── screen-watch ───────────────────────────────────────────────────────────────
88
- import * as startScreenWatch from "./bundled-skills/screen-watch/tools/start-screen-watch.js";
89
83
  // ── sequences ──────────────────────────────────────────────────────────────────
90
84
  import * as sequenceAnalytics from "./bundled-skills/sequences/tools/sequence-analytics.js";
91
85
  import * as sequenceCreate from "./bundled-skills/sequences/tools/sequence-create.js";
@@ -152,9 +146,6 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
152
146
  ["contacts:tools/contact-merge.ts", contactMerge],
153
147
  ["contacts:tools/google-contacts.ts", googleContacts],
154
148
 
155
- // conversations
156
- ["conversations:tools/rename-conversation.ts", renameConversation],
157
-
158
149
  // document
159
150
  ["document:tools/document-create.ts", documentCreate],
160
151
  ["document:tools/document-update.ts", documentUpdate],
@@ -190,9 +181,6 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
190
181
  ["messaging:tools/messaging-sender-digest.ts", messagingSenderDigest],
191
182
  ["messaging:tools/messaging-archive-by-sender.ts", messagingArchiveBySender],
192
183
 
193
- // notifications
194
- ["notifications:tools/send-notification.ts", sendNotification],
195
-
196
184
  // phone-calls
197
185
  ["phone-calls:tools/call-start.ts", callStart],
198
186
  ["phone-calls:tools/call-status.ts", callStatus],
@@ -210,9 +198,6 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
210
198
  ["schedule:tools/schedule-update.ts", scheduleUpdate],
211
199
  ["schedule:tools/schedule-delete.ts", scheduleDelete],
212
200
 
213
- // screen-watch
214
- ["screen-watch:tools/start-screen-watch.ts", startScreenWatch],
215
-
216
201
  // sequences
217
202
  ["sequences:tools/sequence-create.ts", sequenceCreate],
218
203
  ["sequences:tools/sequence-list.ts", sequenceList],
@@ -327,7 +327,7 @@
327
327
  "key": "onboarding-pre-chat",
328
328
  "label": "Pre-Chat Onboarding Flow",
329
329
  "description": "Gates the 3-screen pre-chat onboarding flow (tools, tasks/tone, name exchange) shown before the first conversation",
330
- "defaultEnabled": false
330
+ "defaultEnabled": true
331
331
  },
332
332
  {
333
333
  "id": "home-tab",
@@ -376,6 +376,22 @@
376
376
  "label": "Message Height Cache",
377
377
  "description": "Swap the transcript's LazyVStack for a plain VStack so scrollContentHeight is the true sum of row heights (no LazyVStack estimator drift). Row frames are not pinned — the earlier frame-pinning approach was removed because it caused overlap when rows grew past their first measurement (streaming, expanding thinking blocks). Tradeoff: eager layout — every row measures up-front, which can stall for many seconds to minutes on very long conversations.",
378
378
  "defaultEnabled": true
379
+ },
380
+ {
381
+ "id": "permission-controls-v3",
382
+ "scope": "assistant",
383
+ "key": "permission-controls-v3",
384
+ "label": "Permission Controls v3",
385
+ "description": "Enables permission controls v3: gateway-backed auto-approve thresholds, risk badges on tool results, simplified Allow/Deny permission prompt, and rule editor modal.",
386
+ "defaultEnabled": false
387
+ },
388
+ {
389
+ "id": "compaction-playground",
390
+ "scope": "assistant",
391
+ "key": "compaction-playground",
392
+ "label": "Compaction Playground",
393
+ "description": "Expose the developer-only Compaction Playground tab in macOS Settings and enable the /playground/* HTTP endpoints for exercising compaction conditions. Dev-only; default off.",
394
+ "defaultEnabled": false
379
395
  }
380
396
  ]
381
397
  }
@@ -43,6 +43,8 @@ export {
43
43
  TwilioConfigSchema,
44
44
  WhatsAppConfigSchema,
45
45
  } from "./schemas/channels.js";
46
+ export type { ConversationsConfig } from "./schemas/conversations.js";
47
+ export { ConversationsConfigSchema } from "./schemas/conversations.js";
46
48
  export {
47
49
  DEFAULT_ELEVENLABS_VOICE_ID,
48
50
  VALID_CONVERSATION_TIMEOUTS,
@@ -240,6 +242,7 @@ import {
240
242
  TwilioConfigSchema,
241
243
  WhatsAppConfigSchema,
242
244
  } from "./schemas/channels.js";
245
+ import { ConversationsConfigSchema } from "./schemas/conversations.js";
243
246
  import { FilingConfigSchema } from "./schemas/filing.js";
244
247
  import { HeartbeatConfigSchema } from "./schemas/heartbeat.js";
245
248
  import { HostBrowserConfigSchema } from "./schemas/host-browser.js";
@@ -307,6 +310,9 @@ export const AssistantConfigSchema = z
307
310
  hostBrowser: HostBrowserConfigSchema.default(
308
311
  HostBrowserConfigSchema.parse({}),
309
312
  ),
313
+ conversations: ConversationsConfigSchema.default(
314
+ ConversationsConfigSchema.parse({}),
315
+ ),
310
316
  journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
311
317
  analysis: AnalysisConfigSchema.default(AnalysisConfigSchema.parse({})),
312
318
  backup: BackupConfigSchema.default(BackupConfigSchema.parse({})),
@@ -328,6 +334,19 @@ export const AssistantConfigSchema = z
328
334
  NotificationsConfigSchema.parse({}),
329
335
  ),
330
336
  ui: UiConfigSchema.default(UiConfigSchema.parse({})),
337
+ // Per-plugin config blocks keyed by plugin name. The schema is intentionally
338
+ // permissive — each plugin's manifest supplies its own validator which the
339
+ // plugin bootstrap (`external-plugins-bootstrap.ts`) runs against the raw
340
+ // block under `plugins.<name>` before handing the parsed result to the
341
+ // plugin's `init()`. Keeping this open here means adding a new plugin does
342
+ // not require a core-schema change, while invalid configs still surface
343
+ // through the plugin's own validator at bootstrap.
344
+ plugins: z
345
+ .record(z.string(), z.unknown())
346
+ .optional()
347
+ .describe(
348
+ "Per-plugin configuration keyed by plugin name. Validated downstream by each plugin's manifest.config validator at bootstrap.",
349
+ ),
331
350
  collectUsageData: z
332
351
  .boolean()
333
352
  .default(true)
@@ -54,7 +54,7 @@ export const BackupConfigSchema = z
54
54
  .int("backup.retention must be an integer")
55
55
  .min(1, "backup.retention must be >= 1")
56
56
  .max(100, "backup.retention must be <= 100")
57
- .default(7)
57
+ .default(3)
58
58
  .describe("Number of recent backups to retain"),
59
59
  offsite: BackupOffsiteConfigSchema.default(
60
60
  BackupOffsiteConfigSchema.parse({}),