@vellumai/assistant 0.6.4 → 0.6.5

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 (717) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +32 -36
  3. package/Dockerfile +12 -0
  4. package/README.md +3 -4
  5. package/bun.lock +8 -3
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/error-handling.md +111 -0
  9. package/docs/skills.md +10 -10
  10. package/docs/stt-provider-onboarding.md +2 -1
  11. package/knip.json +9 -2
  12. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  14. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  15. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  16. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  17. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  18. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  19. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  20. package/openapi.yaml +123 -11
  21. package/package.json +6 -3
  22. package/scripts/generate-openapi.ts +50 -11
  23. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  24. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  25. package/src/__tests__/agent-loop.test.ts +112 -1
  26. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  27. package/src/__tests__/anthropic-provider.test.ts +171 -2
  28. package/src/__tests__/approval-cascade.test.ts +31 -10
  29. package/src/__tests__/approval-routes-http.test.ts +134 -10
  30. package/src/__tests__/assistant-attachments.test.ts +44 -0
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  32. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  33. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  34. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  35. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  36. package/src/__tests__/btw-routes.test.ts +47 -1
  37. package/src/__tests__/call-controller.test.ts +1 -2
  38. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  39. package/src/__tests__/catalog-cache.test.ts +27 -4
  40. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  41. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  42. package/src/__tests__/checker.test.ts +428 -501
  43. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  44. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  45. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  46. package/src/__tests__/config-analysis.test.ts +11 -28
  47. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  48. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  49. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  50. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  51. package/src/__tests__/config-schema.test.ts +427 -114
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  54. package/src/__tests__/contacts-write.test.ts +4 -4
  55. package/src/__tests__/context-token-estimator.test.ts +191 -1
  56. package/src/__tests__/context-window-manager.test.ts +530 -2
  57. package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
  58. package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
  59. package/src/__tests__/conversation-agent-loop.test.ts +412 -82
  60. package/src/__tests__/conversation-attachments.test.ts +1 -1
  61. package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
  62. package/src/__tests__/conversation-error.test.ts +37 -6
  63. package/src/__tests__/conversation-history-web-search.test.ts +6 -0
  64. package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
  65. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  66. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  67. package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
  68. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  69. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
  70. package/src/__tests__/conversation-queue.test.ts +41 -26
  71. package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  75. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  76. package/src/__tests__/conversation-slash-queue.test.ts +34 -19
  77. package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
  78. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  79. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  80. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  81. package/src/__tests__/conversation-title-service.test.ts +2 -2
  82. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  83. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  84. package/src/__tests__/conversation-usage.test.ts +3 -1
  85. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  86. package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
  87. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
  88. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  89. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  90. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  91. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  92. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  93. package/src/__tests__/credentials-cli.test.ts +1 -9
  94. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  95. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  96. package/src/__tests__/delete-propagation.test.ts +437 -0
  97. package/src/__tests__/dm-backfill.test.ts +417 -0
  98. package/src/__tests__/dm-persistence.test.ts +227 -0
  99. package/src/__tests__/edit-propagation.test.ts +280 -0
  100. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  101. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  102. package/src/__tests__/estimator-calibration.test.ts +213 -0
  103. package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
  104. package/src/__tests__/file-write-tool.test.ts +151 -1
  105. package/src/__tests__/filing-service.test.ts +255 -0
  106. package/src/__tests__/gemini-provider.test.ts +0 -3
  107. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  108. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  109. package/src/__tests__/heartbeat-service.test.ts +96 -15
  110. package/src/__tests__/host-shell-tool.test.ts +124 -18
  111. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  112. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  113. package/src/__tests__/intent-routing.test.ts +1 -40
  114. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  115. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  116. package/src/__tests__/llm-resolver.test.ts +214 -0
  117. package/src/__tests__/llm-schema.test.ts +223 -0
  118. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  119. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  120. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  121. package/src/__tests__/model-intents.test.ts +9 -83
  122. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  123. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  124. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  125. package/src/__tests__/oauth-store.test.ts +10 -7
  126. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  127. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  128. package/src/__tests__/openai-provider.test.ts +7 -0
  129. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  130. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  131. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  132. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  133. package/src/__tests__/permission-mode.test.ts +16 -0
  134. package/src/__tests__/permission-types.test.ts +0 -1
  135. package/src/__tests__/persona-resolver.test.ts +13 -13
  136. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  137. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  138. package/src/__tests__/pricing.test.ts +50 -3
  139. package/src/__tests__/profiler-routes.test.ts +1 -1
  140. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  141. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  143. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  144. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  145. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  146. package/src/__tests__/reaction-persistence.test.ts +560 -0
  147. package/src/__tests__/relay-server.test.ts +1 -1
  148. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  149. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  150. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  151. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  152. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  153. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  154. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  156. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  157. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  158. package/src/__tests__/server-history-render.test.ts +31 -0
  159. package/src/__tests__/shell-parser-property.test.ts +13 -13
  160. package/src/__tests__/skill-cache-store.test.ts +182 -0
  161. package/src/__tests__/skills.test.ts +19 -33
  162. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  163. package/src/__tests__/slack-skill.test.ts +3 -8
  164. package/src/__tests__/starter-bundle.test.ts +35 -0
  165. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  166. package/src/__tests__/suggestion-routes.test.ts +160 -3
  167. package/src/__tests__/system-prompt.test.ts +22 -35
  168. package/src/__tests__/task-runner.test.ts +3 -1
  169. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  170. package/src/__tests__/terminal-tools.test.ts +8 -0
  171. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  172. package/src/__tests__/thread-backfill.test.ts +941 -0
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  175. package/src/__tests__/tool-executor.test.ts +60 -94
  176. package/src/__tests__/trust-store.test.ts +442 -109
  177. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  178. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  179. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  180. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  181. package/src/__tests__/volume-security-guard.test.ts +3 -2
  182. package/src/__tests__/web-search-history.test.ts +337 -0
  183. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  184. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  185. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  186. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  187. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  188. package/src/__tests__/workspace-policy.test.ts +1 -13
  189. package/src/acp/client-handler.ts +1 -2
  190. package/src/agent/loop.ts +209 -17
  191. package/src/avatar/resvg-lazy.test.ts +136 -0
  192. package/src/avatar/resvg-lazy.ts +82 -9
  193. package/src/avatar/traits-png-sync.ts +21 -1
  194. package/src/browser/__tests__/operations.test.ts +163 -0
  195. package/src/browser/identifiers.ts +51 -0
  196. package/src/browser/operations.ts +660 -0
  197. package/src/browser/types.ts +81 -0
  198. package/src/calls/guardian-question-copy.ts +2 -2
  199. package/src/calls/telephony-stt-routing.ts +1 -1
  200. package/src/calls/voice-session-bridge.ts +1 -0
  201. package/src/cli/AGENTS.md +1 -1
  202. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  203. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  204. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  205. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  206. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  207. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  208. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  209. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  210. package/src/cli/commands/__tests__/task.test.ts +913 -0
  211. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  212. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  213. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  214. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  215. package/src/cli/commands/attachment.ts +182 -0
  216. package/src/cli/commands/browser.ts +350 -0
  217. package/src/cli/commands/cache.ts +341 -0
  218. package/src/cli/commands/completions.ts +0 -3
  219. package/src/cli/commands/config.ts +6 -6
  220. package/src/cli/commands/conversations-import.ts +347 -0
  221. package/src/cli/commands/conversations.ts +14 -1
  222. package/src/cli/commands/email.ts +234 -194
  223. package/src/cli/commands/image-generation.ts +300 -0
  224. package/src/cli/commands/inference.ts +200 -0
  225. package/src/cli/commands/memory.ts +127 -17
  226. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  227. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  228. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  229. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  230. package/src/cli/commands/stt.ts +339 -0
  231. package/src/cli/commands/task.ts +795 -0
  232. package/src/cli/commands/trust.ts +50 -19
  233. package/src/cli/commands/tts.ts +273 -0
  234. package/src/cli/commands/ui.ts +670 -0
  235. package/src/cli/commands/watchers.ts +509 -0
  236. package/src/cli/lib/daemon-credential-client.ts +0 -19
  237. package/src/cli/program.ts +23 -4
  238. package/src/cli.ts +0 -37
  239. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  240. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  241. package/src/config/bundled-skills/messaging/SKILL.md +2 -2
  242. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  243. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
  244. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  245. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  246. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  247. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  248. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  249. package/src/config/bundled-tool-registry.ts +0 -175
  250. package/src/config/env.ts +7 -2
  251. package/src/config/feature-flag-registry.json +25 -9
  252. package/src/config/llm-resolver.ts +128 -0
  253. package/src/config/loader.ts +194 -10
  254. package/src/config/raw-config-utils.ts +30 -2
  255. package/src/config/sanitize-for-transfer.ts +35 -0
  256. package/src/config/schema.ts +30 -41
  257. package/src/config/schemas/analysis.ts +3 -22
  258. package/src/config/schemas/calls.ts +0 -4
  259. package/src/config/schemas/filing.ts +2 -7
  260. package/src/config/schemas/heartbeat.ts +0 -5
  261. package/src/config/schemas/inference.ts +3 -23
  262. package/src/config/schemas/llm.ts +318 -0
  263. package/src/config/schemas/memory-processing.ts +1 -9
  264. package/src/config/schemas/notifications.ts +4 -11
  265. package/src/config/schemas/platform.ts +3 -9
  266. package/src/config/schemas/security.ts +33 -0
  267. package/src/config/schemas/services.ts +9 -4
  268. package/src/config/schemas/stt.ts +1 -0
  269. package/src/config/schemas/tts.ts +53 -0
  270. package/src/config/schemas/updates.ts +1 -1
  271. package/src/config/schemas/workspace-git.ts +3 -40
  272. package/src/config/skills.ts +2 -2
  273. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  274. package/src/context/__tests__/microcompact.test.ts +805 -0
  275. package/src/context/estimator-calibration.ts +136 -0
  276. package/src/context/microcompact.ts +443 -0
  277. package/src/context/prompts/compact.md +12 -0
  278. package/src/context/token-estimator.ts +61 -3
  279. package/src/context/window-manager.ts +229 -25
  280. package/src/credential-execution/approval-bridge.ts +0 -1
  281. package/src/credential-execution/executable-discovery.ts +19 -8
  282. package/src/credential-execution/process-manager.test.ts +109 -0
  283. package/src/credential-execution/process-manager.ts +65 -2
  284. package/src/daemon/approval-generators.ts +29 -4
  285. package/src/daemon/assistant-attachments.ts +24 -13
  286. package/src/daemon/classifier.ts +2 -2
  287. package/src/daemon/config-watcher.ts +0 -1
  288. package/src/daemon/context-overflow-reducer.ts +4 -1
  289. package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
  290. package/src/daemon/conversation-agent-loop.ts +462 -80
  291. package/src/daemon/conversation-attachments.ts +2 -6
  292. package/src/daemon/conversation-error.ts +36 -1
  293. package/src/daemon/conversation-lifecycle.ts +30 -6
  294. package/src/daemon/conversation-messaging.ts +73 -4
  295. package/src/daemon/conversation-process.ts +10 -4
  296. package/src/daemon/conversation-queue-manager.ts +3 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +760 -29
  298. package/src/daemon/conversation-slash.ts +2 -2
  299. package/src/daemon/conversation-surfaces.ts +389 -1
  300. package/src/daemon/conversation-tool-setup.ts +10 -5
  301. package/src/daemon/conversation-usage.ts +1 -1
  302. package/src/daemon/conversation.ts +118 -30
  303. package/src/daemon/external-skills-bootstrap.ts +41 -0
  304. package/src/daemon/guardian-action-generators.ts +34 -14
  305. package/src/daemon/handlers/config-model.test.ts +86 -0
  306. package/src/daemon/handlers/config-model.ts +54 -12
  307. package/src/daemon/handlers/conversations.ts +9 -2
  308. package/src/daemon/handlers/shared.ts +39 -11
  309. package/src/daemon/handlers/skills.ts +2 -2
  310. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  311. package/src/daemon/lifecycle.ts +76 -14
  312. package/src/daemon/message-types/conversations.ts +14 -0
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/trust.ts +0 -2
  315. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  316. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  317. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  318. package/src/daemon/pkb-context-tracker.ts +125 -0
  319. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  320. package/src/daemon/pkb-reminder-builder.ts +31 -0
  321. package/src/daemon/providers-setup.ts +6 -0
  322. package/src/daemon/server.ts +117 -9
  323. package/src/daemon/tool-side-effects.ts +0 -9
  324. package/src/daemon/watch-handler.ts +4 -4
  325. package/src/daemon/web-search-history.ts +126 -0
  326. package/src/events/domain-events.ts +0 -1
  327. package/src/filing/filing-service.ts +9 -10
  328. package/src/heartbeat/heartbeat-service.ts +76 -28
  329. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  330. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  331. package/src/home/assistant-feed-authoring.ts +4 -0
  332. package/src/home/emit-feed-event.ts +4 -0
  333. package/src/home/feed-scheduler.ts +20 -4
  334. package/src/home/feed-types.ts +56 -2
  335. package/src/home/relationship-state-writer.ts +2 -2
  336. package/src/home/rollup-producer.ts +34 -5
  337. package/src/home/suggested-prompts.ts +101 -0
  338. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  339. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  340. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  341. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  342. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  343. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  344. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  345. package/src/ipc/cli-client.ts +2 -1
  346. package/src/ipc/cli-server.ts +26 -8
  347. package/src/ipc/gateway-client.ts +4 -4
  348. package/src/ipc/routes/attachment.ts +114 -0
  349. package/src/ipc/routes/browser-context.ts +61 -0
  350. package/src/ipc/routes/browser.ts +96 -0
  351. package/src/ipc/routes/cache.ts +96 -0
  352. package/src/ipc/routes/index.ts +17 -1
  353. package/src/ipc/routes/task-queue.ts +226 -0
  354. package/src/ipc/routes/task.ts +173 -0
  355. package/src/ipc/routes/ui-request.ts +50 -0
  356. package/src/ipc/routes/watcher.ts +203 -0
  357. package/src/ipc/socket-path.ts +100 -0
  358. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  359. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  360. package/src/memory/admin.ts +18 -0
  361. package/src/memory/conversation-analyze-job.ts +14 -13
  362. package/src/memory/conversation-attention-store.ts +13 -6
  363. package/src/memory/conversation-crud.ts +103 -3
  364. package/src/memory/conversation-group-migration.ts +38 -6
  365. package/src/memory/conversation-title-service.ts +7 -4
  366. package/src/memory/db-init.ts +2 -0
  367. package/src/memory/embedding-backend.ts +1 -1
  368. package/src/memory/graph/compaction.ts +299 -0
  369. package/src/memory/graph/consolidation.ts +4 -4
  370. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  371. package/src/memory/graph/extraction.test.ts +272 -2
  372. package/src/memory/graph/extraction.ts +173 -51
  373. package/src/memory/graph/graph-search.test.ts +92 -0
  374. package/src/memory/graph/graph-search.ts +4 -1
  375. package/src/memory/graph/narrative.ts +2 -2
  376. package/src/memory/graph/pattern-scan.ts +2 -2
  377. package/src/memory/graph/retriever.test.ts +459 -0
  378. package/src/memory/graph/retriever.ts +230 -48
  379. package/src/memory/graph/store.ts +41 -0
  380. package/src/memory/graph/tool-handlers.ts +27 -0
  381. package/src/memory/graph/tools.ts +6 -1
  382. package/src/memory/indexer.ts +5 -5
  383. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  384. package/src/memory/job-handlers/summarization.ts +2 -2
  385. package/src/memory/job-utils.ts +7 -1
  386. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  387. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  388. package/src/memory/jobs-store.ts +44 -3
  389. package/src/memory/jobs-worker.ts +4 -0
  390. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  391. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  392. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  393. package/src/memory/migrations/index.ts +1 -0
  394. package/src/memory/pkb/pkb-index.test.ts +368 -0
  395. package/src/memory/pkb/pkb-index.ts +255 -0
  396. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  397. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  398. package/src/memory/pkb/pkb-search.test.ts +438 -0
  399. package/src/memory/pkb/pkb-search.ts +137 -0
  400. package/src/memory/pkb/types.ts +53 -0
  401. package/src/memory/qdrant-client.ts +122 -1
  402. package/src/memory/slack-thread-store.ts +37 -0
  403. package/src/messaging/providers/gmail/adapter.ts +6 -16
  404. package/src/messaging/providers/gmail/client.ts +22 -0
  405. package/src/messaging/providers/gmail/types.ts +7 -0
  406. package/src/messaging/providers/slack/adapter.ts +14 -2
  407. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  408. package/src/messaging/providers/slack/backfill.ts +101 -0
  409. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  410. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  411. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  412. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  413. package/src/messaging/style-analyzer.ts +5 -2
  414. package/src/notifications/README.md +9 -5
  415. package/src/notifications/decision-engine.ts +3 -9
  416. package/src/notifications/preference-extractor.ts +2 -6
  417. package/src/oauth/oauth-store.ts +1 -0
  418. package/src/oauth/platform-connection.test.ts +47 -0
  419. package/src/oauth/platform-connection.ts +15 -5
  420. package/src/oauth/seed-providers.ts +4 -2
  421. package/src/permissions/approval-policy.test.ts +948 -0
  422. package/src/permissions/approval-policy.ts +257 -0
  423. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  424. package/src/permissions/bash-risk-classifier.ts +707 -0
  425. package/src/permissions/checker.ts +217 -708
  426. package/src/permissions/command-registry.test.ts +535 -0
  427. package/src/permissions/command-registry.ts +825 -0
  428. package/src/permissions/defaults.ts +26 -78
  429. package/src/permissions/file-risk-classifier.test.ts +535 -0
  430. package/src/permissions/file-risk-classifier.ts +274 -0
  431. package/src/permissions/risk-types.ts +205 -0
  432. package/src/permissions/secret-prompter.ts +53 -2
  433. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  434. package/src/permissions/skill-risk-classifier.ts +214 -0
  435. package/src/permissions/trust-client.ts +52 -25
  436. package/src/permissions/trust-store-interface.ts +1 -6
  437. package/src/permissions/trust-store.ts +161 -62
  438. package/src/permissions/types.ts +23 -14
  439. package/src/permissions/web-risk-classifier.test.ts +170 -0
  440. package/src/permissions/web-risk-classifier.ts +89 -0
  441. package/src/permissions/workspace-policy.ts +1 -16
  442. package/src/platform/client.ts +19 -1
  443. package/src/prompts/persona-resolver.ts +3 -3
  444. package/src/prompts/system-prompt.ts +19 -20
  445. package/src/prompts/templates/SOUL.md +2 -2
  446. package/src/prompts/update-bulletin-job.ts +190 -0
  447. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  448. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  449. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  450. package/src/providers/anthropic/client.ts +183 -14
  451. package/src/providers/call-site-routing.ts +71 -0
  452. package/src/providers/gemini/client.ts +65 -2
  453. package/src/providers/managed-proxy/constants.ts +2 -1
  454. package/src/providers/model-catalog.ts +501 -33
  455. package/src/providers/model-intents.ts +4 -4
  456. package/src/providers/openai/chat-completions-provider.ts +57 -1
  457. package/src/providers/openai/responses-provider.ts +86 -9
  458. package/src/providers/openrouter/client.ts +76 -9
  459. package/src/providers/provider-env-vars.ts +56 -0
  460. package/src/providers/provider-send-message.ts +22 -5
  461. package/src/providers/ratelimit.ts +4 -0
  462. package/src/providers/registry.ts +19 -8
  463. package/src/providers/retry.ts +174 -39
  464. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  465. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  466. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  467. package/src/providers/speech-to-text/resolve.ts +7 -0
  468. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  469. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  470. package/src/providers/speech-to-text/xai.test.ts +155 -0
  471. package/src/providers/speech-to-text/xai.ts +97 -0
  472. package/src/providers/types.ts +93 -3
  473. package/src/runtime/AGENTS.md +2 -2
  474. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  475. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  476. package/src/runtime/agent-wake.ts +63 -22
  477. package/src/runtime/auth/route-policy.ts +4 -0
  478. package/src/runtime/btw-sidechain.ts +13 -3
  479. package/src/runtime/channel-reply-delivery.ts +106 -2
  480. package/src/runtime/decision-token.ts +116 -0
  481. package/src/runtime/gateway-client.ts +2 -2
  482. package/src/runtime/http-router.ts +32 -0
  483. package/src/runtime/http-server.ts +52 -1
  484. package/src/runtime/http-types.ts +23 -1
  485. package/src/runtime/interactive-ui.ts +362 -0
  486. package/src/runtime/invite-instruction-generator.ts +2 -2
  487. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  488. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  489. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  490. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  491. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  492. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  493. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  494. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  495. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  496. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  497. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  498. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  499. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  500. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  501. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  502. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  503. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  504. package/src/runtime/routes/approval-routes.ts +12 -17
  505. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  506. package/src/runtime/routes/avatar-routes.ts +20 -4
  507. package/src/runtime/routes/btw-routes.ts +1 -4
  508. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  509. package/src/runtime/routes/conversation-routes.ts +133 -27
  510. package/src/runtime/routes/debug-routes.ts +1 -1
  511. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  512. package/src/runtime/routes/events-routes.ts +16 -0
  513. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  514. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  515. package/src/runtime/routes/home-feed-routes.ts +120 -2
  516. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  517. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  518. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  519. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  520. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  521. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  522. package/src/runtime/routes/migration-routes.ts +720 -124
  523. package/src/runtime/routes/settings-routes.ts +4 -2
  524. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  525. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  526. package/src/runtime/routes/work-items-routes.ts +3 -2
  527. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  528. package/src/runtime/services/analyze-conversation.ts +12 -16
  529. package/src/runtime/skill-route-registry.ts +28 -6
  530. package/src/schedule/scheduler.ts +8 -0
  531. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  532. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  533. package/src/security/oauth2.ts +98 -35
  534. package/src/security/secure-keys.ts +7 -8
  535. package/src/security/token-manager.ts +27 -13
  536. package/src/security/untrusted-content.ts +102 -0
  537. package/src/skills/catalog-cache.ts +26 -7
  538. package/src/skills/catalog-install.ts +31 -3
  539. package/src/skills/skill-cache-store.ts +97 -0
  540. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  541. package/src/stt/daemon-batch-transcriber.ts +33 -0
  542. package/src/stt/stt-stream-session.ts +8 -1
  543. package/src/stt/types.ts +5 -1
  544. package/src/subagent/manager.ts +41 -13
  545. package/src/tasks/ephemeral-permissions.ts +9 -4
  546. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  547. package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
  548. package/src/tools/browser/browser-execution.ts +65 -38
  549. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  550. package/src/tools/credentials/tool-policy.ts +39 -5
  551. package/src/tools/credentials/vault.ts +9 -4
  552. package/src/tools/executor.ts +4 -0
  553. package/src/tools/filesystem/write.ts +52 -0
  554. package/src/tools/host-terminal/host-shell.ts +45 -5
  555. package/src/tools/memory/register.test.ts +185 -0
  556. package/src/tools/memory/register.ts +3 -1
  557. package/src/tools/network/web-fetch.ts +20 -10
  558. package/src/tools/network/web-search.ts +19 -4
  559. package/src/tools/permission-checker.ts +36 -15
  560. package/src/tools/policy-context.ts +25 -8
  561. package/src/tools/registry.ts +55 -3
  562. package/src/tools/side-effects.ts +0 -11
  563. package/src/tools/skills/execute.ts +2 -2
  564. package/src/tools/skills/sandbox-runner.ts +5 -2
  565. package/src/tools/terminal/backends/native.ts +51 -2
  566. package/src/tools/terminal/safe-env.ts +3 -2
  567. package/src/tools/terminal/shell.ts +1 -0
  568. package/src/tools/tool-manifest.ts +6 -21
  569. package/src/tools/types.ts +12 -3
  570. package/src/tools/verification-control-plane-policy.ts +1 -1
  571. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  572. package/src/tts/provider-catalog.ts +18 -0
  573. package/src/tts/providers/index.ts +2 -0
  574. package/src/tts/providers/xai-provider.ts +224 -0
  575. package/src/tts/types.ts +46 -0
  576. package/src/types/tar-stream.d.ts +66 -0
  577. package/src/util/json.ts +17 -0
  578. package/src/util/platform.ts +2 -2
  579. package/src/util/pricing.ts +15 -5
  580. package/src/watcher/engine.ts +1 -1
  581. package/src/watcher/providers/google-calendar.ts +134 -8
  582. package/src/watcher/providers/outlook-calendar.ts +42 -2
  583. package/src/workspace/git-service.ts +23 -4
  584. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  585. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  586. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  587. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  588. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  589. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  590. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  591. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  592. package/src/workspace/migrations/AGENTS.md +1 -1
  593. package/src/workspace/migrations/registry.ts +16 -0
  594. package/src/workspace/provider-commit-message-generator.ts +19 -38
  595. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  596. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  597. package/src/__tests__/gmail-preferences.test.ts +0 -117
  598. package/src/__tests__/outlook-attachments.test.ts +0 -301
  599. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  600. package/src/__tests__/outlook-categories.test.ts +0 -212
  601. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  602. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  603. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  604. package/src/__tests__/outlook-trash.test.ts +0 -77
  605. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  606. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  607. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  608. package/src/__tests__/update-bulletin.test.ts +0 -478
  609. package/src/__tests__/update-template-contract.test.ts +0 -29
  610. package/src/cli/commands/doctor.ts +0 -341
  611. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  612. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  613. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  614. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  615. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  616. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  617. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  618. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  619. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  620. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  621. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  622. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  623. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  624. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  625. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  626. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  627. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  628. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  629. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  630. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  631. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  632. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  633. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  634. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  635. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  636. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  637. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  638. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  639. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  640. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  641. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  642. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  643. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  644. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  645. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  646. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  647. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  648. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  649. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  650. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  651. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  652. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  653. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  654. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  655. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  656. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  657. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  658. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  659. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  660. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  661. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  662. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  663. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  664. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  665. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  666. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  667. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  668. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  669. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  670. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  671. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  672. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  673. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  674. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  675. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  676. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  677. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  678. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  679. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  680. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  681. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  682. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  683. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  684. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  685. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  686. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  687. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  688. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  689. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  690. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  691. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  692. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  693. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  694. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  695. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  696. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  697. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  698. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  699. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  700. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  701. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  702. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  703. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  704. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  705. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  706. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  707. package/src/prompts/templates/UPDATES.md +0 -50
  708. package/src/prompts/update-bulletin-format.ts +0 -85
  709. package/src/prompts/update-bulletin-state.ts +0 -58
  710. package/src/prompts/update-bulletin-template-path.ts +0 -13
  711. package/src/prompts/update-bulletin.ts +0 -139
  712. package/src/shared/provider-env-vars.ts +0 -19
  713. package/src/tools/watcher/create.ts +0 -86
  714. package/src/tools/watcher/delete.ts +0 -36
  715. package/src/tools/watcher/digest.ts +0 -54
  716. package/src/tools/watcher/list.ts +0 -83
  717. package/src/tools/watcher/update.ts +0 -71
