@vellumai/assistant 0.8.1 → 0.8.2

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 (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +325 -3
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Enumerate plugins materialized under `<workspaceDir>/plugins/`.
3
+ *
4
+ * The CLI command `assistant plugins list` is a thin wrapper. Downstream
5
+ * callers (the daemon's diagnostics surface, a future TUI, scripted
6
+ * audits) can call {@link listInstalledPlugins} directly without going
7
+ * through commander.
8
+ *
9
+ * Designed to be lenient: a malformed `package.json` is reported as an
10
+ * error on that one entry rather than failing the whole listing. The
11
+ * daemon makes the same call on boot via `external-plugin-loader.ts`
12
+ * and we want both surfaces to agree on what's present.
13
+ */
14
+
15
+ import {
16
+ existsSync,
17
+ readdirSync,
18
+ readFileSync,
19
+ statSync,
20
+ } from "node:fs";
21
+ import { join } from "node:path";
22
+
23
+ import { getWorkspacePluginsDir } from "../../util/platform.js";
24
+
25
+ /** Minimal manifest fields surfaced to the CLI. */
26
+ export interface PluginPackageMetadata {
27
+ readonly name?: string;
28
+ readonly version?: string;
29
+ readonly description?: string;
30
+ readonly peerDependencies?: Record<string, string>;
31
+ }
32
+
33
+ /** One installed plugin entry. */
34
+ export interface InstalledPluginInfo {
35
+ /** Directory name under `<workspaceDir>/plugins/`. */
36
+ readonly name: string;
37
+ /** Absolute path to the plugin directory. */
38
+ readonly target: string;
39
+ /** Parsed `package.json` content, when present and parseable. */
40
+ readonly packageJson: PluginPackageMetadata | null;
41
+ /**
42
+ * Non-fatal issues with this entry (missing `package.json`, malformed
43
+ * JSON, unexpected type, etc.). Empty when the entry parses cleanly.
44
+ */
45
+ readonly issues: readonly string[];
46
+ }
47
+
48
+ /** Options accepted by {@link listInstalledPlugins}. */
49
+ export interface ListInstalledPluginsOptions {
50
+ /** Override the workspace plugins directory. Falls back to {@link getWorkspacePluginsDir}. */
51
+ readonly workspacePluginsDir?: string;
52
+ }
53
+
54
+ /**
55
+ * Return one entry per directory under the workspace plugins directory,
56
+ * sorted alphabetically by name. Hidden entries (`.`-prefixed) and
57
+ * non-directory entries are skipped silently — the daemon's loader does
58
+ * the same. Returns `[]` if the plugins directory does not exist.
59
+ */
60
+ export function listInstalledPlugins(
61
+ opts: ListInstalledPluginsOptions = {},
62
+ ): InstalledPluginInfo[] {
63
+ const pluginsDir = opts.workspacePluginsDir ?? getWorkspacePluginsDir();
64
+ if (!existsSync(pluginsDir)) return [];
65
+
66
+ const entries = readdirSync(pluginsDir, { withFileTypes: true })
67
+ .filter((e) => !e.name.startsWith("."))
68
+ .filter((e) => {
69
+ if (e.isDirectory()) return true;
70
+ if (!e.isSymbolicLink()) return false;
71
+ // Resolve the symlink and only keep it if it points to a directory.
72
+ try {
73
+ return statSync(join(pluginsDir, e.name)).isDirectory();
74
+ } catch {
75
+ return false;
76
+ }
77
+ })
78
+ .map((e) => e.name)
79
+ .sort();
80
+
81
+ return entries.map((name) => readPluginEntry(pluginsDir, name));
82
+ }
83
+
84
+ function readPluginEntry(
85
+ pluginsDir: string,
86
+ name: string,
87
+ ): InstalledPluginInfo {
88
+ const target = join(pluginsDir, name);
89
+ const pkgJsonPath = join(target, "package.json");
90
+ const issues: string[] = [];
91
+
92
+ if (!existsSync(pkgJsonPath)) {
93
+ issues.push("missing package.json");
94
+ return { name, target, packageJson: null, issues };
95
+ }
96
+
97
+ let raw: string;
98
+ try {
99
+ raw = readFileSync(pkgJsonPath, "utf8");
100
+ } catch (err) {
101
+ issues.push(
102
+ `package.json unreadable: ${err instanceof Error ? err.message : String(err)}`,
103
+ );
104
+ return { name, target, packageJson: null, issues };
105
+ }
106
+
107
+ let parsed: unknown;
108
+ try {
109
+ parsed = JSON.parse(raw);
110
+ } catch (err) {
111
+ issues.push(
112
+ `package.json invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
113
+ );
114
+ return { name, target, packageJson: null, issues };
115
+ }
116
+
117
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
118
+ issues.push("package.json is not an object");
119
+ return { name, target, packageJson: null, issues };
120
+ }
121
+
122
+ const meta = parsed as Record<string, unknown>;
123
+ const packageJson: PluginPackageMetadata = {
124
+ name: typeof meta.name === "string" ? meta.name : undefined,
125
+ version: typeof meta.version === "string" ? meta.version : undefined,
126
+ description:
127
+ typeof meta.description === "string" ? meta.description : undefined,
128
+ peerDependencies:
129
+ typeof meta.peerDependencies === "object" &&
130
+ meta.peerDependencies !== null &&
131
+ !Array.isArray(meta.peerDependencies)
132
+ ? (meta.peerDependencies as Record<string, string>)
133
+ : undefined,
134
+ };
135
+
136
+ return { name, target, packageJson, issues };
137
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Remove a plugin previously materialized under `<workspaceDir>/plugins/`.
3
+ *
4
+ * Symmetric to {@link ./install-from-github.installPlugin}. The CLI
5
+ * command `assistant plugins uninstall <name>` is a thin wrapper that
6
+ * supplies the live workspace directory and formats the result.
7
+ *
8
+ * The operation is destructive — a successful return means the plugin
9
+ * directory and everything beneath it have been removed from disk.
10
+ * Callers needing a confirmation prompt should run it before invoking
11
+ * this function (the CLI command does this via `--force`).
12
+ */
13
+
14
+ import { existsSync, rmSync, statSync } from "node:fs";
15
+ import { join } from "node:path";
16
+
17
+ import { getWorkspacePluginsDir } from "../../util/platform.js";
18
+ import {
19
+ InvalidPluginNameError,
20
+ sanitizePluginName,
21
+ } from "./install-from-github.js";
22
+
23
+ /** Plugin is not present in the workspace plugins directory. */
24
+ export class PluginNotInstalledError extends Error {
25
+ constructor(
26
+ readonly pluginName: string,
27
+ readonly target: string,
28
+ ) {
29
+ super(`Plugin "${pluginName}" is not installed at ${target}.`);
30
+ this.name = "PluginNotInstalledError";
31
+ }
32
+ }
33
+
34
+ /** Options accepted by {@link uninstallPlugin}. */
35
+ export interface UninstallPluginOptions {
36
+ readonly name: string;
37
+ /** Override the workspace plugins directory. Falls back to {@link getWorkspacePluginsDir}. */
38
+ readonly workspacePluginsDir?: string;
39
+ }
40
+
41
+ /** Result of a successful uninstall. */
42
+ export interface UninstallPluginResult {
43
+ readonly name: string;
44
+ /** Absolute path that was removed. */
45
+ readonly target: string;
46
+ }
47
+
48
+ /**
49
+ * Validate the name, confirm the plugin exists, then recursively remove
50
+ * the install target. Throws {@link InvalidPluginNameError} if the name
51
+ * fails sanitization or {@link PluginNotInstalledError} if no directory
52
+ * (or symlink to a directory) is present at the resolved target.
53
+ *
54
+ * The name check is performed up front so an attacker-supplied
55
+ * `../../etc/passwd` style argument never reaches `rmSync` — even
56
+ * though commander typically prevents it at the argv level, defense in
57
+ * depth.
58
+ */
59
+ export function uninstallPlugin(
60
+ opts: UninstallPluginOptions,
61
+ ): UninstallPluginResult {
62
+ const name = sanitizePluginName(opts.name);
63
+ const pluginsDir = opts.workspacePluginsDir ?? getWorkspacePluginsDir();
64
+ const target = join(pluginsDir, name);
65
+
66
+ if (!existsSync(target)) {
67
+ throw new PluginNotInstalledError(name, target);
68
+ }
69
+
70
+ // `existsSync` follows symlinks; guard against a stray file with the
71
+ // plugin's name (which would be surprising rather than dangerous —
72
+ // we'd refuse to delete it).
73
+ const stats = statSync(target);
74
+ if (!stats.isDirectory()) {
75
+ throw new PluginNotInstalledError(name, target);
76
+ }
77
+
78
+ rmSync(target, { recursive: true, force: true });
79
+ return { name, target };
80
+ }
81
+
82
+ export { InvalidPluginNameError };
@@ -0,0 +1,111 @@
1
+ import type { Command } from "commander";
2
+
3
+ /**
4
+ * Result of detecting an unknown top-level subcommand. `suggestion` is set
5
+ * when a near-match is found (Levenshtein distance ≤ 40% of the longer name).
6
+ */
7
+ export interface UnknownCommandHit {
8
+ readonly token: string;
9
+ readonly suggestion?: string;
10
+ }
11
+
12
+ /**
13
+ * Collect every top-level subcommand name and alias registered on `program`.
14
+ */
15
+ export function knownCommandNames(program: Command): Set<string> {
16
+ const names = new Set<string>();
17
+ for (const cmd of program.commands) {
18
+ names.add(cmd.name());
19
+ for (const alias of cmd.aliases()) {
20
+ names.add(alias);
21
+ }
22
+ }
23
+ return names;
24
+ }
25
+
26
+ /**
27
+ * Pre-parse scan: returns the first positional argv token that doesn't match
28
+ * any known subcommand/alias, or null when the args look valid.
29
+ *
30
+ * Commander processes `--help` / `--version` before any action or hook runs,
31
+ * so `assistant invalid --help` would otherwise dump the root help instead of
32
+ * surfacing the unknown command. Callers should run this before `parse()` so
33
+ * the error wins over the help short-circuit.
34
+ *
35
+ * The first non-flag token is treated as the subcommand candidate. Flags are
36
+ * skipped wholesale; the root program has no value-taking options today.
37
+ */
38
+ export function detectUnknownCommand(
39
+ program: Command,
40
+ argv: readonly string[],
41
+ ): UnknownCommandHit | null {
42
+ const firstPositional = argv.find((token) => !token.startsWith("-"));
43
+ if (!firstPositional) return null;
44
+
45
+ const known = knownCommandNames(program);
46
+ if (known.has(firstPositional)) return null;
47
+
48
+ const suggestion = findClosestCommand(firstPositional, [...known]);
49
+ return suggestion ? { token: firstPositional, suggestion } : { token: firstPositional };
50
+ }
51
+
52
+ /**
53
+ * Format the unknown-command error as a multi-line message. Kept as a pure
54
+ * function so it can be reused by `default-action`'s in-parse path (when the
55
+ * user runs `assistant invalid` with no `--help`) and by the pre-parse path.
56
+ */
57
+ export function formatUnknownCommandMessage(hit: UnknownCommandHit): string {
58
+ const lines = [`unknown command '${hit.token}'`];
59
+ if (hit.suggestion) {
60
+ lines.push(`(Did you mean '${hit.suggestion}'?)`);
61
+ }
62
+ lines.push(`Run 'assistant --help' to see a list of available commands.`);
63
+ return lines.join("\n");
64
+ }
65
+
66
+ /**
67
+ * Find the closest matching command name using Levenshtein distance.
68
+ * Returns the best match if the distance is ≤ 40% of the longer string's
69
+ * length, otherwise returns undefined.
70
+ */
71
+ export function findClosestCommand(
72
+ input: string,
73
+ candidates: readonly string[],
74
+ ): string | undefined {
75
+ let best: string | undefined;
76
+ let bestDist = Infinity;
77
+ const lowered = input.toLowerCase();
78
+
79
+ for (const name of candidates) {
80
+ const dist = levenshtein(lowered, name.toLowerCase());
81
+ if (dist < bestDist) {
82
+ bestDist = dist;
83
+ best = name;
84
+ }
85
+ }
86
+
87
+ const maxLen = Math.max(input.length, best?.length ?? 0);
88
+ if (best && bestDist <= Math.ceil(maxLen * 0.4)) {
89
+ return best;
90
+ }
91
+ return undefined;
92
+ }
93
+
94
+ function levenshtein(a: string, b: string): number {
95
+ const m = a.length;
96
+ const n = b.length;
97
+ const dp: number[][] = Array.from({ length: m + 1 }, () =>
98
+ Array(n + 1).fill(0),
99
+ );
100
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
101
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
102
+ for (let i = 1; i <= m; i++) {
103
+ for (let j = 1; j <= n; j++) {
104
+ dp[i][j] =
105
+ a[i - 1] === b[j - 1]
106
+ ? dp[i - 1][j - 1]
107
+ : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
108
+ }
109
+ }
110
+ return dp[m][n];
111
+ }
@@ -5,6 +5,7 @@ import { Command } from "commander";
5
5
  import { initFeatureFlagOverrides } from "../config/assistant-feature-flags.js";
6
6
  import { getConfigReadOnly } from "../config/loader.js";
7
7
  import { isEmailEnabled } from "../email/feature-gate.js";
8
+ import { isExternalPluginsEnabled } from "../plugins/feature-gate.js";
8
9
  import { getWorkspaceDir } from "../util/platform.js";
9
10
  import { APP_VERSION } from "../version.js";
10
11
  import { registerAttachmentCommand } from "./commands/attachment.js";
@@ -37,18 +38,22 @@ import { registerNotificationsCommand } from "./commands/notifications.js";
37
38
  import { registerOAuthCommand } from "./commands/oauth/index.js";
38
39
  import { registerPendingCommand } from "./commands/pending.js";
39
40
  import { registerPlatformCommand } from "./commands/platform/index.js";
41
+ import { registerPluginsCommand } from "./commands/plugins.js";
40
42
  import { registerRoutesCommand } from "./commands/routes.js";
43
+ import { registerSchedulesCommand } from "./commands/schedules.js";
41
44
  import { registerSequenceCommand } from "./commands/sequence.js";
42
45
  import { registerSkillsCommand } from "./commands/skills.js";
43
46
  import { registerStatusCommand } from "./commands/status.js";
44
47
  import { registerSttCommand } from "./commands/stt.js";
45
48
  import { registerTaskCommand } from "./commands/task.js";
49
+ import { registerTelemetryCommand } from "./commands/telemetry.js";
46
50
  import { registerTrustCommand } from "./commands/trust.js";
47
51
  import { registerTtsCommand } from "./commands/tts.js";
48
52
  import { registerUiCommand } from "./commands/ui.js";
49
53
  import { registerUsageCommand } from "./commands/usage.js";
50
54
  import { registerWatchersCommand } from "./commands/watchers.js";
51
55
  import { registerWebhooksCommand } from "./commands/webhooks.js";
56
+ import { red } from "./lib/cli-colors.js";
52
57
  import { log } from "./logger.js";
53
58
 
54
59
  /**
@@ -65,6 +70,13 @@ export async function buildCliProgram(): Promise<Command> {
65
70
  .version(APP_VERSION)
66
71
  .allowExcessArguments(true);
67
72
 
73
+ // Color Commander-emitted error output red (unknown options, missing args,
74
+ // cmd.error() calls). Plain success output and --help text are untouched.
75
+ // The `red` helper is a no-op when stderr isn't a TTY or NO_COLOR is set.
76
+ program.configureOutput({
77
+ outputError: (str, write) => write(red(str)),
78
+ });
79
+
68
80
  program.addHelpText(
69
81
  "after",
70
82
  `
@@ -107,12 +119,17 @@ Examples:
107
119
  registerOAuthCommand(program);
108
120
  registerPendingCommand(program);
109
121
  registerPlatformCommand(program);
122
+ if (isExternalPluginsEnabled(getConfigReadOnly())) {
123
+ registerPluginsCommand(program);
124
+ }
110
125
  registerRoutesCommand(program);
126
+ registerSchedulesCommand(program);
111
127
  registerSequenceCommand(program);
112
128
  registerStatusCommand(program);
113
129
  registerSkillsCommand(program);
114
130
  registerSttCommand(program);
115
131
  registerTaskCommand(program);
132
+ registerTelemetryCommand(program);
116
133
  registerTrustCommand(program);
117
134
  registerTtsCommand(program);
118
135
  registerUiCommand(program);
@@ -126,9 +143,28 @@ Examples:
126
143
  // remain available even without a workspace.
127
144
  // Workspace-independent commands are exempt:
128
145
  // completions — pure shell-script generation, no workspace files needed
129
- const workspaceExemptCommands = new Set(["completions", "status"]);
146
+ // status — diagnostic; should run even when the workspace is broken
147
+ // changelog — pure read-only network surface backed by GitHub Releases;
148
+ // its on-disk cache is best-effort and tolerates a missing
149
+ // workspace dir (see changelog.ts:writeCache)
150
+ const workspaceExemptCommands = new Set([
151
+ "completions",
152
+ "status",
153
+ "changelog",
154
+ ]);
155
+ // An action command's `.name()` returns the leaf (e.g. "show" for
156
+ // `changelog show <ver>`), so we walk up the parent chain to see whether
157
+ // any ancestor — typically the top-level subcommand — is exempt.
158
+ const isExemptFromWorkspaceCheck = (command: Command): boolean => {
159
+ let current: Command | null | undefined = command;
160
+ while (current && current !== program) {
161
+ if (workspaceExemptCommands.has(current.name())) return true;
162
+ current = current.parent;
163
+ }
164
+ return false;
165
+ };
130
166
  program.hook("preAction", (_thisCommand, actionCommand) => {
131
- if (workspaceExemptCommands.has(actionCommand.name())) {
167
+ if (isExemptFromWorkspaceCheck(actionCommand)) {
132
168
  return;
133
169
  }
134
170
  const workspaceDir = getWorkspaceDir();
@@ -177,27 +177,31 @@ useEffect(() => {
177
177
  }, []);
178
178
  ```
179
179
 
180
- **File workflow:** Call `app_create` first to create the app record and scaffold, use `file_write` for each source file under `src/`, then call `app_refresh` once to compile and refresh the UI.
180
+ **File workflow:** Pass all source files inline via the `source_files` parameter of `app_create`. This writes and compiles the real app in a single call — no scaffold placeholder, no separate `file_write` or `app_refresh` needed for initial creation. For subsequent edits, use `file_edit`/`file_write` then call `app_refresh` once.
181
181
 
182
182
  **Allowed third-party packages:** `date-fns`, `chart.js`, `lodash-es`, `zod`, `clsx`, `lucide`. Import them directly - esbuild resolves them at build time. No CDN imports. Note: `lucide` is the vanilla JS icon library (not `lucide-react`). Use its `createElement` or `createIcons` API, or manually inline SVG - do not import JSX icon components.
183
183
 
184
- **Example - creating a multi-file project** (assuming app slug is `project-tracker`):
184
+ **Example - creating a multi-file project:**
185
185
 
186
186
  ```
187
- file_write("{workspaceDir}/data/apps/project-tracker/src/index.html", `<!DOCTYPE html>
187
+ app_create({
188
+ name: "Project Tracker",
189
+ description: "Track projects with status and priority",
190
+ schema_json: '{"type":"object","properties":{"title":{"type":"string"},"status":{"type":"string"}},"required":["title"]}',
191
+ preview: { title: "Project Tracker", icon: "📋" },
192
+ source_files: {
193
+ "src/index.html": `<!DOCTYPE html>
188
194
  <html lang="en">
189
195
  <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
190
196
  <title>Project Tracker</title></head>
191
197
  <body><div id="app"></div></body>
192
- </html>`)
193
-
194
- file_write("{workspaceDir}/data/apps/project-tracker/src/main.tsx", `import { render } from 'preact';
198
+ </html>`,
199
+ "src/main.tsx": `import { render } from 'preact';
195
200
  import { App } from './components/App';
196
201
  import './styles.css';
197
202
 
198
- render(<App />, document.getElementById('app')!);`)
199
-
200
- file_write("{workspaceDir}/data/apps/project-tracker/src/components/App.tsx", `import { FunctionComponent } from 'preact';
203
+ render(<App />, document.getElementById('app')!);`,
204
+ "src/components/App.tsx": `import { FunctionComponent } from 'preact';
201
205
  import { useState, useEffect } from 'preact/hooks';
202
206
  import { Header } from './Header';
203
207
 
@@ -217,9 +221,8 @@ export const App: FunctionComponent = () => {
217
221
  {/* ... */}
218
222
  </div>
219
223
  );
220
- };`)
221
-
222
- file_write("{workspaceDir}/data/apps/project-tracker/src/components/Header.tsx", `import { FunctionComponent } from 'preact';
224
+ };`,
225
+ "src/components/Header.tsx": `import { FunctionComponent } from 'preact';
223
226
 
224
227
  interface HeaderProps {
225
228
  title: string;
@@ -231,14 +234,12 @@ export const Header: FunctionComponent<HeaderProps> = ({ title, count }) => (
231
234
  <h1>{title}</h1>
232
235
  <span className="badge">{count} items</span>
233
236
  </header>
234
- );`)
235
-
236
- file_write("{workspaceDir}/data/apps/project-tracker/src/styles.css", `.app { padding: var(--v-spacing-lg); }
237
+ );`,
238
+ "src/styles.css": `.app { padding: var(--v-spacing-lg); }
237
239
  .header { display: flex; justify-content: space-between; align-items: center; }
238
- .badge { background: var(--v-accent); color: var(--v-aux-white); padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`)
239
-
240
- # After all files are written, compile and refresh:
241
- app_refresh(app_id)
240
+ .badge { background: var(--v-accent); color: var(--v-aux-white); padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`
241
+ }
242
+ })
242
243
  ```
243
244
 
244
245
  **Technical constraints (multi-file):**
@@ -329,10 +330,11 @@ Call `app_create` with:
329
330
  - `name`: Short descriptive name
330
331
  - `description`: One-sentence summary
331
332
  - `schema_json`: JSON schema as string
332
- - `auto_open`: (optional, defaults to `true`) Shows an inline preview card in chat
333
+ - `source_files`: Map of relative file paths to contents (e.g. `{"src/main.tsx": "...", "src/styles.css": "..."}`). **Always include this** with the complete app source — it writes, compiles, and opens the real app in a single call.
334
+ - `auto_open`: (optional, defaults to `true`) Shows an inline preview card in chat after the app is built. Only fires when real source files are provided (not for scaffold-only apps).
333
335
  - `preview`: Always include - `title` (required), `subtitle`, `description`, `icon` (image URL preferred, emoji fallback), `metrics` (up to 3 key-value pills)
334
336
 
335
- Do not pass `html` or `pages` to `app_create`; those single-file shortcuts are retired. After `app_create` returns the app ID, write the real app source under `src/` and call `app_refresh`.
337
+ Do not pass `html` or `pages` to `app_create`; those single-file shortcuts are retired.
336
338
 
337
339
  The app is NOT opened in a workspace panel automatically - users open it via the 'Open App' button on the inline card.
338
340
 
@@ -64,6 +64,13 @@
64
64
  },
65
65
  "required": ["title"]
66
66
  },
67
+ "source_files": {
68
+ "type": "object",
69
+ "description": "Map of file paths to file contents to write under the app directory. Supply the real app source here instead of making separate file_write calls. Paths are relative to the app directory (e.g. 'src/main.tsx', 'src/components/App.tsx', 'src/styles.css'). When src/main.tsx is included, no scaffold placeholder is created and the app compiles immediately with real content.",
70
+ "additionalProperties": {
71
+ "type": "string"
72
+ }
73
+ },
67
74
  "change_summary": {
68
75
  "type": "string",
69
76
  "description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add coin flip app', 'fix: correct button alignment'). Used as the version history entry."