@vellumai/assistant 0.4.48 → 0.4.50

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 (423) hide show
  1. package/ARCHITECTURE.md +26 -35
  2. package/README.md +5 -26
  3. package/docs/architecture/integrations.md +45 -41
  4. package/docs/architecture/keychain-broker.md +3 -3
  5. package/docs/architecture/memory.md +180 -119
  6. package/docs/runbook-trusted-contacts.md +3 -8
  7. package/hook-templates/debug-prompt-logger/hook.json +1 -1
  8. package/hook-templates/debug-prompt-logger/run.sh +1 -3
  9. package/package.json +2 -2
  10. package/src/__tests__/actor-token-service.test.ts +0 -1
  11. package/src/__tests__/agent-loop.test.ts +3 -1
  12. package/src/__tests__/anthropic-provider.test.ts +249 -2
  13. package/src/__tests__/approval-cascade.test.ts +796 -0
  14. package/src/__tests__/approval-primitive.test.ts +0 -1
  15. package/src/__tests__/approval-routes-http.test.ts +4 -0
  16. package/src/__tests__/assistant-attachments.test.ts +12 -34
  17. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  18. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +76 -0
  19. package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -1
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  22. package/src/__tests__/channel-guardian.test.ts +0 -2
  23. package/src/__tests__/channel-readiness-routes.test.ts +15 -6
  24. package/src/__tests__/channel-readiness-service.test.ts +10 -9
  25. package/src/__tests__/checker.test.ts +13 -20
  26. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
  27. package/src/__tests__/computer-use-tools.test.ts +2 -19
  28. package/src/__tests__/config-schema.test.ts +1 -68
  29. package/src/__tests__/config-watcher.test.ts +0 -1
  30. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  31. package/src/__tests__/context-image-dimensions.test.ts +332 -0
  32. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  33. package/src/__tests__/context-token-estimator.test.ts +196 -13
  34. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  35. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  36. package/src/__tests__/conversation-routes-guardian-reply.test.ts +152 -0
  37. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -0
  38. package/src/__tests__/credential-metadata-store.test.ts +64 -73
  39. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  40. package/src/__tests__/credential-security-invariants.test.ts +13 -7
  41. package/src/__tests__/credential-vault-unit.test.ts +284 -49
  42. package/src/__tests__/credential-vault.test.ts +150 -16
  43. package/src/__tests__/credentials-cli.test.ts +71 -0
  44. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  45. package/src/__tests__/date-context.test.ts +93 -77
  46. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  47. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  48. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  49. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  50. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  51. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  52. package/src/__tests__/guardian-routing-invariants.test.ts +93 -1
  53. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  54. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -39
  55. package/src/__tests__/heartbeat-service.test.ts +0 -1
  56. package/src/__tests__/history-repair.test.ts +245 -0
  57. package/src/__tests__/host-cu-proxy.test.ts +791 -0
  58. package/src/__tests__/host-shell-tool.test.ts +27 -15
  59. package/src/__tests__/http-user-message-parity.test.ts +2 -0
  60. package/src/__tests__/ingress-url-consistency.test.ts +14 -21
  61. package/src/__tests__/integration-status.test.ts +32 -51
  62. package/src/__tests__/intent-routing.test.ts +0 -1
  63. package/src/__tests__/invite-redemption-service.test.ts +65 -1
  64. package/src/__tests__/invite-routes-http.test.ts +10 -9
  65. package/src/__tests__/keychain-broker-client.test.ts +14 -46
  66. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  67. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  68. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  69. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  70. package/src/__tests__/memory-regressions.test.ts +477 -2841
  71. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  72. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  73. package/src/__tests__/mime-builder.test.ts +28 -0
  74. package/src/__tests__/native-web-search.test.ts +1 -0
  75. package/src/__tests__/notification-routing-intent.test.ts +0 -1
  76. package/src/__tests__/oauth-cli.test.ts +941 -15
  77. package/src/__tests__/oauth-provider-profiles.test.ts +9 -9
  78. package/src/__tests__/oauth-scope-policy.test.ts +4 -6
  79. package/src/__tests__/oauth-store.test.ts +870 -0
  80. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  81. package/src/__tests__/provider-error-scenarios.test.ts +0 -1
  82. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -1
  83. package/src/__tests__/public-ingress-urls.test.ts +15 -21
  84. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  85. package/src/__tests__/recording-handler.test.ts +3 -4
  86. package/src/__tests__/registry.test.ts +2 -3
  87. package/src/__tests__/relay-server.test.ts +46 -1
  88. package/src/__tests__/runtime-events-sse.test.ts +55 -7
  89. package/src/__tests__/schedule-store.test.ts +0 -1
  90. package/src/__tests__/schedule-tools.test.ts +32 -0
  91. package/src/__tests__/scheduler-recurrence.test.ts +0 -1
  92. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  93. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  94. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  95. package/src/__tests__/secret-ingress-handler.test.ts +0 -1
  96. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  97. package/src/__tests__/secure-keys.test.ts +7 -2
  98. package/src/__tests__/send-endpoint-busy.test.ts +24 -6
  99. package/src/__tests__/sequence-store.test.ts +0 -1
  100. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  101. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  102. package/src/__tests__/session-agent-loop.test.ts +19 -15
  103. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  104. package/src/__tests__/session-error.test.ts +124 -2
  105. package/src/__tests__/session-history-web-search.test.ts +918 -0
  106. package/src/__tests__/session-init.benchmark.test.ts +4 -5
  107. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  108. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  109. package/src/__tests__/session-queue.test.ts +37 -27
  110. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  111. package/src/__tests__/session-slash-known.test.ts +1 -15
  112. package/src/__tests__/session-slash-queue.test.ts +1 -15
  113. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  114. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  115. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  116. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  117. package/src/__tests__/skill-include-graph.test.ts +66 -0
  118. package/src/__tests__/skill-load-feature-flag.test.ts +0 -1
  119. package/src/__tests__/skill-load-tool.test.ts +149 -1
  120. package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
  121. package/src/__tests__/skills-install-extract.test.ts +93 -0
  122. package/src/__tests__/skills-uninstall.test.ts +1 -1
  123. package/src/__tests__/skills.test.ts +3 -3
  124. package/src/__tests__/skillssh-registry.test.ts +451 -0
  125. package/src/__tests__/slack-channel-config.test.ts +67 -3
  126. package/src/__tests__/slack-share-routes.test.ts +17 -19
  127. package/src/__tests__/system-prompt.test.ts +0 -1
  128. package/src/__tests__/telegram-invite-adapter.test.ts +18 -22
  129. package/src/__tests__/terminal-tools.test.ts +4 -3
  130. package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
  131. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  132. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  133. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  134. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
  135. package/src/__tests__/tool-executor.test.ts +0 -1
  136. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  137. package/src/__tests__/trust-store-pattern-matches.test.ts +29 -0
  138. package/src/__tests__/trust-store.test.ts +7 -13
  139. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  140. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  141. package/src/__tests__/twilio-routes.test.ts +0 -16
  142. package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
  143. package/src/__tests__/voice-invite-redemption.test.ts +32 -1
  144. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  145. package/src/agent/ax-tree-compaction.test.ts +286 -0
  146. package/src/agent/loop.ts +104 -131
  147. package/src/approvals/AGENTS.md +1 -1
  148. package/src/approvals/guardian-request-resolvers.ts +14 -2
  149. package/src/bundler/compiler-tools.ts +66 -2
  150. package/src/calls/call-domain.ts +133 -6
  151. package/src/calls/call-store.ts +6 -0
  152. package/src/calls/relay-server.ts +52 -18
  153. package/src/calls/relay-setup-router.ts +17 -1
  154. package/src/calls/twilio-config.ts +3 -8
  155. package/src/calls/twilio-routes.ts +1 -2
  156. package/src/calls/types.ts +3 -1
  157. package/src/calls/voice-ingress-preflight.ts +1 -1
  158. package/src/cli/commands/browser-relay.ts +18 -12
  159. package/src/cli/commands/completions.ts +0 -3
  160. package/src/cli/commands/credentials.ts +101 -15
  161. package/src/cli/commands/doctor.ts +4 -3
  162. package/src/cli/commands/mcp.ts +46 -59
  163. package/src/cli/commands/memory.ts +16 -165
  164. package/src/cli/commands/oauth/apps.ts +284 -0
  165. package/src/cli/commands/oauth/connections.ts +633 -0
  166. package/src/cli/commands/oauth/index.ts +52 -0
  167. package/src/cli/commands/oauth/providers.ts +256 -0
  168. package/src/cli/commands/sessions.ts +5 -2
  169. package/src/cli/commands/skills.ts +177 -339
  170. package/src/cli/http-client.ts +0 -20
  171. package/src/cli/main-screen.tsx +2 -2
  172. package/src/cli/program.ts +6 -11
  173. package/src/cli/reference.ts +1 -3
  174. package/src/cli.ts +4 -10
  175. package/src/config/assistant-feature-flags.ts +0 -3
  176. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  177. package/src/config/bundled-skills/computer-use/SKILL.md +3 -6
  178. package/src/config/bundled-skills/computer-use/TOOLS.json +23 -5
  179. package/src/config/bundled-skills/computer-use/tools/{computer-use-request-control.ts → computer-use-observe.ts} +1 -5
  180. package/src/config/bundled-skills/google-calendar/calendar-client.ts +21 -16
  181. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -4
  182. package/src/config/bundled-skills/settings/SKILL.md +1 -1
  183. package/src/config/bundled-skills/settings/TOOLS.json +2 -8
  184. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +5 -33
  185. package/src/config/bundled-tool-registry.ts +2 -5
  186. package/src/config/env-registry.ts +14 -83
  187. package/src/config/env.ts +11 -50
  188. package/src/config/feature-flag-registry.json +16 -16
  189. package/src/config/loader.ts +0 -6
  190. package/src/config/schema.ts +4 -13
  191. package/src/config/schemas/memory-lifecycle.ts +0 -9
  192. package/src/config/schemas/memory-processing.ts +0 -180
  193. package/src/config/schemas/memory-retrieval.ts +32 -104
  194. package/src/config/schemas/memory.ts +0 -10
  195. package/src/config/skills.ts +21 -2
  196. package/src/config/types.ts +0 -4
  197. package/src/context/image-dimensions.ts +229 -0
  198. package/src/context/token-estimator.ts +75 -12
  199. package/src/context/window-manager.ts +53 -11
  200. package/src/daemon/assistant-attachments.ts +1 -13
  201. package/src/daemon/config-watcher.ts +61 -3
  202. package/src/daemon/daemon-control.ts +1 -1
  203. package/src/daemon/date-context.ts +114 -31
  204. package/src/daemon/handlers/config-ingress.ts +8 -33
  205. package/src/daemon/handlers/config-slack-channel.ts +49 -46
  206. package/src/daemon/handlers/config-telegram.ts +32 -16
  207. package/src/daemon/handlers/sessions.ts +27 -36
  208. package/src/daemon/handlers/shared.ts +0 -130
  209. package/src/daemon/handlers/skills.ts +20 -1
  210. package/src/daemon/history-repair.ts +72 -8
  211. package/src/daemon/host-cu-proxy.ts +430 -0
  212. package/src/daemon/lifecycle.ts +67 -71
  213. package/src/daemon/mcp-reload-service.ts +2 -2
  214. package/src/daemon/message-protocol.ts +3 -0
  215. package/src/daemon/message-types/computer-use.ts +1 -129
  216. package/src/daemon/message-types/host-cu.ts +19 -0
  217. package/src/daemon/message-types/memory.ts +4 -16
  218. package/src/daemon/message-types/messages.ts +4 -0
  219. package/src/daemon/message-types/sessions.ts +4 -0
  220. package/src/daemon/server.ts +25 -21
  221. package/src/daemon/session-agent-loop-handlers.ts +40 -0
  222. package/src/daemon/session-agent-loop.ts +334 -48
  223. package/src/daemon/session-attachments.ts +1 -2
  224. package/src/daemon/session-error.ts +89 -6
  225. package/src/daemon/session-history.ts +17 -7
  226. package/src/daemon/session-media-retry.ts +6 -2
  227. package/src/daemon/session-memory.ts +69 -149
  228. package/src/daemon/session-process.ts +10 -1
  229. package/src/daemon/session-runtime-assembly.ts +49 -19
  230. package/src/daemon/session-slash.ts +1 -1
  231. package/src/daemon/session-surfaces.ts +43 -28
  232. package/src/daemon/session-tool-setup.ts +9 -10
  233. package/src/daemon/session.ts +150 -17
  234. package/src/daemon/tool-side-effects.ts +2 -8
  235. package/src/daemon/watch-handler.ts +2 -2
  236. package/src/events/tool-metrics-listener.ts +2 -2
  237. package/src/hooks/manager.ts +1 -4
  238. package/src/inbound/public-ingress-urls.ts +7 -7
  239. package/src/instrument.ts +61 -1
  240. package/src/logfire.ts +16 -5
  241. package/src/memory/admin.ts +2 -191
  242. package/src/memory/canonical-guardian-store.ts +38 -2
  243. package/src/memory/conversation-crud.ts +0 -33
  244. package/src/memory/conversation-key-store.ts +21 -0
  245. package/src/memory/conversation-queries.ts +22 -3
  246. package/src/memory/db-init.ts +32 -0
  247. package/src/memory/embedding-backend.ts +84 -8
  248. package/src/memory/embedding-types.ts +9 -1
  249. package/src/memory/indexer.ts +7 -46
  250. package/src/memory/items-extractor.ts +274 -76
  251. package/src/memory/job-handlers/backfill.ts +2 -127
  252. package/src/memory/job-handlers/cleanup.ts +2 -16
  253. package/src/memory/job-handlers/extraction.ts +2 -138
  254. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  255. package/src/memory/job-handlers/summarization.ts +3 -148
  256. package/src/memory/job-utils.ts +21 -59
  257. package/src/memory/jobs-store.ts +1 -159
  258. package/src/memory/jobs-worker.ts +9 -52
  259. package/src/memory/migrations/104-core-indexes.ts +3 -3
  260. package/src/memory/migrations/149-oauth-tables.ts +62 -0
  261. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  262. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  263. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  264. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  265. package/src/memory/migrations/154-drop-fts.ts +20 -0
  266. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  267. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  268. package/src/memory/migrations/index.ts +8 -0
  269. package/src/memory/qdrant-client.ts +148 -51
  270. package/src/memory/raw-query.ts +1 -1
  271. package/src/memory/retriever.test.ts +294 -273
  272. package/src/memory/retriever.ts +421 -645
  273. package/src/memory/schema/calls.ts +2 -0
  274. package/src/memory/schema/index.ts +1 -0
  275. package/src/memory/schema/memory-core.ts +3 -48
  276. package/src/memory/schema/oauth.ts +67 -0
  277. package/src/memory/search/formatting.ts +263 -176
  278. package/src/memory/search/lexical.ts +1 -254
  279. package/src/memory/search/ranking.ts +0 -455
  280. package/src/memory/search/semantic.ts +100 -14
  281. package/src/memory/search/staleness.ts +47 -0
  282. package/src/memory/search/tier-classifier.ts +21 -0
  283. package/src/memory/search/types.ts +15 -77
  284. package/src/memory/task-memory-cleanup.ts +4 -6
  285. package/src/messaging/provider.ts +4 -4
  286. package/src/messaging/providers/gmail/client.ts +82 -2
  287. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  288. package/src/messaging/providers/gmail/people-client.ts +10 -10
  289. package/src/messaging/providers/telegram-bot/adapter.ts +17 -17
  290. package/src/messaging/providers/whatsapp/adapter.ts +11 -8
  291. package/src/messaging/registry.ts +2 -32
  292. package/src/notifications/copy-composer.ts +0 -5
  293. package/src/notifications/signal.ts +4 -5
  294. package/src/oauth/byo-connection.test.ts +133 -25
  295. package/src/oauth/byo-connection.ts +22 -6
  296. package/src/oauth/connect-orchestrator.ts +113 -57
  297. package/src/oauth/connect-types.ts +17 -23
  298. package/src/oauth/connection-resolver.ts +35 -11
  299. package/src/oauth/connection.ts +1 -1
  300. package/src/oauth/manual-token-connection.ts +104 -0
  301. package/src/oauth/oauth-store.ts +582 -0
  302. package/src/oauth/platform-connection.test.ts +29 -0
  303. package/src/oauth/platform-connection.ts +6 -5
  304. package/src/oauth/provider-behaviors.ts +124 -0
  305. package/src/oauth/scope-policy.ts +9 -2
  306. package/src/oauth/seed-providers.ts +167 -0
  307. package/src/oauth/token-persistence.ts +81 -77
  308. package/src/permissions/checker.ts +3 -3
  309. package/src/permissions/defaults.ts +1 -1
  310. package/src/permissions/prompter.ts +10 -1
  311. package/src/permissions/trust-store.ts +36 -1
  312. package/src/playbooks/playbook-compiler.ts +1 -1
  313. package/src/prompts/__tests__/build-cli-reference-section.test.ts +3 -1
  314. package/src/prompts/system-prompt.ts +46 -42
  315. package/src/providers/anthropic/client.ts +59 -20
  316. package/src/providers/retry.ts +1 -27
  317. package/src/providers/types.ts +7 -1
  318. package/src/runtime/AGENTS.md +9 -0
  319. package/src/runtime/auth/route-policy.ts +6 -6
  320. package/src/runtime/channel-reply-delivery.ts +0 -40
  321. package/src/runtime/gateway-client.ts +0 -7
  322. package/src/runtime/guardian-reply-router.ts +24 -22
  323. package/src/runtime/http-server.ts +10 -8
  324. package/src/runtime/http-types.ts +2 -2
  325. package/src/runtime/invite-redemption-service.ts +19 -1
  326. package/src/runtime/invite-service.ts +25 -0
  327. package/src/runtime/middleware/twilio-validation.ts +1 -11
  328. package/src/runtime/pending-interactions.ts +14 -12
  329. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  330. package/src/runtime/routes/channel-delivery-routes.ts +0 -1
  331. package/src/runtime/routes/conversation-routes.ts +81 -19
  332. package/src/runtime/routes/events-routes.ts +21 -11
  333. package/src/runtime/routes/host-cu-routes.ts +97 -0
  334. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  335. package/src/runtime/routes/inbound-stages/background-dispatch.ts +12 -111
  336. package/src/runtime/routes/integrations/slack/share.ts +6 -7
  337. package/src/runtime/routes/log-export-routes.ts +126 -8
  338. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  339. package/src/runtime/routes/memory-item-routes.ts +503 -0
  340. package/src/runtime/routes/session-management-routes.ts +3 -3
  341. package/src/runtime/routes/settings-routes.ts +55 -48
  342. package/src/runtime/routes/surface-action-routes.ts +1 -1
  343. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  344. package/src/runtime/routes/watch-routes.ts +128 -0
  345. package/src/runtime/routes/workspace-routes.ts +2 -1
  346. package/src/schedule/integration-status.ts +10 -9
  347. package/src/security/credential-key.ts +0 -156
  348. package/src/security/keychain-broker-client.ts +22 -10
  349. package/src/security/oauth2.ts +1 -1
  350. package/src/security/secure-keys.ts +25 -3
  351. package/src/security/token-manager.ts +137 -64
  352. package/src/skills/catalog-install.ts +414 -0
  353. package/src/skills/include-graph.ts +32 -0
  354. package/src/skills/skillssh-registry.ts +503 -0
  355. package/src/telegram/bot-username.ts +2 -3
  356. package/src/tools/assets/search.ts +5 -1
  357. package/src/tools/browser/network-recorder.ts +1 -1
  358. package/src/tools/browser/network-recording-types.ts +1 -1
  359. package/src/tools/computer-use/definitions.ts +36 -11
  360. package/src/tools/computer-use/registry.ts +5 -6
  361. package/src/tools/credentials/broker.ts +1 -2
  362. package/src/tools/credentials/metadata-store.ts +17 -121
  363. package/src/tools/credentials/vault.ts +92 -167
  364. package/src/tools/memory/definitions.ts +4 -13
  365. package/src/tools/memory/handlers.test.ts +83 -103
  366. package/src/tools/memory/handlers.ts +50 -85
  367. package/src/tools/registry.ts +2 -7
  368. package/src/tools/schedule/create.ts +8 -1
  369. package/src/tools/schedule/update.ts +8 -1
  370. package/src/tools/skills/load.ts +85 -3
  371. package/src/tools/watch/watch-state.ts +0 -12
  372. package/src/util/logger.ts +7 -41
  373. package/src/util/platform.ts +9 -28
  374. package/src/watcher/providers/google-calendar.ts +2 -1
  375. package/src/__tests__/clarification-resolver.test.ts +0 -193
  376. package/src/__tests__/computer-use-session-compaction.test.ts +0 -143
  377. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -322
  378. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -166
  379. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -78
  380. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -105
  381. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -249
  382. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  383. package/src/__tests__/conflict-policy.test.ts +0 -269
  384. package/src/__tests__/conflict-store.test.ts +0 -372
  385. package/src/__tests__/contradiction-checker.test.ts +0 -361
  386. package/src/__tests__/entity-extractor.test.ts +0 -211
  387. package/src/__tests__/entity-search.test.ts +0 -1117
  388. package/src/__tests__/profile-compiler.test.ts +0 -392
  389. package/src/__tests__/ride-shotgun-handler.test.ts +0 -452
  390. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  391. package/src/__tests__/session-profile-injection.test.ts +0 -557
  392. package/src/cli/commands/dev.ts +0 -129
  393. package/src/cli/commands/map.ts +0 -391
  394. package/src/cli/commands/oauth.ts +0 -77
  395. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  396. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  397. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  398. package/src/daemon/computer-use-session.ts +0 -1026
  399. package/src/daemon/ride-shotgun-handler.ts +0 -569
  400. package/src/daemon/session-conflict-gate.ts +0 -167
  401. package/src/daemon/session-dynamic-profile.ts +0 -77
  402. package/src/memory/clarification-resolver.ts +0 -417
  403. package/src/memory/conflict-intent.ts +0 -205
  404. package/src/memory/conflict-policy.ts +0 -127
  405. package/src/memory/conflict-store.ts +0 -410
  406. package/src/memory/contradiction-checker.ts +0 -508
  407. package/src/memory/entity-extractor.ts +0 -535
  408. package/src/memory/format-recall.ts +0 -47
  409. package/src/memory/fts-reconciler.ts +0 -165
  410. package/src/memory/job-handlers/conflict.ts +0 -200
  411. package/src/memory/profile-compiler.ts +0 -195
  412. package/src/memory/recall-cache.ts +0 -117
  413. package/src/memory/search/entity.ts +0 -535
  414. package/src/memory/search/query-expansion.test.ts +0 -70
  415. package/src/memory/search/query-expansion.ts +0 -118
  416. package/src/oauth/provider-base-urls.ts +0 -21
  417. package/src/oauth/provider-profiles.ts +0 -192
  418. package/src/prompts/computer-use-prompt.ts +0 -98
  419. package/src/runtime/routes/computer-use-routes.ts +0 -641
  420. package/src/runtime/routes/mcp-routes.ts +0 -20
  421. package/src/runtime/telegram-streaming-delivery.test.ts +0 -729
  422. package/src/runtime/telegram-streaming-delivery.ts +0 -393
  423. package/src/tools/computer-use/request-computer-control.ts +0 -56