@@ -0,0 +1,707 @@
1
+ /**
2
+ * Bash risk classifier — data-driven command risk classification.
3
+ *
4
+ * Implements RiskClassifier<BashClassifierInput> using the default command
5
+ * registry and user rules. This is the primary classifier for bash/host_bash
6
+ * tools — checker.ts delegates to `bashRiskClassifier.classify()` and maps
7
+ * the result to the permission system's RiskLevel enum.
8
+ *
9
+ * @see /docs/bash-risk-classifier-design.md
10
+ */
11
+
12
+ import type {
13
+ CommandSegment,
14
+ ParsedCommand,
15
+ } from "../tools/terminal/parser.js";
16
+ import { getLogger } from "../util/logger.js";
17
+ import { DEFAULT_COMMAND_REGISTRY } from "./command-registry.js";
18
+ import type {
19
+ ArgRule,
20
+ BashClassifierInput,
21
+ CommandRiskSpec,
22
+ Risk,
23
+ RiskAssessment,
24
+ RiskClassifier,
25
+ ScopeOption,
26
+ UserRule,
27
+ } from "./risk-types.js";
28
+ import { cachedParse } from "./shell-identity.js";
29
+
30
+ const log = getLogger("bash-risk-classifier");
31
+
32
+ // ── Risk ordering helpers ────────────────────────────────────────────────────
33
+
34
+ const RISK_ORD: Record<Risk, number> = {
35
+ low: 0,
36
+ medium: 1,
37
+ unknown: 2,
38
+ high: 3,
39
+ };
40
+
41
+ /**
42
+ * Numeric ordering for risk comparison.
43
+ *
44
+ * `high` outranks `unknown`: if any segment is definitively high-risk, the
45
+ * overall command is high — the known-dangerous signal dominates. `unknown`
46
+ * sits between medium and high: an unrecognized command is riskier than a
47
+ * known-medium one, but not as definitive as a known-high one.
48
+ */
49
+ export function riskOrd(risk: Risk): number {
50
+ return RISK_ORD[risk];
51
+ }
52
+
53
+ /** Return the higher of two risk levels. */
54
+ export function maxRisk(a: Risk, b: Risk): Risk {
55
+ return riskOrd(a) >= riskOrd(b) ? a : b;
56
+ }
57
+
58
+ /** Escalate a risk level by one step: low→medium, medium→high, high→high. */
59
+ export function escalateOne(risk: Risk): Risk {
60
+ switch (risk) {
61
+ case "low":
62
+ return "medium";
63
+ case "medium":
64
+ return "high";
65
+ case "high":
66
+ return "high";
67
+ case "unknown":
68
+ return "unknown";
69
+ }
70
+ }
71
+
72
+ // ── Compiled regex cache ─────────────────────────────────────────────────────
73
+ // The registry is static, so we can compile and cache RegExp instances for
74
+ // arg rules' valuePatterns. This avoids re-compiling on every classify call.
75
+
76
+ const compiledPatterns = new Map<string, RegExp>();
77
+
78
+ function getCompiledPattern(pattern: string): RegExp {
79
+ let re = compiledPatterns.get(pattern);
80
+ if (!re) {
81
+ re = new RegExp(pattern);
82
+ compiledPatterns.set(pattern, re);
83
+ }
84
+ return re;
85
+ }
86
+
87
+ /** Clear the compiled regex cache. Exposed for tests and hot-swap scenarios. */
88
+ export function clearCompiledPatterns(): void {
89
+ compiledPatterns.clear();
90
+ }
91
+
92
+ // ── Arg rule matching ────────────────────────────────────────────────────────
93
+
94
+ /**
95
+ * Check whether an arg matches an ArgRule.
96
+ *
97
+ * - If `flags` is set, the arg must be one of those flags. If `valuePattern`
98
+ * is also set, the arg must match both the flag list AND the pattern.
99
+ * - If only `valuePattern` is set (no flags), the arg is matched against the
100
+ * pattern (positional / any-arg matching).
101
+ * - If neither is set, the rule always matches (flag-presence-only rules
102
+ * should have flags set).
103
+ */
104
+ export function matchesArgRule(rule: ArgRule, arg: string): boolean {
105
+ if (rule.flags && rule.flags.length > 0) {
106
+ // Check for inline --flag=value form
107
+ const eqIdx = arg.indexOf("=");
108
+ if (eqIdx > 0) {
109
+ const flagPart = arg.slice(0, eqIdx);
110
+ const valuePart = arg.slice(eqIdx + 1);
111
+ if (rule.flags.includes(flagPart)) {
112
+ // Flag matched via --flag=value. Check valuePattern against the value portion.
113
+ if (rule.valuePattern) {
114
+ return getCompiledPattern(rule.valuePattern).test(valuePart);
115
+ }
116
+ return true;
117
+ }
118
+ }
119
+
120
+ // Standard flag match: arg must be one of the listed flags exactly
121
+ if (!rule.flags.includes(arg)) return false;
122
+ // If there's also a valuePattern but no inline value, the next-arg
123
+ // lookahead in classifySegment handles matching. For the flag-only
124
+ // check here, a flag match without inline value and with a valuePattern
125
+ // is a partial match — the caller handles the lookahead.
126
+ if (rule.valuePattern) {
127
+ // Don't match here — let the lookahead in classifySegment handle it.
128
+ // Return false so the caller knows to try next-arg matching.
129
+ return false;
130
+ }
131
+ return true;
132
+ }
133
+
134
+ if (rule.valuePattern) {
135
+ return getCompiledPattern(rule.valuePattern).test(arg);
136
+ }
137
+
138
+ // No flags and no valuePattern — always matches (unusual but allowed)
139
+ return true;
140
+ }
141
+
142
+ // ── Wrapper unwrapping ───────────────────────────────────────────────────────
143
+
144
+ const WRAPPER_SKIP_FIRST_POSITIONAL = new Set(["timeout", "taskset"]);
145
+ const ENV_VALUE_FLAGS = new Set(["-u", "--unset", "-C", "--chdir"]);
146
+ const TIMEOUT_VALUE_FLAGS = new Set(["-s", "--signal", "-k", "--kill-after"]);
147
+
148
+ /**
149
+ * Given a wrapper segment, extract the wrapped program and its args.
150
+ * Returns undefined when no suitable argument is found.
151
+ */
152
+ function getWrappedProgramWithArgs(seg: {
153
+ program: string;
154
+ args: string[];
155
+ }): { program: string; args: string[] } | undefined {
156
+ const isEnv = seg.program === "env";
157
+ const isTimeout = seg.program === "timeout";
158
+ const skipFirst = WRAPPER_SKIP_FIRST_POSITIONAL.has(seg.program);
159
+ let skippedFirstPositional = false;
160
+ for (let i = 0; i < seg.args.length; i++) {
161
+ const arg = seg.args[i];
162
+ if (arg.startsWith("-")) {
163
+ if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++;
164
+ if (isTimeout && TIMEOUT_VALUE_FLAGS.has(arg)) i++;
165
+ continue;
166
+ }
167
+ if (isEnv && arg.includes("=")) continue;
168
+ if (skipFirst && !skippedFirstPositional) {
169
+ skippedFirstPositional = true;
170
+ continue;
171
+ }
172
+ return { program: arg, args: seg.args.slice(i + 1) };
173
+ }
174
+ return undefined;
175
+ }
176
+
177
+ /**
178
+ * Extract the first positional (non-flag) arg, skipping value-consuming flags.
179
+ */
180
+ function firstPositionalArg(
181
+ args: string[],
182
+ valueFlags?: Set<string>,
183
+ ): string | undefined {
184
+ for (let i = 0; i < args.length; i++) {
185
+ const arg = args[i];
186
+ if (arg.startsWith("-")) {
187
+ if (valueFlags?.has(arg)) i++;
188
+ continue;
189
+ }
190
+ return arg;
191
+ }
192
+ return undefined;
193
+ }
194
+
195
+ // ── Safe-file downgrade for rm ────────────────────────────────────────────────
196
+ // Bare filenames that `rm` is allowed to delete at Medium risk (instead of
197
+ // High) in sandboxed bash.
198
+ const RM_SAFE_BARE_FILES = new Set(["BOOTSTRAP.md", "UPDATES.md"]);
199
+
200
+ // Flags that don't affect rm safety — they don't enable recursive deletion or
201
+ // change which files are targeted.
202
+ const RM_BENIGN_FLAGS = new Set([
203
+ "-f",
204
+ "-i",
205
+ "-v",
206
+ "--force",
207
+ "--interactive",
208
+ "--verbose",
209
+ ]);
210
+
211
+ // ── Segment classification ───────────────────────────────────────────────────
212
+
213
+ /**
214
+ * Resolve a CommandRiskSpec through subcommand hierarchy.
215
+ *
216
+ * For commands like `git push --force`, walks the subcommand tree:
217
+ * git → git.subcommands.push
218
+ *
219
+ * Returns the resolved spec and the remaining args after subcommand resolution.
220
+ */
221
+ function resolveSubcommand(
222
+ spec: CommandRiskSpec,
223
+ args: string[],
224
+ ): { spec: CommandRiskSpec; remainingArgs: string[] } {
225
+ if (!spec.subcommands || args.length === 0) {
226
+ return { spec, remainingArgs: args };
227
+ }
228
+
229
+ const valueFlags = spec.globalValueFlags
230
+ ? new Set(spec.globalValueFlags)
231
+ : undefined;
232
+ const subcommandName = firstPositionalArg(args, valueFlags);
233
+
234
+ if (!subcommandName || !spec.subcommands[subcommandName]) {
235
+ return { spec, remainingArgs: args };
236
+ }
237
+
238
+ const subSpec = spec.subcommands[subcommandName];
239
+ const subIdx = args.indexOf(subcommandName);
240
+ const remainingArgs = args.slice(subIdx + 1);
241
+
242
+ // Recurse for nested subcommands (e.g., git stash drop, gh pr view)
243
+ return resolveSubcommand(subSpec, remainingArgs);
244
+ }
245
+
246
+ /**
247
+ * Classify a single command segment against user rules and the registry.
248
+ *
249
+ * @param toolName - Which tool is being invoked. Used for sandbox-specific
250
+ * downgrades (e.g. rm safe-file downgrade only applies in sandboxed "bash",
251
+ * not "host_bash").
252
+ */
253
+ export function classifySegment(
254
+ segment: CommandSegment,
255
+ userRules: UserRule[],
256
+ registry: Record<string, CommandRiskSpec>,
257
+ toolName: "bash" | "host_bash" = "bash",
258
+ ): { risk: Risk; reason: string; matchType: RiskAssessment["matchType"] } {
259
+ // 1. Check user rules first (highest priority)
260
+ // TODO: implement user rule matching with specificity ordering.
261
+ // For now, userRules is always empty so this is a no-op.
262
+ for (const rule of userRules) {
263
+ const re = getCompiledPattern(rule.pattern);
264
+ if (re.test(segment.command)) {
265
+ return { risk: rule.risk, reason: rule.label, matchType: "user_rule" };
266
+ }
267
+ }
268
+
269
+ // 2. Look up command in default registry
270
+ // Use Object.hasOwn to avoid prototype pollution — program names like
271
+ // "toString" or "hasOwnProperty" exist on Object.prototype and would
272
+ // return truthy for `registry[name]` even though they're not real entries.
273
+ let programName = segment.program;
274
+ let spec = Object.hasOwn(registry, programName)
275
+ ? registry[programName]
276
+ : undefined;
277
+
278
+ if (!spec) {
279
+ // Strip path prefix: /usr/bin/rm → rm
280
+ const bare = programName.split("/").pop();
281
+ if (bare) {
282
+ programName = bare;
283
+ spec = Object.hasOwn(registry, programName)
284
+ ? registry[programName]
285
+ : undefined;
286
+ }
287
+ }
288
+
289
+ if (!spec) {
290
+ return {
291
+ risk: "unknown",
292
+ reason: `Unknown command: ${segment.program}`,
293
+ matchType: "unknown",
294
+ };
295
+ }
296
+
297
+ // 3. Handle wrappers — unwrap and classify inner command (recursive)
298
+ // When a wrapper's first arg matches a nonExecFlags entry, the wrapper is
299
+ // in a non-exec mode (e.g. `command -v`, `env -0`). Skip unwrapping and
300
+ // fall through to arg/base risk evaluation.
301
+ if (spec.isWrapper) {
302
+ // nonExecFlags only checks args[0] — a flag in a later position won't
303
+ // suppress unwrapping. This is intentional: wrappers place their mode
304
+ // flag first (e.g. `command -v`, `timeout --help`).
305
+ const isNonExecMode =
306
+ spec.nonExecFlags &&
307
+ segment.args.length > 0 &&
308
+ spec.nonExecFlags.includes(segment.args[0]);
309
+
310
+ if (!isNonExecMode) {
311
+ const inner = getWrappedProgramWithArgs(segment);
312
+ if (inner) {
313
+ // Build a synthetic segment for the inner command
314
+ const innerSegment: CommandSegment = {
315
+ command: [inner.program, ...inner.args].join(" "),
316
+ program: inner.program,
317
+ args: inner.args,
318
+ operator: segment.operator,
319
+ };
320
+ const innerResult = classifySegment(
321
+ innerSegment,
322
+ userRules,
323
+ registry,
324
+ toolName,
325
+ );
326
+ return {
327
+ risk: maxRisk(spec.baseRisk as Risk, innerResult.risk),
328
+ reason:
329
+ innerResult.reason || `${programName} wrapping ${inner.program}`,
330
+ matchType: innerResult.matchType,
331
+ };
332
+ }
333
+ // Wrapper with no inner command (bare `sudo`, `env`)
334
+ return {
335
+ risk: spec.baseRisk,
336
+ reason: spec.reason || programName,
337
+ matchType: "registry",
338
+ };
339
+ }
340
+ // Non-exec mode: fall through to subcommand/arg rule evaluation
341
+ }
342
+
343
+ // 4. Subcommand resolution
344
+ const { spec: resolvedSpec, remainingArgs: _remainingArgs } =
345
+ resolveSubcommand(spec, segment.args);
346
+
347
+ // 5. Evaluate arg rules
348
+ //
349
+ // Arg rules can both escalate AND de-escalate from baseRisk.
350
+ //
351
+ // De-escalation is only safe when ALL non-flag args are covered by rules.
352
+ // If any arg goes unmatched, baseRisk is the floor — we can't assume an
353
+ // unknown arg is safe. Example: `rm /tmp/foo /etc/passwd` should stay high
354
+ // even though /tmp/foo matches the rm:tmp de-escalation rule, because
355
+ // /etc/passwd is unmatched.
356
+ //
357
+ // Escalation always applies — any matched rule that's higher than baseRisk
358
+ // raises the risk regardless of unmatched args.
359
+ let risk: Risk = resolvedSpec.baseRisk;
360
+ let reason = resolvedSpec.reason || `${segment.program} (default)`;
361
+
362
+ const argRules = resolvedSpec.argRules;
363
+ if (argRules && argRules.length > 0) {
364
+ let anyArgRuleMatched = false;
365
+ let hasUnmatchedNonFlagArg = false;
366
+ let argRuleMaxRisk: Risk = "low";
367
+ let argRuleReason = "";
368
+
369
+ const allArgs = segment.args;
370
+ for (let i = 0; i < allArgs.length; i++) {
371
+ const arg = allArgs[i];
372
+ let matched = false;
373
+ for (const rule of argRules) {
374
+ // Standard match: flag or positional against this arg
375
+ if (matchesArgRule(rule, arg)) {
376
+ if (
377
+ !anyArgRuleMatched ||
378
+ riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
379
+ ) {
380
+ argRuleMaxRisk = rule.risk;
381
+ argRuleReason = rule.reason;
382
+ }
383
+ anyArgRuleMatched = true;
384
+ matched = true;
385
+ break; // first match per arg wins
386
+ }
387
+ // Flag+value lookahead: if this arg is a flag listed in the rule and
388
+ // the rule has a valuePattern, check the NEXT arg against the pattern.
389
+ // This handles `curl -d @file` where `-d` is the flag and `@file` is
390
+ // the value in the next token.
391
+ if (
392
+ rule.flags &&
393
+ rule.valuePattern &&
394
+ rule.flags.includes(arg) &&
395
+ i + 1 < allArgs.length
396
+ ) {
397
+ const nextArg = allArgs[i + 1];
398
+ if (getCompiledPattern(rule.valuePattern).test(nextArg)) {
399
+ if (
400
+ !anyArgRuleMatched ||
401
+ riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
402
+ ) {
403
+ argRuleMaxRisk = rule.risk;
404
+ argRuleReason = rule.reason;
405
+ }
406
+ anyArgRuleMatched = true;
407
+ matched = true;
408
+ break;
409
+ }
410
+ }
411
+ }
412
+ // Track unmatched non-flag args. Flags (starting with -) are structural
413
+ // and don't need rule coverage for de-escalation safety.
414
+ if (!matched && !arg.startsWith("-")) {
415
+ hasUnmatchedNonFlagArg = true;
416
+ }
417
+ }
418
+
419
+ if (anyArgRuleMatched) {
420
+ if (riskOrd(argRuleMaxRisk) >= riskOrd(risk)) {
421
+ // Escalation: always apply (matched rule is >= baseRisk)
422
+ risk = argRuleMaxRisk;
423
+ reason = argRuleReason;
424
+ } else if (!hasUnmatchedNonFlagArg) {
425
+ // De-escalation: only safe when ALL non-flag args matched rules.
426
+ // Every arg is accounted for, so the lower risk is justified.
427
+ risk = argRuleMaxRisk;
428
+ reason = argRuleReason;
429
+ }
430
+ // Otherwise: some args matched low rules but other args went unmatched.
431
+ // Keep baseRisk as the floor — can't safely de-escalate.
432
+ }
433
+ }
434
+
435
+ // 6. Check for variable expansion in args (conservative escalation)
436
+ // Use max(computedRisk, baseRisk) as the floor for escalation so that
437
+ // de-escalated commands still escalate from at least baseRisk.
438
+ // Example: `curl http://localhost:$PORT` — arg rule de-escalates to low,
439
+ // but baseRisk=medium is the floor, so escalateOne(medium) → high.
440
+ if (segment.args.some((a) => a.includes("$"))) {
441
+ const escalationBase = maxRisk(risk, resolvedSpec.baseRisk);
442
+ const escalated = escalateOne(escalationBase);
443
+ if (riskOrd(escalated) > riskOrd(risk)) {
444
+ risk = escalated;
445
+ reason = `${segment.program} with variable expansion`;
446
+ }
447
+ }
448
+
449
+ // 7. rm safe-file downgrade (sandbox only)
450
+ // When rm targets a single known safe bare file (with only benign flags),
451
+ // downgrade to medium in sandboxed bash. host_bash keeps high because it has a
452
+ // global ask rule that would prompt medium-risk commands.
453
+ if (programName === "rm" && toolName === "bash" && risk === "high") {
454
+ // Strip benign flags (-f, -i, -v) and check if exactly one bare filename remains
455
+ const positionalArgs = segment.args.filter((a) => !a.startsWith("-"));
456
+ const flagArgs = segment.args.filter((a) => a.startsWith("-"));
457
+ const allFlagsBenign = flagArgs.every((f) => RM_BENIGN_FLAGS.has(f));
458
+
459
+ if (
460
+ positionalArgs.length === 1 &&
461
+ allFlagsBenign &&
462
+ !positionalArgs[0].includes("/") &&
463
+ RM_SAFE_BARE_FILES.has(positionalArgs[0])
464
+ ) {
465
+ risk = "medium";
466
+ reason = `rm of known safe file: ${positionalArgs[0]}`;
467
+ }
468
+ }
469
+
470
+ return { risk, reason, matchType: "registry" };
471
+ }
472
+
473
+ // ── Scope option generation ──────────────────────────────────────────────────
474
+
475
+ /**
476
+ * Generate scope options (narrowest to broadest) from a parsed command.
477
+ *
478
+ * Algorithm:
479
+ * 1. Exact command (all args literal)
480
+ * 2. Wildcard positionals right-to-left (one at a time)
481
+ * 3. Drop flags (keep command + subcommand)
482
+ * 4. Wildcard at subcommand level
483
+ * 5. Wildcard at command level
484
+ * 6. Deduplicate
485
+ *
486
+ * For commands with complexSyntax, only offer exact and command-level wildcard.
487
+ */
488
+ export function generateScopeOptions(
489
+ parsed: ParsedCommand,
490
+ registry: Record<string, CommandRiskSpec> = DEFAULT_COMMAND_REGISTRY,
491
+ ): ScopeOption[] {
492
+ if (parsed.segments.length === 0) return [];
493
+
494
+ const options: ScopeOption[] = [];
495
+ const seen = new Set<string>();
496
+
497
+ function addOption(pattern: string, label: string): void {
498
+ if (seen.has(pattern)) return;
499
+ seen.add(pattern);
500
+ options.push({ pattern, label });
501
+ }
502
+
503
+ // For multi-segment commands (pipelines), use the full command as exact match
504
+ // and individual segment programs for broader options
505
+ if (parsed.segments.length > 1) {
506
+ const fullCommand = parsed.segments.map((s) => s.command).join(" | ");
507
+ addOption(`^${escapeRegex(fullCommand)}$`, fullCommand);
508
+ // Add command-level wildcards for each unique program
509
+ const programs = new Set(parsed.segments.map((s) => s.program));
510
+ for (const prog of programs) {
511
+ addOption(`^${escapeRegex(prog)}\\b`, `${prog} *`);
512
+ }
513
+ return options;
514
+ }
515
+
516
+ // Single segment
517
+ const seg = parsed.segments[0];
518
+ const programName = seg.program;
519
+
520
+ // Check if command has complexSyntax
521
+ const spec = registry[programName];
522
+ const isComplex = spec?.complexSyntax === true;
523
+
524
+ // 1. Exact match
525
+ addOption(`^${escapeRegex(seg.command)}$`, seg.command);
526
+
527
+ if (isComplex) {
528
+ // For complex syntax, skip intermediate options
529
+ addOption(`^${escapeRegex(programName)}\\b`, `${programName} *`);
530
+ return options;
531
+ }
532
+
533
+ // Separate args into flags and positionals
534
+ const flags: string[] = [];
535
+ const positionals: string[] = [];
536
+ for (const arg of seg.args) {
537
+ if (arg.startsWith("-")) {
538
+ flags.push(arg);
539
+ } else {
540
+ positionals.push(arg);
541
+ }
542
+ }
543
+
544
+ // Detect subcommand
545
+ let subcommand: string | undefined;
546
+ if (spec?.subcommands && positionals.length > 0) {
547
+ const firstPos = positionals[0];
548
+ if (spec.subcommands[firstPos]) {
549
+ subcommand = firstPos;
550
+ }
551
+ }
552
+
553
+ // 2. Wildcard positionals right-to-left
554
+ if (positionals.length > 1) {
555
+ for (let drop = 1; drop < positionals.length; drop++) {
556
+ const kept = positionals.slice(0, positionals.length - drop);
557
+ const parts = [programName, ...flags, ...kept].filter(Boolean);
558
+ const pattern = `^${parts.map(escapeRegex).join("\\s+")}\\s+.*$`;
559
+ const label = [programName, ...flags, ...kept, "*"].join(" ");
560
+ addOption(pattern, label);
561
+ }
562
+ }
563
+
564
+ // 3. Drop flags (keep command + subcommand + wildcard)
565
+ if (flags.length > 0) {
566
+ const parts = subcommand ? [programName, subcommand] : [programName];
567
+ addOption(
568
+ `^${parts.map(escapeRegex).join("\\s+")}\\b`,
569
+ [...parts, "*"].join(" "),
570
+ );
571
+ }
572
+
573
+ // 4. Subcommand wildcard
574
+ if (subcommand) {
575
+ addOption(
576
+ `^${escapeRegex(programName)}\\s+${escapeRegex(subcommand)}\\b`,
577
+ `${programName} ${subcommand} *`,
578
+ );
579
+ }
580
+
581
+ // 5. Command-level wildcard
582
+ addOption(`^${escapeRegex(programName)}\\b`, `${programName} *`);
583
+
584
+ return options;
585
+ }
586
+
587
+ /** Escape a string for use in a regex. */
588
+ function escapeRegex(s: string): string {
589
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
590
+ }
591
+
592
+ // ── Main classifier ──────────────────────────────────────────────────────────
593
+
594
+ /**
595
+ * Bash risk classifier implementation.
596
+ *
597
+ * Primary classifier for bash/host_bash tools. checker.ts delegates to
598
+ * the singleton `bashRiskClassifier` instance for all bash command
599
+ * risk classification.
600
+ */
601
+ export class BashRiskClassifier implements RiskClassifier<BashClassifierInput> {
602
+ private readonly registry: Record<string, CommandRiskSpec>;
603
+ private readonly userRules: UserRule[];
604
+
605
+ constructor(
606
+ registry: Record<string, CommandRiskSpec> = DEFAULT_COMMAND_REGISTRY,
607
+ userRules: UserRule[] = [],
608
+ ) {
609
+ this.registry = registry;
610
+ this.userRules = userRules;
611
+ }
612
+
613
+ async classify(input: BashClassifierInput): Promise<RiskAssessment> {
614
+ const { command, toolName } = input;
615
+
616
+ if (!command.trim()) {
617
+ return {
618
+ riskLevel: "low",
619
+ reason: "Empty command",
620
+ scopeOptions: [],
621
+ matchType: "registry",
622
+ };
623
+ }
624
+
625
+ const parsed = await cachedParse(command);
626
+
627
+ let maxRiskLevel: Risk = "low";
628
+ let maxReason = "";
629
+ let matchType: RiskAssessment["matchType"] = "registry";
630
+
631
+ // Classify each segment
632
+ for (const segment of parsed.segments) {
633
+ const result = classifySegment(
634
+ segment,
635
+ this.userRules,
636
+ this.registry,
637
+ toolName,
638
+ );
639
+ if (riskOrd(result.risk) > riskOrd(maxRiskLevel)) {
640
+ maxRiskLevel = result.risk;
641
+ maxReason = result.reason;
642
+ matchType = result.matchType;
643
+ } else if (!maxReason && result.reason) {
644
+ // Capture reason from first segment even if it doesn't escalate
645
+ // (avoids empty reason for all-low commands like `ls`)
646
+ maxReason = result.reason;
647
+ matchType = result.matchType;
648
+ }
649
+ }
650
+
651
+ // No segments → opaque
652
+ if (parsed.segments.length === 0) {
653
+ maxRiskLevel = "high";
654
+ maxReason = "No parseable command segments";
655
+ matchType = "unknown";
656
+ }
657
+
658
+ // Dangerous patterns escalate to at least high
659
+ if (parsed.dangerousPatterns.length > 0) {
660
+ if (riskOrd("high") > riskOrd(maxRiskLevel)) {
661
+ maxRiskLevel = "high";
662
+ }
663
+ maxReason = parsed.dangerousPatterns[0].description;
664
+ }
665
+
666
+ // Opaque constructs escalation:
667
+ // - With dangerous patterns present → escalate to high
668
+ // - Without dangerous patterns → escalate to medium only
669
+ if (parsed.hasOpaqueConstructs) {
670
+ const opaqueTarget: Risk =
671
+ parsed.dangerousPatterns.length > 0 ? "high" : "medium";
672
+ if (riskOrd(opaqueTarget) > riskOrd(maxRiskLevel)) {
673
+ maxRiskLevel = opaqueTarget;
674
+ }
675
+ if (!maxReason) {
676
+ maxReason = "Command contains opaque constructs";
677
+ }
678
+ }
679
+
680
+ const scopeOptions = generateScopeOptions(parsed, this.registry);
681
+
682
+ const assessment: RiskAssessment = {
683
+ riskLevel: maxRiskLevel,
684
+ reason: maxReason,
685
+ scopeOptions,
686
+ matchType,
687
+ };
688
+
689
+ // Risk assessment analytics
690
+ const primaryProgram = parsed.segments[0]?.program ?? "(none)";
691
+ log.info(
692
+ {
693
+ command,
694
+ program: primaryProgram,
695
+ riskLevel: assessment.riskLevel,
696
+ reason: assessment.reason,
697
+ matchType: assessment.matchType,
698
+ },
699
+ "Risk assessment",
700
+ );
701
+
702
+ return assessment;
703
+ }
704
+ }
705
+
706
+ /** Singleton classifier instance with default registry. */
707
+ export const bashRiskClassifier = new BashRiskClassifier();