@@ -21,7 +21,6 @@ mock.module("../util/logger.js", () => ({
21
21
  new Proxy({} as Record<string, unknown>, {
22
22
  get: () => () => {},
23
23
  }),
24
- isDebug: () => false,
25
24
  truncateForLog: (value: string) => value,
26
25
  }));
27
26
 
@@ -118,6 +118,8 @@ function makeIdleSession(opts?: {
118
118
  updateClient: () => {},
119
119
  setHostBashProxy: () => {},
120
120
  setHostFileProxy: () => {},
121
+ setHostCuProxy: () => {},
122
+ addPreactivatedSkillId: () => {},
121
123
  enqueueMessage: () => ({ queued: false, requestId: "noop" }),
122
124
  hasAnyPendingConfirmation: () => false,
123
125
  runAgentLoop: async (
@@ -181,6 +183,8 @@ function makeConfirmationEmittingSession(opts?: {
181
183
  updateClient: () => {},
182
184
  setHostBashProxy: () => {},
183
185
  setHostFileProxy: () => {},
186
+ setHostCuProxy: () => {},
187
+ addPreactivatedSkillId: () => {},
184
188
  enqueueMessage: () => ({ queued: false, requestId: "noop" }),
185
189
  hasAnyPendingConfirmation: () => false,
186
190
  runAgentLoop: async (
@@ -10,7 +10,6 @@ import {
10
10
  estimateBase64Bytes,
11
11
  inferMimeType,
12
12
  MAX_ASSISTANT_ATTACHMENT_BYTES,
13
- MAX_ASSISTANT_ATTACHMENTS,
14
13
  validateDrafts,
15
14
  } from "../daemon/assistant-attachments.js";
16
15
 
@@ -163,33 +162,27 @@ describe("validateDrafts", () => {
163
162
  expect(result.warnings[0]).toContain("exceeds");
164
163
  });
165
164
 
166
- test("truncates beyond max count", () => {
167
- const drafts = Array.from(
168
- { length: MAX_ASSISTANT_ATTACHMENTS + 2 },
169
- (_, i) => makeDraft({ filename: `file-${i}.txt` }),
165
+ test("accepts many drafts without count limit", () => {
166
+ const drafts = Array.from({ length: 20 }, (_, i) =>
167
+ makeDraft({ filename: `file-${i}.txt` }),
170
168
  );
171
169
  const result = validateDrafts(drafts);
172
- expect(result.accepted).toHaveLength(MAX_ASSISTANT_ATTACHMENTS);
173
- expect(result.warnings).toHaveLength(2);
174
- expect(result.warnings[0]).toContain(
175
- `file-${MAX_ASSISTANT_ATTACHMENTS}.txt`,
176
- );
177
- expect(result.warnings[0]).toContain("exceeded maximum");
170
+ expect(result.accepted).toHaveLength(20);
171
+ expect(result.warnings).toHaveLength(0);
178
172
  });
179
173
 
180
- test("rejects oversized before applying count cap", () => {
174
+ test("rejects oversized while accepting all valid drafts", () => {
181
175
  const drafts = [
182
176
  makeDraft({
183
177
  filename: "big.bin",
184
178
  sizeBytes: MAX_ASSISTANT_ATTACHMENT_BYTES + 1,
185
179
  }),
186
- ...Array.from({ length: MAX_ASSISTANT_ATTACHMENTS }, (_, i) =>
180
+ ...Array.from({ length: 10 }, (_, i) =>
187
181
  makeDraft({ filename: `ok-${i}.txt` }),
188
182
  ),
189
183
  ];
190
184
  const result = validateDrafts(drafts);
191
- // big.bin rejected for size; all MAX_ASSISTANT_ATTACHMENTS ok files accepted
192
- expect(result.accepted).toHaveLength(MAX_ASSISTANT_ATTACHMENTS);
185
+ expect(result.accepted).toHaveLength(10);
193
186
  expect(result.warnings).toHaveLength(1);
194
187
  expect(result.warnings[0]).toContain("big.bin");
195
188
  });
@@ -431,11 +424,8 @@ describe("contentBlocksToDrafts", () => {
431
424
  // ---------------------------------------------------------------------------
432
425
 
433
426
  describe("validateDrafts with reversed tool drafts", () => {
434
- test("most recent tool screenshots win the attachment cap when reversed before validation", () => {
435
- // Simulate a browser session producing many screenshots chronologically.
436
- // After reversing, the most recent (highest index) should appear first
437
- // and win the MAX_ASSISTANT_ATTACHMENTS cap.
438
- const totalScreenshots = MAX_ASSISTANT_ATTACHMENTS + 3;
427
+ test("all tool screenshots accepted after reversing", () => {
428
+ const totalScreenshots = 8;
439
429
  const toolDrafts = Array.from({ length: totalScreenshots }, (_, i) =>
440
430
  makeDraft({
441
431
  sourceType: "tool_block",
@@ -446,23 +436,11 @@ describe("validateDrafts with reversed tool drafts", () => {
446
436
  }),
447
437
  );
448
438
 
449
- // Reverse to prioritize most recent
450
439
  toolDrafts.reverse();
451
440
 
452
441
  const result = validateDrafts(toolDrafts);
453
- expect(result.accepted).toHaveLength(MAX_ASSISTANT_ATTACHMENTS);
454
-
455
- // The accepted drafts should be the most recent screenshots (highest step numbers)
456
- const acceptedFilenames = result.accepted.map((d) => d.filename);
457
- for (let i = 0; i < MAX_ASSISTANT_ATTACHMENTS; i++) {
458
- expect(acceptedFilenames[i]).toBe(
459
- `screenshot-step-${totalScreenshots - 1 - i}.png`,
460
- );
461
- }
462
-
463
- // The oldest screenshots should be dropped
464
- expect(result.warnings).toHaveLength(3);
465
- expect(result.warnings[0]).toContain("screenshot-step-2.png");
442
+ expect(result.accepted).toHaveLength(totalScreenshots);
443
+ expect(result.warnings).toHaveLength(0);
466
444
  });
467
445
  });
468
446
 
@@ -170,29 +170,6 @@ describe("assistant feature flag guard", () => {
170
170
  // Test: registry entries have required fields
171
171
  // ---------------------------------------------------------------------------
172
172
 
173
- // ---------------------------------------------------------------------------
174
- // Test: bundled registry copy stays in sync with canonical meta/ copy
175
- // ---------------------------------------------------------------------------
176
-
177
- test("bundled assistant/src/config/feature-flag-registry.json matches canonical meta/ copy", () => {
178
- const canonicalPath = getRegistryPath();
179
- const bundledPath = join(
180
- process.cwd(),
181
- "src",
182
- "config",
183
- "feature-flag-registry.json",
184
- );
185
-
186
- const canonical = JSON.parse(readFileSync(canonicalPath, "utf-8"));
187
- const bundled = JSON.parse(readFileSync(bundledPath, "utf-8"));
188
-
189
- expect(bundled).toEqual(canonical);
190
- });
191
-
192
- // ---------------------------------------------------------------------------
193
- // Test: registry entries have required fields
194
- // ---------------------------------------------------------------------------
195
-
196
173
  test("all assistant-scope entries in the unified registry have required fields", () => {
197
174
  const registry = loadRegistry();
198
175
  const assistantFlags = registry.flags.filter(
@@ -10,6 +10,11 @@
10
10
  * `isAssistantFeatureFlagEnabled('<key>', ...)` in production code must be
11
11
  * declared in the unified registry. This keeps flag usage declarative while
12
12
  * allowing skills to exist without corresponding feature flags.
13
+ *
14
+ * 3. Indirect key coverage: all `feature_flags.<id>.enabled` string literals
15
+ * anywhere in production code (maps, constants, variables, etc.) must be
16
+ * declared in the unified registry. This catches indirect key patterns that
17
+ * Guard 2 would miss, such as flag keys stored in lookup maps or constants.
13
18
  */
14
19
 
15
20
  import { execSync } from "node:child_process";
@@ -197,3 +202,74 @@ describe("assistant feature flag declaration coverage guard", () => {
197
202
  }
198
203
  });
199
204
  });
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // Guard 3: Indirect key coverage — flag key literals anywhere in production code
208
+ // ---------------------------------------------------------------------------
209
+
210
+ describe("assistant feature flag indirect key coverage guard", () => {
211
+ test("all feature_flags.<id>.enabled string literals in production code are declared in the unified registry", () => {
212
+ const repoRoot = getRepoRoot();
213
+
214
+ // Load the unified registry and extract all declared keys (any scope)
215
+ const registry = loadRegistry();
216
+ const declaredKeys = new Set(registry.flags.map((f) => f.key));
217
+
218
+ // Search for any string literal matching the canonical key pattern
219
+ // in production .ts files under assistant/src/ and gateway/src/.
220
+ // This catches keys in maps, constants, variables, or any other
221
+ // indirect patterns that Guard 2 would miss.
222
+ let grepOutput = "";
223
+ try {
224
+ grepOutput = execSync(
225
+ `git grep -nE "feature_flags\\.[a-z0-9_-]+\\.enabled\\b" -- 'assistant/src/**/*.ts' 'gateway/src/**/*.ts'`,
226
+ { encoding: "utf-8", cwd: repoRoot },
227
+ ).trim();
228
+ } catch (err) {
229
+ // Exit code 1 means no matches — happy path
230
+ if ((err as { status?: number }).status === 1) {
231
+ return;
232
+ }
233
+ throw err;
234
+ }
235
+
236
+ const keyPattern = /feature_flags\.[a-z0-9_-]+\.enabled\b/g;
237
+ const undeclared: string[] = [];
238
+
239
+ for (const line of grepOutput.split("\n")) {
240
+ if (!line) continue;
241
+
242
+ // Format: "file:line:content"
243
+ const colonIdx = line.indexOf(":");
244
+ if (colonIdx === -1) continue;
245
+ const filePath = line.slice(0, colonIdx);
246
+
247
+ // Skip test files
248
+ if (isTestFile(filePath)) continue;
249
+
250
+ // Extract all key occurrences from this line
251
+ const content = line.slice(colonIdx + 1);
252
+ for (const match of content.matchAll(keyPattern)) {
253
+ const key = match[0];
254
+ if (!declaredKeys.has(key)) {
255
+ undeclared.push(`${filePath}: ${key}`);
256
+ }
257
+ }
258
+ }
259
+
260
+ if (undeclared.length > 0) {
261
+ const message = [
262
+ "Found feature_flags.<id>.enabled string literals in production code that are NOT declared in the unified registry.",
263
+ "This catches indirect flag key usage (maps, constants, variables) that the direct-call guard misses.",
264
+ `Registry: meta/feature-flags/feature-flag-registry.json`,
265
+ "",
266
+ "Undeclared keys:",
267
+ ...undeclared.map((k) => ` - ${k}`),
268
+ "",
269
+ "To fix: add the missing key(s) to the unified registry, or remove the stale reference.",
270
+ ].join("\n");
271
+
272
+ expect(undeclared, message).toEqual([]);
273
+ }
274
+ });
275
+ });
@@ -75,7 +75,6 @@ mock.module("../util/logger.js", () => ({
75
75
  ...realLogger,
76
76
  getLogger: () => noopLogger,
77
77
  getCliLogger: () => noopLogger,
78
- isDebug: () => false,
79
78
  truncateForLog: (v: string) => v,
80
79
  initLogger: () => {},
81
80
  pruneOldLogFiles: () => 0,
@@ -55,10 +55,10 @@ describe("browser skill cutover — startup tool payload", () => {
55
55
  const definitions = getAllToolDefinitions();
56
56
  const serialized = JSON.stringify(definitions);
57
57
  // Startup payload is ~22 000 chars without browser tools.
58
- // Floor at 15 000 catches accidental wholesale removal; ceiling at 35 000
58
+ // Floor at 14 000 catches accidental wholesale removal; ceiling at 35 000
59
59
  // gives headroom while still catching browser tool leakage
60
60
  // (~4 640 chars would push it past the ceiling).
61
- expect(serialized.length).toBeGreaterThan(15_000);
61
+ expect(serialized.length).toBeGreaterThan(14_000);
62
62
  expect(serialized.length).toBeLessThan(35_000);
63
63
  });
64
64
 
@@ -26,11 +26,13 @@ mock.module("../util/logger.js", () => ({
26
26
  import {
27
27
  createCanonicalGuardianDelivery,
28
28
  createCanonicalGuardianRequest,
29
+ expireAllPendingCanonicalRequests,
29
30
  getCanonicalGuardianRequest,
30
31
  listCanonicalGuardianDeliveries,
31
32
  listCanonicalGuardianRequests,
32
33
  listPendingCanonicalGuardianRequestsByDestinationChat,
33
34
  listPendingCanonicalGuardianRequestsByDestinationConversation,
35
+ listPendingRequestsByConversationScope,
34
36
  resolveCanonicalGuardianRequest,
35
37
  updateCanonicalGuardianDelivery,
36
38
  updateCanonicalGuardianRequest,
@@ -717,4 +719,97 @@ describe("canonical-guardian-store", () => {
717
719
  );
718
720
  expect(pending).toHaveLength(0);
719
721
  });
722
+
723
+ // ── listPendingRequestsByConversationScope expiry filtering ─────────
724
+
725
+ test("listPendingRequestsByConversationScope excludes expired requests", () => {
726
+ // Create a pending request that has already expired
727
+ createCanonicalGuardianRequest({
728
+ kind: "tool_approval",
729
+ sourceType: "desktop",
730
+ conversationId: "conv-scope-1",
731
+ guardianPrincipalId: TEST_PRINCIPAL,
732
+ expiresAt: new Date(Date.now() - 10_000).toISOString(),
733
+ });
734
+
735
+ // Create a pending request that has not expired
736
+ const unexpired = createCanonicalGuardianRequest({
737
+ kind: "tool_approval",
738
+ sourceType: "desktop",
739
+ conversationId: "conv-scope-1",
740
+ guardianPrincipalId: TEST_PRINCIPAL,
741
+ expiresAt: new Date(Date.now() + 60_000).toISOString(),
742
+ });
743
+
744
+ const results = listPendingRequestsByConversationScope("conv-scope-1");
745
+ expect(results).toHaveLength(1);
746
+ expect(results[0].id).toBe(unexpired.id);
747
+ });
748
+
749
+ test("listPendingRequestsByConversationScope includes requests with no expiresAt", () => {
750
+ const noExpiry = createCanonicalGuardianRequest({
751
+ kind: "tool_approval",
752
+ sourceType: "desktop",
753
+ conversationId: "conv-scope-2",
754
+ guardianPrincipalId: TEST_PRINCIPAL,
755
+ });
756
+
757
+ const results = listPendingRequestsByConversationScope("conv-scope-2");
758
+ expect(results).toHaveLength(1);
759
+ expect(results[0].id).toBe(noExpiry.id);
760
+ });
761
+
762
+ // ── expireAllPendingCanonicalRequests ───────────────────────────────
763
+
764
+ test("expireAllPendingCanonicalRequests transitions all pending to expired", () => {
765
+ const req1 = createCanonicalGuardianRequest({
766
+ kind: "tool_approval",
767
+ sourceType: "desktop",
768
+ conversationId: "conv-bulk-1",
769
+ guardianPrincipalId: TEST_PRINCIPAL,
770
+ expiresAt: new Date(Date.now() + 60_000).toISOString(),
771
+ });
772
+ const req2 = createCanonicalGuardianRequest({
773
+ kind: "tool_approval",
774
+ sourceType: "channel",
775
+ conversationId: "conv-bulk-2",
776
+ guardianPrincipalId: TEST_PRINCIPAL,
777
+ expiresAt: new Date(Date.now() + 60_000).toISOString(),
778
+ });
779
+
780
+ const count = expireAllPendingCanonicalRequests();
781
+ expect(count).toBe(2);
782
+
783
+ expect(getCanonicalGuardianRequest(req1.id)!.status).toBe("expired");
784
+ expect(getCanonicalGuardianRequest(req2.id)!.status).toBe("expired");
785
+ });
786
+
787
+ test("expireAllPendingCanonicalRequests does not affect already-resolved requests", () => {
788
+ const approved = createCanonicalGuardianRequest({
789
+ kind: "tool_approval",
790
+ sourceType: "desktop",
791
+ conversationId: "conv-bulk-3",
792
+ guardianPrincipalId: TEST_PRINCIPAL,
793
+ });
794
+ updateCanonicalGuardianRequest(approved.id, { status: "approved" });
795
+
796
+ const denied = createCanonicalGuardianRequest({
797
+ kind: "tool_approval",
798
+ sourceType: "desktop",
799
+ conversationId: "conv-bulk-3",
800
+ guardianPrincipalId: TEST_PRINCIPAL,
801
+ });
802
+ updateCanonicalGuardianRequest(denied.id, { status: "denied" });
803
+
804
+ const count = expireAllPendingCanonicalRequests();
805
+ expect(count).toBe(0);
806
+
807
+ expect(getCanonicalGuardianRequest(approved.id)!.status).toBe("approved");
808
+ expect(getCanonicalGuardianRequest(denied.id)!.status).toBe("denied");
809
+ });
810
+
811
+ test("expireAllPendingCanonicalRequests returns 0 when no pending requests exist", () => {
812
+ const count = expireAllPendingCanonicalRequests();
813
+ expect(count).toBe(0);
814
+ });
720
815
  });
@@ -1305,8 +1305,6 @@ function createMockCtx(): {
1305
1305
  let captured: ChannelVerificationSessionResponse | null = null;
1306
1306
  const ctx = {
1307
1307
  sessions: new Map(),
1308
- cuSessions: new Map(),
1309
- cuObservationParseSequence: new Map(),
1310
1308
  sharedRequestTimestamps: [],
1311
1309
  debounceTimers: {
1312
1310
  schedule: () => {},
@@ -11,7 +11,6 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
11
11
  // Mocks — must be set up before importing the service
12
12
  // ---------------------------------------------------------------------------
13
13
 
14
- let mockTwilioPhoneNumberEnv: string | undefined;
15
14
  let mockRawConfig: Record<string, unknown> | undefined;
16
15
  let mockSecureKeys: Record<string, string>;
17
16
  let mockHasTwilioCredentials: boolean;
@@ -23,16 +22,27 @@ mock.module("../calls/twilio-rest.js", () => ({
23
22
  getTollFreeVerificationStatus: async () => null,
24
23
  }));
25
24
 
26
- mock.module("../config/env.js", () => ({
27
- getTwilioPhoneNumberEnv: () => mockTwilioPhoneNumberEnv,
28
- }));
25
+ mock.module("../config/env.js", () => ({}));
29
26
 
30
27
  mock.module("../config/loader.js", () => ({
31
28
  loadRawConfig: () => mockRawConfig,
29
+ loadConfig: () => {
30
+ const raw = mockRawConfig ?? {};
31
+ const wa = (raw.whatsapp ?? {}) as Record<string, unknown>;
32
+ const tw = (raw.twilio ?? {}) as Record<string, unknown>;
33
+ return {
34
+ twilio: { phoneNumber: (tw.phoneNumber as string) ?? "" },
35
+ whatsapp: { phoneNumber: (wa.phoneNumber as string) ?? "" },
36
+ };
37
+ },
32
38
  getConfig: () => {
33
39
  const raw = mockRawConfig ?? {};
34
40
  const wa = (raw.whatsapp ?? {}) as Record<string, unknown>;
35
- return { whatsapp: { phoneNumber: (wa.phoneNumber as string) ?? "" } };
41
+ const tw = (raw.twilio ?? {}) as Record<string, unknown>;
42
+ return {
43
+ twilio: { phoneNumber: (tw.phoneNumber as string) ?? "" },
44
+ whatsapp: { phoneNumber: (wa.phoneNumber as string) ?? "" },
45
+ };
36
46
  },
37
47
  invalidateConfigCache: () => {},
38
48
  }));
@@ -60,7 +70,6 @@ import { credentialKey } from "../security/credential-key.js";
60
70
 
61
71
  describe("channel readiness routes — email and WhatsApp probes", () => {
62
72
  beforeEach(() => {
63
- mockTwilioPhoneNumberEnv = undefined;
64
73
  mockRawConfig = undefined;
65
74
  mockSecureKeys = {};
66
75
  mockHasTwilioCredentials = false;
@@ -1,6 +1,6 @@
1
1
  import { beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
2
2
 
3
- let mockTwilioPhoneNumberEnv: string | undefined;
3
+ let mockTwilioPhoneNumber: string | undefined;
4
4
  let mockRawConfig: Record<string, unknown> | undefined;
5
5
  let mockSecureKeys: Record<string, string>;
6
6
  let mockHasTwilioCredentials: boolean;
@@ -27,16 +27,17 @@ mock.module("../channels/config.js", () => ({
27
27
  }),
28
28
  }));
29
29
 
30
- mock.module("../config/env.js", () => ({
31
- getTwilioPhoneNumberEnv: () => mockTwilioPhoneNumberEnv,
32
- }));
30
+ mock.module("../config/env.js", () => ({}));
33
31
 
34
32
  mock.module("../config/loader.js", () => ({
35
33
  loadRawConfig: () => mockRawConfig,
34
+ loadConfig: () => ({
35
+ twilio: { phoneNumber: mockTwilioPhoneNumber ?? "" },
36
+ whatsapp: { phoneNumber: "" },
37
+ }),
36
38
  getConfig: () => ({
37
- whatsapp: {
38
- phoneNumber: "",
39
- },
39
+ twilio: { phoneNumber: mockTwilioPhoneNumber ?? "" },
40
+ whatsapp: { phoneNumber: "" },
40
41
  }),
41
42
  }));
42
43
 
@@ -100,7 +101,7 @@ describe("ChannelReadinessService", () => {
100
101
 
101
102
  beforeEach(() => {
102
103
  service = new ChannelReadinessService();
103
- mockTwilioPhoneNumberEnv = undefined;
104
+ mockTwilioPhoneNumber = undefined;
104
105
  mockRawConfig = undefined;
105
106
  mockSecureKeys = {};
106
107
  mockHasTwilioCredentials = false;
@@ -406,7 +407,7 @@ describe("ChannelReadinessService", () => {
406
407
 
407
408
  test("voice readiness includes gateway_health when ingress is configured", async () => {
408
409
  mockHasTwilioCredentials = true;
409
- mockTwilioPhoneNumberEnv = "+15550001111";
410
+ mockTwilioPhoneNumber = "+15550001111";
410
411
  mockRawConfig = {
411
412
  ingress: {
412
413
  enabled: true,
@@ -135,9 +135,7 @@ registerTool(mockBundledSkillTool);
135
135
  // Register CU tools so classifyRisk returns their declared Low risk level
136
136
  // instead of falling through to Medium (unknown tool).
137
137
  import { registerComputerUseActionTools } from "../tools/computer-use/registry.js";
138
- import { requestComputerControlTool } from "../tools/computer-use/request-computer-control.js";
139
138
  registerComputerUseActionTools();
140
- registerTool(requestComputerControlTool);
141
139
 
142
140
  function writeSkill(
143
141
  skillId: string,
@@ -673,14 +671,14 @@ describe("Permission Checker", () => {
673
671
  expect(result.decision).toBe("allow");
674
672
  });
675
673
 
676
- test("file_write within workspace with no rule → auto-allowed in workspace mode", async () => {
674
+ test("file_write within workspace with no rule → prompt (medium risk bypasses workspace auto-allow)", async () => {
677
675
  const result = await check(
678
676
  "file_write",
679
677
  { path: "/tmp/file.txt" },
680
678
  "/tmp",
681
679
  );
682
- expect(result.decision).toBe("allow");
683
- expect(result.reason).toContain("workspace-scoped");
680
+ expect(result.decision).toBe("prompt");
681
+ expect(result.reason).toContain("medium risk");
684
682
  });
685
683
 
686
684
  test("file_write outside workspace with no rule → prompt", async () => {
@@ -897,16 +895,16 @@ describe("Permission Checker", () => {
897
895
  );
898
896
  });
899
897
 
900
- test("computer_use_request_control prompts by default via computer-use ask rule", async () => {
898
+ test("computer_use_observe prompts by default via computer-use ask rule", async () => {
901
899
  const result = await check(
902
- "computer_use_request_control",
903
- { task: "Open system settings" },
900
+ "computer_use_observe",
901
+ { reason: "Check current screen state before acting" },
904
902
  "/tmp",
905
903
  );
906
904
  expect(result.decision).toBe("prompt");
907
905
  expect(result.reason).toContain("ask rule");
908
906
  expect(result.matchedRule?.id).toBe(
909
- "default:ask-computer_use_request_control-global",
907
+ "default:ask-computer_use_observe-global",
910
908
  );
911
909
  });
912
910
 
@@ -4407,11 +4405,6 @@ describe("computer-use tool permission defaults", () => {
4407
4405
  expect(risk).toBe(RiskLevel.Low);
4408
4406
  }
4409
4407
  });
4410
-
4411
- test("computer_use_request_control classifies as Low risk", async () => {
4412
- const risk = await classifyRisk("computer_use_request_control", {});
4413
- expect(risk).toBe(RiskLevel.Low);
4414
- });
4415
4408
  });
4416
4409
 
4417
4410
  // ---------------------------------------------------------------------------
@@ -4588,24 +4581,24 @@ describe("workspace mode — auto-allow workspace-scoped operations", () => {
4588
4581
  expect(result.reason).toContain("Workspace mode");
4589
4582
  });
4590
4583
 
4591
- test("file_write within workspace → allow (workspace-scoped)", async () => {
4584
+ test("file_write within workspace → prompt (Medium risk bypasses workspace auto-allow)", async () => {
4592
4585
  const result = await check(
4593
4586
  "file_write",
4594
4587
  { file_path: "/home/user/my-project/src/index.ts" },
4595
4588
  workspaceDir,
4596
4589
  );
4597
- expect(result.decision).toBe("allow");
4598
- expect(result.reason).toContain("Workspace mode");
4590
+ expect(result.decision).toBe("prompt");
4591
+ expect(result.reason).toContain("medium risk");
4599
4592
  });
4600
4593
 
4601
- test("file_edit within workspace → allow (workspace-scoped)", async () => {
4594
+ test("file_edit within workspace → prompt (Medium risk bypasses workspace auto-allow)", async () => {
4602
4595
  const result = await check(
4603
4596
  "file_edit",
4604
4597
  { file_path: "/home/user/my-project/src/index.ts" },
4605
4598
  workspaceDir,
4606
4599
  );
4607
- expect(result.decision).toBe("allow");
4608
- expect(result.reason).toContain("Workspace mode");
4600
+ expect(result.decision).toBe("prompt");
4601
+ expect(result.reason).toContain("medium risk");
4609
4602
  });
4610
4603
 
4611
4604
  // ── file operations outside workspace follow risk-based fallback ──
@@ -29,7 +29,7 @@ const manifestPath = resolve(
29
29
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
30
30
 
31
31
  describe("computer-use skill manifest regression", () => {
32
- test("manifest has exactly 12 tools", () => {
32
+ test("manifest has exactly 11 tools", () => {
33
33
  expect(manifest.tools).toHaveLength(COMPUTER_USE_TOOL_COUNT);
34
34
  });
35
35
 
@@ -13,7 +13,6 @@ import {
13
13
  computerUseTypeTextTool,
14
14
  computerUseWaitTool,
15
15
  } from "../tools/computer-use/definitions.js";
16
- import { requestComputerControlTool } from "../tools/computer-use/request-computer-control.js";
17
16
  import { forwardComputerUseProxyTool } from "../tools/computer-use/skill-proxy-bridge.js";
18
17
  import type { ToolContext } from "../tools/types.js";
19
18
 
@@ -40,22 +39,20 @@ const ctx: ToolContext = {
40
39
  // ── Tool definitions ────────────────────────────────────────────────
41
40
 
42
41
  describe("computer-use tool definitions", () => {
43
- test("allComputerUseTools contains 10 tools", () => {
44
- expect(allComputerUseTools.length).toBe(10);
42
+ test("allComputerUseTools contains 11 tools", () => {
43
+ expect(allComputerUseTools.length).toBe(11);
45
44
  });
46
45
 
47
46
  test("all tools have proxy execution mode", () => {
48
47
  for (const tool of allComputerUseTools) {
49
48
  expect(tool.executionMode).toBe("proxy");
50
49
  }
51
- expect(requestComputerControlTool.executionMode).toBe("proxy");
52
50
  });
53
51
 
54
52
  test("all tools belong to computer-use category", () => {
55
53
  for (const tool of allComputerUseTools) {
56
54
  expect(tool.category).toBe("computer-use");
57
55
  }
58
- expect(requestComputerControlTool.category).toBe("computer-use");
59
56
  });
60
57
 
61
58
  test("all tools have unique names", () => {
@@ -225,20 +222,6 @@ describe("computer_use_respond", () => {
225
222
  });
226
223
  });
227
224
 
228
- // ── request_computer_control ────────────────────────────────────────
229
-
230
- describe("computer_use_request_control", () => {
231
- test("requires task parameter", () => {
232
- expect(schema(requestComputerControlTool).required).toContain("task");
233
- });
234
-
235
- test("execute throws proxy error", () => {
236
- expect(() => requestComputerControlTool.execute({}, ctx)).toThrow(
237
- "surfaceProxyResolver",
238
- );
239
- });
240
- });
241
-
242
225
  // ── skill-proxy-bridge ──────────────────────────────────────────────
243
226
 
244
227
  describe("forwardComputerUseProxyTool", () => {