@vellumai/assistant 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/ARCHITECTURE.md +54 -54
  2. package/docs/architecture/integrations.md +62 -67
  3. package/docs/credential-execution-service.md +3 -3
  4. package/package.json +1 -1
  5. package/src/__tests__/agent-loop.test.ts +111 -0
  6. package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
  7. package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
  8. package/src/__tests__/app-dir-path-guard.test.ts +78 -0
  9. package/src/__tests__/app-executors.test.ts +1 -291
  10. package/src/__tests__/app-git-history.test.ts +4 -4
  11. package/src/__tests__/app-routes-csp.test.ts +1 -0
  12. package/src/__tests__/app-store-dir-names.test.ts +426 -0
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -9
  14. package/src/__tests__/attachments-store.test.ts +169 -21
  15. package/src/__tests__/attachments.test.ts +115 -1
  16. package/src/__tests__/btw-routes.test.ts +1 -0
  17. package/src/__tests__/canonical-guardian-store.test.ts +38 -0
  18. package/src/__tests__/channel-reply-delivery.test.ts +55 -0
  19. package/src/__tests__/checker.test.ts +54 -0
  20. package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
  21. package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
  22. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  23. package/src/__tests__/config-schema-cmd.test.ts +68 -21
  24. package/src/__tests__/config-schema.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
  26. package/src/__tests__/conversation-agent-loop.test.ts +290 -2
  27. package/src/__tests__/conversation-attachments.test.ts +17 -19
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
  29. package/src/__tests__/conversation-disk-view.test.ts +810 -0
  30. package/src/__tests__/conversation-error.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +551 -0
  32. package/src/__tests__/conversation-fork-route.test.ts +386 -0
  33. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  34. package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
  35. package/src/__tests__/conversation-media-retry.test.ts +8 -2
  36. package/src/__tests__/conversation-queue.test.ts +36 -1
  37. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  38. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  39. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  41. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  42. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  43. package/src/__tests__/conversation-store.test.ts +24 -21
  44. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  45. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  46. package/src/__tests__/conversation-title-service.test.ts +137 -0
  47. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  48. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  49. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  51. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  52. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
  53. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  54. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  55. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  56. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  57. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  58. package/src/__tests__/diagnostics-export.test.ts +70 -1
  59. package/src/__tests__/filesystem-tools.test.ts +4 -2
  60. package/src/__tests__/first-greeting.test.ts +80 -0
  61. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  62. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  63. package/src/__tests__/history-repair.test.ts +103 -10
  64. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  65. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  66. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  67. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  68. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  69. package/src/__tests__/media-generate-image.test.ts +47 -94
  70. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  71. package/src/__tests__/memory-recall-quality.test.ts +5 -5
  72. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  73. package/src/__tests__/migration-export-http.test.ts +3 -1
  74. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  75. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  76. package/src/__tests__/mime-builder.test.ts +3 -2
  77. package/src/__tests__/non-member-access-request.test.ts +12 -1
  78. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  79. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  80. package/src/__tests__/oauth-store.test.ts +115 -0
  81. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  82. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  83. package/src/__tests__/recording-handler.test.ts +17 -0
  84. package/src/__tests__/registry.test.ts +3 -8
  85. package/src/__tests__/relay-server.test.ts +1 -1
  86. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  87. package/src/__tests__/schema-transforms.test.ts +165 -5
  88. package/src/__tests__/server-history-render.test.ts +2 -2
  89. package/src/__tests__/skill-feature-flags-integration.test.ts +18 -17
  90. package/src/__tests__/skill-feature-flags.test.ts +13 -13
  91. package/src/__tests__/skill-load-feature-flag.test.ts +4 -4
  92. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  93. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  94. package/src/__tests__/starter-task-flow.test.ts +1 -0
  95. package/src/__tests__/suggestion-routes.test.ts +443 -0
  96. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  97. package/src/__tests__/swarm-recursion.test.ts +1 -0
  98. package/src/__tests__/swarm-tool.test.ts +1 -0
  99. package/src/__tests__/system-prompt.test.ts +8 -0
  100. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  101. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  102. package/src/__tests__/top-level-renderer.test.ts +22 -0
  103. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  104. package/src/__tests__/web-fetch.test.ts +6 -2
  105. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  106. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  107. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  108. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  109. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  110. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  111. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  112. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  113. package/src/agent/attachments.ts +27 -1
  114. package/src/agent/loop.ts +29 -1
  115. package/src/avatar/traits-png-sync.ts +80 -25
  116. package/src/bundler/app-bundler.ts +4 -4
  117. package/src/calls/call-domain.ts +1 -0
  118. package/src/calls/voice-session-bridge.ts +1 -0
  119. package/src/cli/commands/auth.ts +92 -0
  120. package/src/cli/commands/avatar.ts +7 -6
  121. package/src/cli/commands/config.ts +2 -0
  122. package/src/cli/commands/oauth/providers.ts +29 -0
  123. package/src/cli/program.ts +12 -0
  124. package/src/cli.ts +15 -48
  125. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  126. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  127. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  128. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  129. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  130. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  131. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  132. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  133. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  134. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  135. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  136. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  137. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  138. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  139. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  140. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  141. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  142. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  143. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  144. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  145. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  146. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  147. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  148. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  149. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  150. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  151. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  152. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  153. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  154. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  155. package/src/config/bundled-tool-registry.ts +2 -14
  156. package/src/config/feature-flag-registry.json +16 -0
  157. package/src/config/loader.ts +64 -0
  158. package/src/config/raw-config-utils.ts +30 -0
  159. package/src/config/schema-utils.ts +28 -7
  160. package/src/config/schema.ts +8 -0
  161. package/src/config/schemas/elevenlabs.ts +18 -0
  162. package/src/config/schemas/memory-lifecycle.ts +4 -2
  163. package/src/config/schemas/memory-storage.ts +1 -1
  164. package/src/config/schemas/services.ts +8 -6
  165. package/src/contacts/contact-store.ts +13 -6
  166. package/src/contacts/contacts-write.ts +0 -1
  167. package/src/context/window-manager.ts +13 -2
  168. package/src/daemon/conversation-agent-loop-handlers.ts +46 -42
  169. package/src/daemon/conversation-agent-loop.ts +56 -19
  170. package/src/daemon/conversation-attachments.ts +18 -36
  171. package/src/daemon/conversation-error.ts +2 -1
  172. package/src/daemon/conversation-history.ts +18 -4
  173. package/src/daemon/conversation-lifecycle.ts +39 -15
  174. package/src/daemon/conversation-messaging.ts +70 -26
  175. package/src/daemon/conversation-process.ts +58 -34
  176. package/src/daemon/conversation-runtime-assembly.ts +21 -38
  177. package/src/daemon/conversation-slash.ts +121 -256
  178. package/src/daemon/conversation-surfaces.ts +143 -20
  179. package/src/daemon/conversation-tool-setup.ts +0 -6
  180. package/src/daemon/conversation-workspace.ts +21 -1
  181. package/src/daemon/conversation.ts +51 -29
  182. package/src/daemon/first-greeting.ts +35 -0
  183. package/src/daemon/handlers/config-embeddings.ts +148 -0
  184. package/src/daemon/handlers/config-model.ts +71 -26
  185. package/src/daemon/handlers/conversations.ts +0 -23
  186. package/src/daemon/handlers/recording.ts +26 -21
  187. package/src/daemon/history-repair.ts +28 -8
  188. package/src/daemon/host-cu-proxy.ts +2 -2
  189. package/src/daemon/lifecycle.ts +106 -64
  190. package/src/daemon/message-protocol.ts +3 -0
  191. package/src/daemon/message-types/conversations.ts +19 -0
  192. package/src/daemon/message-types/messages.ts +1 -0
  193. package/src/daemon/message-types/shared.ts +2 -0
  194. package/src/daemon/message-types/surfaces.ts +2 -0
  195. package/src/daemon/message-types/upgrades.ts +23 -0
  196. package/src/daemon/server.ts +83 -12
  197. package/src/daemon/shutdown-handlers.ts +8 -5
  198. package/src/daemon/startup-error.ts +9 -0
  199. package/src/daemon/tool-side-effects.ts +11 -28
  200. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  201. package/src/instrument.ts +0 -4
  202. package/src/media/app-icon-generator.ts +2 -2
  203. package/src/memory/app-git-service.ts +28 -16
  204. package/src/memory/app-store.ts +230 -41
  205. package/src/memory/attachments-store.ts +558 -130
  206. package/src/memory/conversation-attention-store.ts +70 -0
  207. package/src/memory/conversation-crud.ts +442 -3
  208. package/src/memory/conversation-directories.ts +125 -0
  209. package/src/memory/conversation-disk-view.ts +390 -0
  210. package/src/memory/conversation-key-store.ts +17 -5
  211. package/src/memory/conversation-queries.ts +5 -1
  212. package/src/memory/conversation-title-service.ts +21 -49
  213. package/src/memory/db-init.ts +28 -0
  214. package/src/memory/embedding-backend.ts +42 -53
  215. package/src/memory/embedding-gemini.test.ts +4 -4
  216. package/src/memory/embedding-local.ts +1 -3
  217. package/src/memory/embedding-ollama.ts +1 -3
  218. package/src/memory/embedding-openai.ts +1 -3
  219. package/src/memory/indexer.ts +9 -7
  220. package/src/memory/items-extractor.ts +42 -13
  221. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  222. package/src/memory/job-handlers/embedding.test.ts +1 -4
  223. package/src/memory/llm-request-log-store.ts +100 -1
  224. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  225. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  226. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  227. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  228. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  229. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  230. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  231. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  232. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  233. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  234. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  235. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  236. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  237. package/src/memory/migrations/index.ts +7 -0
  238. package/src/memory/migrations/registry.ts +13 -0
  239. package/src/memory/retriever.test.ts +601 -2
  240. package/src/memory/retriever.ts +85 -9
  241. package/src/memory/schema/conversations.ts +6 -0
  242. package/src/memory/schema/infrastructure.ts +13 -7
  243. package/src/memory/schema/oauth.ts +6 -0
  244. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  245. package/src/notifications/copy-composer.ts +26 -0
  246. package/src/notifications/decision-engine.ts +14 -1
  247. package/src/notifications/emit-signal.ts +1 -1
  248. package/src/notifications/signal.ts +36 -0
  249. package/src/oauth/byo-connection.test.ts +1 -45
  250. package/src/oauth/byo-connection.ts +2 -8
  251. package/src/oauth/connect-orchestrator.ts +15 -11
  252. package/src/oauth/connection-resolver.test.ts +191 -0
  253. package/src/oauth/connection-resolver.ts +66 -38
  254. package/src/oauth/connection.ts +0 -1
  255. package/src/oauth/oauth-store.ts +97 -47
  256. package/src/oauth/platform-connection.test.ts +0 -1
  257. package/src/oauth/platform-connection.ts +11 -3
  258. package/src/oauth/seed-providers.ts +78 -3
  259. package/src/oauth/token-persistence.ts +16 -10
  260. package/src/permissions/checker.ts +62 -19
  261. package/src/prompts/system-prompt.ts +2 -0
  262. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  263. package/src/providers/anthropic/client.ts +8 -1
  264. package/src/providers/failover.ts +4 -1
  265. package/src/providers/gemini/client.ts +50 -0
  266. package/src/providers/model-catalog.ts +92 -0
  267. package/src/providers/model-intents.ts +29 -20
  268. package/src/providers/openai/client.ts +49 -0
  269. package/src/providers/types.ts +2 -0
  270. package/src/runtime/access-request-helper.ts +16 -7
  271. package/src/runtime/auth/credential-service.ts +3 -1
  272. package/src/runtime/auth/route-policy.ts +14 -1
  273. package/src/runtime/btw-sidechain.ts +101 -0
  274. package/src/runtime/channel-reply-delivery.ts +17 -1
  275. package/src/runtime/http-router.ts +3 -1
  276. package/src/runtime/http-server.ts +196 -141
  277. package/src/runtime/http-types.ts +1 -0
  278. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  279. package/src/runtime/routes/access-request-decision.ts +41 -0
  280. package/src/runtime/routes/app-management-routes.ts +6 -3
  281. package/src/runtime/routes/app-routes.ts +7 -3
  282. package/src/runtime/routes/approval-routes.ts +1 -0
  283. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  284. package/src/runtime/routes/attachment-routes.ts +45 -15
  285. package/src/runtime/routes/btw-routes.ts +21 -61
  286. package/src/runtime/routes/conversation-management-routes.ts +68 -0
  287. package/src/runtime/routes/conversation-query-routes.ts +180 -10
  288. package/src/runtime/routes/conversation-routes.ts +222 -28
  289. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  290. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  291. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  292. package/src/runtime/routes/llm-context-normalization.ts +1199 -0
  293. package/src/runtime/routes/log-export-routes.ts +3 -0
  294. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  295. package/src/runtime/routes/memory-item-routes.ts +4 -0
  296. package/src/runtime/routes/migration-routes.ts +4 -1
  297. package/src/runtime/routes/oauth-apps.ts +291 -0
  298. package/src/runtime/routes/secret-routes.ts +28 -1
  299. package/src/runtime/routes/settings-routes.ts +14 -0
  300. package/src/runtime/routes/trace-event-routes.ts +4 -1
  301. package/src/schedule/schedule-store.ts +9 -21
  302. package/src/security/secure-keys.ts +21 -0
  303. package/src/signals/bash.ts +1 -1
  304. package/src/swarm/backend-claude-code.ts +3 -6
  305. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  306. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  307. package/src/tools/AGENTS.md +6 -10
  308. package/src/tools/apps/executors.ts +17 -232
  309. package/src/tools/claude-code/claude-code.ts +2 -3
  310. package/src/tools/credentials/vault.ts +7 -12
  311. package/src/tools/host-filesystem/read.ts +13 -10
  312. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  313. package/src/tools/schedule/list.ts +2 -7
  314. package/src/tools/schema-transforms.ts +5 -0
  315. package/src/tools/shared/filesystem/format-diff.ts +4 -21
  316. package/src/tools/skills/execute.ts +1 -1
  317. package/src/tools/tool-manifest.ts +0 -6
  318. package/src/tools/ui-surface/definitions.ts +2 -2
  319. package/src/util/device-id.ts +28 -5
  320. package/src/util/platform.ts +6 -0
  321. package/src/util/pricing.ts +1 -0
  322. package/src/util/retry.ts +1 -3
  323. package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
  324. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  325. package/src/workspace/migrations/006-services-config.ts +5 -0
  326. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  327. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  328. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  329. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  330. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  331. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  332. package/src/workspace/migrations/registry.ts +10 -0
  333. package/src/workspace/top-level-renderer.ts +12 -0
  334. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  335. package/src/__tests__/asset-search-tool.test.ts +0 -536
  336. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  337. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  338. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  339. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  340. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  341. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  342. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  343. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  344. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  345. package/src/daemon/media-visibility-policy.ts +0 -59
  346. package/src/tools/assets/materialize.ts +0 -248
  347. package/src/tools/assets/search.ts +0 -400
@@ -2,11 +2,10 @@
2
2
  * Regression tests for app surface refresh and eventing side effects in
3
3
  * createToolExecutor (conversation-tool-setup.ts).
4
4
  *
5
- * After the App Builder tools source swap (PR 8), app tools like app_update,
6
- * app_file_edit, and app_file_write are provided by the app-builder skill
7
- * rather than core registration. The afterExecute hooks in createToolExecutor
8
- * fire based on tool *name* matching, so they must continue to work for
9
- * skill-origin tools. These tests verify that contract.
5
+ * The app_refresh hook is the sole hook for app change refresh. These tests
6
+ * verify that app_refresh, app_create, and app_delete hooks fire correctly,
7
+ * and that removed hooks (app_update, app_file_edit, app_file_write) no
8
+ * longer trigger side effects.
10
9
  */
11
10
 
12
11
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -69,6 +68,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
69
68
  { surfaceType: SurfaceType; data: SurfaceData; title?: string }
70
69
  >(),
71
70
  surfaceUndoStacks: new Map(),
71
+ accumulatedSurfaceState: new Map(),
72
72
  surfaceActionRequestIds: new Set<string>(),
73
73
  currentTurnSurfaces: [],
74
74
  isProcessing: () => false,
@@ -109,9 +109,9 @@ describe("session-tool-setup app refresh side effects", () => {
109
109
  updatePublishedSpy.mockClear();
110
110
  });
111
111
 
112
- // ── app_update ──────────────────────────────────────────────────────
112
+ // ── app_refresh ─────────────────────────────────────────────────────
113
113
 
114
- describe("app_update", () => {
114
+ describe("app_refresh", () => {
115
115
  test("triggers refreshSurfacesForApp when result is not an error", async () => {
116
116
  const ctx = makeCtx();
117
117
  const executor = makeFakeExecutor({
@@ -129,7 +129,7 @@ describe("session-tool-setup app refresh side effects", () => {
129
129
  broadcastSpy,
130
130
  );
131
131
 
132
- await toolFn("app_update", { app_id: "app-1", name: "New Name" });
132
+ await toolFn("app_refresh", { app_id: "app-1" });
133
133
 
134
134
  expect(refreshSpy).toHaveBeenCalledTimes(1);
135
135
  expect((refreshSpy.mock.calls as unknown[][])[0][0]).toBe(ctx);
@@ -150,7 +150,7 @@ describe("session-tool-setup app refresh side effects", () => {
150
150
  broadcastSpy,
151
151
  );
152
152
 
153
- await toolFn("app_update", { app_id: "app-42" });
153
+ await toolFn("app_refresh", { app_id: "app-42" });
154
154
 
155
155
  expect(broadcastSpy).toHaveBeenCalledTimes(1);
156
156
  expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
@@ -172,7 +172,7 @@ describe("session-tool-setup app refresh side effects", () => {
172
172
  mock(() => {}),
173
173
  );
174
174
 
175
- await toolFn("app_update", { app_id: "app-publish" });
175
+ await toolFn("app_refresh", { app_id: "app-publish" });
176
176
 
177
177
  // updatePublishedAppDeployment is called with void (fire-and-forget),
178
178
  // so just verify it was invoked.
@@ -199,7 +199,7 @@ describe("session-tool-setup app refresh side effects", () => {
199
199
  broadcastSpy,
200
200
  );
201
201
 
202
- await toolFn("app_update", { app_id: "app-err" });
202
+ await toolFn("app_refresh", { app_id: "app-err" });
203
203
 
204
204
  expect(refreshSpy).not.toHaveBeenCalled();
205
205
  expect(broadcastSpy).not.toHaveBeenCalled();
@@ -220,303 +220,13 @@ describe("session-tool-setup app refresh side effects", () => {
220
220
  broadcastSpy,
221
221
  );
222
222
 
223
- await toolFn("app_update", {});
223
+ await toolFn("app_refresh", {});
224
224
 
225
225
  expect(refreshSpy).not.toHaveBeenCalled();
226
226
  expect(broadcastSpy).not.toHaveBeenCalled();
227
227
  });
228
228
  });
229
229
 
230
- // ── app_file_edit ───────────────────────────────────────────────────
231
-
232
- describe("app_file_edit", () => {
233
- test("triggers refreshSurfacesForApp with fileChange flag", async () => {
234
- const ctx = makeCtx();
235
- const executor = makeFakeExecutor({
236
- content: '{"ok":true}',
237
- isError: false,
238
- });
239
- const broadcastSpy = mock(() => {});
240
-
241
- const toolFn = createToolExecutor(
242
- executor as unknown as ToolExecutor,
243
- noopPrompter,
244
- noopSecretPrompter,
245
- ctx,
246
- noopLifecycleHandler,
247
- broadcastSpy,
248
- );
249
-
250
- await toolFn("app_file_edit", {
251
- app_id: "app-edit",
252
- path: "index.html",
253
- old_string: "old",
254
- new_string: "new",
255
- });
256
-
257
- expect(refreshSpy).toHaveBeenCalledTimes(1);
258
- expect((refreshSpy.mock.calls as unknown[][])[0][1]).toBe("app-edit");
259
- // Verify opts include fileChange: true
260
- expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
261
- fileChange: true,
262
- status: undefined,
263
- });
264
- });
265
-
266
- test("propagates status field through refresh opts", async () => {
267
- const ctx = makeCtx();
268
- const executor = makeFakeExecutor({
269
- content: '{"ok":true}',
270
- isError: false,
271
- });
272
-
273
- const toolFn = createToolExecutor(
274
- executor as unknown as ToolExecutor,
275
- noopPrompter,
276
- noopSecretPrompter,
277
- ctx,
278
- noopLifecycleHandler,
279
- mock(() => {}),
280
- );
281
-
282
- await toolFn("app_file_edit", {
283
- app_id: "app-status",
284
- path: "styles.css",
285
- old_string: "x",
286
- new_string: "y",
287
- status: "updating styles",
288
- });
289
-
290
- expect(refreshSpy).toHaveBeenCalledTimes(1);
291
- expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
292
- fileChange: true,
293
- status: "updating styles",
294
- });
295
- });
296
-
297
- test("broadcasts app_files_changed for file edit", async () => {
298
- const ctx = makeCtx();
299
- const executor = makeFakeExecutor({ content: "{}", isError: false });
300
- const broadcastSpy = mock(() => {});
301
-
302
- const toolFn = createToolExecutor(
303
- executor as unknown as ToolExecutor,
304
- noopPrompter,
305
- noopSecretPrompter,
306
- ctx,
307
- noopLifecycleHandler,
308
- broadcastSpy,
309
- );
310
-
311
- await toolFn("app_file_edit", {
312
- app_id: "app-edit-bc",
313
- path: "f",
314
- old_string: "a",
315
- new_string: "b",
316
- });
317
-
318
- expect(broadcastSpy).toHaveBeenCalledTimes(1);
319
- expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
320
- type: "app_files_changed",
321
- appId: "app-edit-bc",
322
- });
323
- });
324
-
325
- test("calls updatePublishedAppDeployment for file edit", async () => {
326
- const ctx = makeCtx();
327
- const executor = makeFakeExecutor({ content: "{}", isError: false });
328
-
329
- const toolFn = createToolExecutor(
330
- executor as unknown as ToolExecutor,
331
- noopPrompter,
332
- noopSecretPrompter,
333
- ctx,
334
- noopLifecycleHandler,
335
- mock(() => {}),
336
- );
337
-
338
- await toolFn("app_file_edit", {
339
- app_id: "app-pub-edit",
340
- path: "f",
341
- old_string: "a",
342
- new_string: "b",
343
- });
344
-
345
- expect(updatePublishedSpy).toHaveBeenCalledTimes(1);
346
- expect((updatePublishedSpy.mock.calls as unknown[][])[0][0]).toBe(
347
- "app-pub-edit",
348
- );
349
- });
350
-
351
- test("skips side effects when result is an error", async () => {
352
- const ctx = makeCtx();
353
- const executor = makeFakeExecutor({ content: "Error", isError: true });
354
- const broadcastSpy = mock(() => {});
355
-
356
- const toolFn = createToolExecutor(
357
- executor as unknown as ToolExecutor,
358
- noopPrompter,
359
- noopSecretPrompter,
360
- ctx,
361
- noopLifecycleHandler,
362
- broadcastSpy,
363
- );
364
-
365
- await toolFn("app_file_edit", {
366
- app_id: "app-err",
367
- path: "f",
368
- old_string: "a",
369
- new_string: "b",
370
- });
371
-
372
- expect(refreshSpy).not.toHaveBeenCalled();
373
- expect(broadcastSpy).not.toHaveBeenCalled();
374
- expect(updatePublishedSpy).not.toHaveBeenCalled();
375
- });
376
- });
377
-
378
- // ── app_file_write ──────────────────────────────────────────────────
379
-
380
- describe("app_file_write", () => {
381
- test("triggers refreshSurfacesForApp with fileChange flag", async () => {
382
- const ctx = makeCtx();
383
- const executor = makeFakeExecutor({
384
- content: '{"written":true}',
385
- isError: false,
386
- });
387
- const broadcastSpy = mock(() => {});
388
-
389
- const toolFn = createToolExecutor(
390
- executor as unknown as ToolExecutor,
391
- noopPrompter,
392
- noopSecretPrompter,
393
- ctx,
394
- noopLifecycleHandler,
395
- broadcastSpy,
396
- );
397
-
398
- await toolFn("app_file_write", {
399
- app_id: "app-write",
400
- path: "new.html",
401
- content: "<div/>",
402
- });
403
-
404
- expect(refreshSpy).toHaveBeenCalledTimes(1);
405
- expect((refreshSpy.mock.calls as unknown[][])[0][1]).toBe("app-write");
406
- expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
407
- fileChange: true,
408
- status: undefined,
409
- });
410
- });
411
-
412
- test("propagates status field through refresh opts", async () => {
413
- const ctx = makeCtx();
414
- const executor = makeFakeExecutor({
415
- content: '{"written":true}',
416
- isError: false,
417
- });
418
-
419
- const toolFn = createToolExecutor(
420
- executor as unknown as ToolExecutor,
421
- noopPrompter,
422
- noopSecretPrompter,
423
- ctx,
424
- noopLifecycleHandler,
425
- mock(() => {}),
426
- );
427
-
428
- await toolFn("app_file_write", {
429
- app_id: "app-ws",
430
- path: "f.txt",
431
- content: "hi",
432
- status: "adding dark mode",
433
- });
434
-
435
- expect(refreshSpy).toHaveBeenCalledTimes(1);
436
- expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
437
- fileChange: true,
438
- status: "adding dark mode",
439
- });
440
- });
441
-
442
- test("broadcasts app_files_changed for file write", async () => {
443
- const ctx = makeCtx();
444
- const executor = makeFakeExecutor({ content: "{}", isError: false });
445
- const broadcastSpy = mock(() => {});
446
-
447
- const toolFn = createToolExecutor(
448
- executor as unknown as ToolExecutor,
449
- noopPrompter,
450
- noopSecretPrompter,
451
- ctx,
452
- noopLifecycleHandler,
453
- broadcastSpy,
454
- );
455
-
456
- await toolFn("app_file_write", {
457
- app_id: "app-write-bc",
458
- path: "f",
459
- content: "x",
460
- });
461
-
462
- expect(broadcastSpy).toHaveBeenCalledTimes(1);
463
- expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
464
- type: "app_files_changed",
465
- appId: "app-write-bc",
466
- });
467
- });
468
-
469
- test("calls updatePublishedAppDeployment for file write", async () => {
470
- const ctx = makeCtx();
471
- const executor = makeFakeExecutor({ content: "{}", isError: false });
472
-
473
- const toolFn = createToolExecutor(
474
- executor as unknown as ToolExecutor,
475
- noopPrompter,
476
- noopSecretPrompter,
477
- ctx,
478
- noopLifecycleHandler,
479
- mock(() => {}),
480
- );
481
-
482
- await toolFn("app_file_write", {
483
- app_id: "app-pub-write",
484
- path: "f",
485
- content: "x",
486
- });
487
-
488
- expect(updatePublishedSpy).toHaveBeenCalledTimes(1);
489
- expect((updatePublishedSpy.mock.calls as unknown[][])[0][0]).toBe(
490
- "app-pub-write",
491
- );
492
- });
493
-
494
- test("skips side effects when result is an error", async () => {
495
- const ctx = makeCtx();
496
- const executor = makeFakeExecutor({ content: "Error", isError: true });
497
- const broadcastSpy = mock(() => {});
498
-
499
- const toolFn = createToolExecutor(
500
- executor as unknown as ToolExecutor,
501
- noopPrompter,
502
- noopSecretPrompter,
503
- ctx,
504
- noopLifecycleHandler,
505
- broadcastSpy,
506
- );
507
-
508
- await toolFn("app_file_write", {
509
- app_id: "app-err",
510
- path: "f",
511
- content: "x",
512
- });
513
-
514
- expect(refreshSpy).not.toHaveBeenCalled();
515
- expect(broadcastSpy).not.toHaveBeenCalled();
516
- expect(updatePublishedSpy).not.toHaveBeenCalled();
517
- });
518
- });
519
-
520
230
  // ── app_create side effects ─────────────────────────────────────────
521
231
 
522
232
  describe("app_create side effects", () => {
@@ -616,7 +326,7 @@ describe("session-tool-setup app refresh side effects", () => {
616
326
 
617
327
  describe("name-based hooks fire for skill-origin tools", () => {
618
328
  test("hooks fire purely on tool name, regardless of tool origin", async () => {
619
- // The key invariant: createToolExecutor uses `name === 'app_update'`
329
+ // The key invariant: createToolExecutor uses `name === 'app_refresh'`
620
330
  // string comparison, not tool metadata or origin. This means skill-
621
331
  // projected tools with the same name trigger the same afterExecute
622
332
  // hooks as core tools.
@@ -633,22 +343,14 @@ describe("session-tool-setup app refresh side effects", () => {
633
343
  broadcastSpy,
634
344
  );
635
345
 
636
- // Simulate calling each app tool by name (as the agent loop does)
637
- for (const toolName of [
638
- "app_update",
639
- "app_file_edit",
640
- "app_file_write",
641
- ]) {
346
+ // Simulate calling app_refresh by name (as the agent loop does)
347
+ for (const toolName of ["app_refresh"]) {
642
348
  refreshSpy.mockClear();
643
349
  broadcastSpy.mockClear();
644
350
  updatePublishedSpy.mockClear();
645
351
 
646
352
  await toolFn(toolName, {
647
353
  app_id: "skill-app",
648
- path: "f",
649
- old_string: "a",
650
- new_string: "b",
651
- content: "x",
652
354
  });
653
355
 
654
356
  expect(refreshSpy).toHaveBeenCalledTimes(1);
@@ -675,7 +377,15 @@ describe("session-tool-setup app refresh side effects", () => {
675
377
  broadcastSpy,
676
378
  );
677
379
 
678
- for (const toolName of ["read_file", "write_file", "shell", "app_list"]) {
380
+ for (const toolName of [
381
+ "read_file",
382
+ "write_file",
383
+ "shell",
384
+ "app_list",
385
+ "app_update",
386
+ "app_file_edit",
387
+ "app_file_write",
388
+ ]) {
679
389
  refreshSpy.mockClear();
680
390
  broadcastSpy.mockClear();
681
391
  updatePublishedSpy.mockClear();
@@ -706,7 +416,7 @@ describe("session-tool-setup app refresh side effects", () => {
706
416
  );
707
417
 
708
418
  // Should not throw even though broadcastToAllClients is undefined
709
- const result = await toolFn("app_update", { app_id: "app-no-bc" });
419
+ const result = await toolFn("app_refresh", { app_id: "app-no-bc" });
710
420
 
711
421
  expect(result.isError).toBe(false);
712
422
  expect(refreshSpy).toHaveBeenCalledTimes(1);
@@ -56,6 +56,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
56
56
  { surfaceType: SurfaceType; data: SurfaceData; title?: string }
57
57
  >(),
58
58
  surfaceUndoStacks: new Map(),
59
+ accumulatedSurfaceState: new Map(),
59
60
  surfaceActionRequestIds: new Set<string>(),
60
61
  currentTurnSurfaces: [],
61
62
  isProcessing: () => false,
@@ -56,6 +56,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
56
56
  { surfaceType: SurfaceType; data: SurfaceData; title?: string }
57
57
  >(),
58
58
  surfaceUndoStacks: new Map(),
59
+ accumulatedSurfaceState: new Map(),
59
60
  surfaceActionRequestIds: new Set<string>(),
60
61
  currentTurnSurfaces: [],
61
62
  isProcessing: () => false,
@@ -1,6 +1,10 @@
1
+ import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
1
4
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
5
 
3
6
  import type { AgentEvent } from "../agent/loop.js";
7
+ import { getConversationDirName } from "../memory/conversation-disk-view.js";
4
8
  import type { Message, ProviderResponse } from "../providers/types.js";
5
9
 
6
10
  // ---------------------------------------------------------------------------
@@ -79,6 +83,7 @@ mock.module("../memory/conversation-crud.js", () => ({
79
83
  getMessages: () => [],
80
84
  getConversation: () => ({
81
85
  id: "conv-1",
86
+ createdAt: Date.parse("2026-03-19T12:00:00.000Z"),
82
87
  contextSummary: null,
83
88
  contextCompactedMessageCount: 0,
84
89
  contextCompactedAt: null,
@@ -175,7 +180,7 @@ import { Conversation } from "../daemon/conversation.js";
175
180
  // Helpers
176
181
  // ---------------------------------------------------------------------------
177
182
 
178
- function makeConversation(): Conversation {
183
+ function makeConversation(workingDir = "/tmp"): Conversation {
179
184
  const provider = {
180
185
  name: "mock",
181
186
  async sendMessage(): Promise<ProviderResponse> {
@@ -193,10 +198,17 @@ function makeConversation(): Conversation {
193
198
  "system prompt",
194
199
  4096,
195
200
  () => {},
196
- "/tmp",
201
+ workingDir,
197
202
  );
198
203
  }
199
204
 
205
+ const conversationDirName = getConversationDirName(
206
+ "conv-1",
207
+ Date.parse("2026-03-19T12:00:00.000Z"),
208
+ );
209
+ const conversationPath = `conversations/${conversationDirName}/`;
210
+ const conversationAttachmentsPath = `${conversationPath}attachments/`;
211
+
200
212
  // ---------------------------------------------------------------------------
201
213
  // Tests
202
214
  // ---------------------------------------------------------------------------
@@ -224,6 +236,12 @@ describe("Conversation workspace cache state", () => {
224
236
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
225
237
  "</workspace_top_level>",
226
238
  );
239
+ expect(conversation.getWorkspaceTopLevelContext()!).toContain(
240
+ `Current conversation folder: ${conversationPath}`,
241
+ );
242
+ expect(conversation.getWorkspaceTopLevelContext()!).toContain(
243
+ `Attachment files: ${conversationAttachmentsPath}`,
244
+ );
227
245
  });
228
246
 
229
247
  test("refreshWorkspaceTopLevelContextIfNeeded no-ops when not dirty and cache exists", () => {
@@ -257,4 +275,28 @@ describe("Conversation workspace cache state", () => {
257
275
  );
258
276
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
259
277
  });
278
+
279
+ test("workspace hints follow the resolved legacy directory when canonical is absent", () => {
280
+ const workspaceRoot = mkdtempSync(
281
+ join(tmpdir(), "conversation-workspace-cache-state-"),
282
+ );
283
+ const legacyDirName = `conv-1_2026-03-19T12-00-00.000Z`;
284
+ mkdirSync(join(workspaceRoot, "conversations", legacyDirName), {
285
+ recursive: true,
286
+ });
287
+
288
+ try {
289
+ const tempConversation = makeConversation(workspaceRoot);
290
+ tempConversation.refreshWorkspaceTopLevelContextIfNeeded();
291
+
292
+ expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
293
+ `Current conversation folder: conversations/${legacyDirName}/`,
294
+ );
295
+ expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
296
+ `Attachment files: conversations/${legacyDirName}/attachments/`,
297
+ );
298
+ } finally {
299
+ rmSync(workspaceRoot, { recursive: true, force: true });
300
+ }
301
+ });
260
302
  });
@@ -1,6 +1,7 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import type { AgentEvent } from "../agent/loop.js";
4
+ import { getConversationDirName } from "../memory/conversation-disk-view.js";
4
5
  import type { Message, ProviderResponse } from "../providers/types.js";
5
6
 
6
7
  // ---------------------------------------------------------------------------
@@ -115,6 +116,7 @@ mock.module("../memory/conversation-crud.js", () => ({
115
116
  getMessages: () => [],
116
117
  getConversation: () => ({
117
118
  id: "conv-1",
119
+ createdAt: Date.parse("2026-03-19T12:00:00.000Z"),
118
120
  contextSummary: null,
119
121
  contextCompactedMessageCount: 0,
120
122
  contextCompactedAt: null,
@@ -277,6 +279,13 @@ function messageText(message: Message): string {
277
279
  .join("\n");
278
280
  }
279
281
 
282
+ const conversationDirName = getConversationDirName(
283
+ "conv-1",
284
+ Date.parse("2026-03-19T12:00:00.000Z"),
285
+ );
286
+ const conversationPath = `conversations/${conversationDirName}/`;
287
+ const conversationAttachmentsPath = `${conversationPath}attachments/`;
288
+
280
289
  // ---------------------------------------------------------------------------
281
290
  // Tests
282
291
  // ---------------------------------------------------------------------------
@@ -312,6 +321,8 @@ describe("Conversation workspace injection", () => {
312
321
  const runtimeUser = runCalls[0][runCalls[0].length - 1];
313
322
  const text = messageText(runtimeUser);
314
323
  expect(text).toContain("Root: /tmp");
324
+ expect(text).toContain(`Current conversation folder: ${conversationPath}`);
325
+ expect(text).toContain(`Attachment files: ${conversationAttachmentsPath}`);
315
326
  });
316
327
 
317
328
  test("workspace context is prepended before user text", async () => {
@@ -154,16 +154,16 @@ describe("CES flags do not affect unrelated flags", () => {
154
154
  ).toBe(true);
155
155
  });
156
156
 
157
- test("enabling all CES flags does not change contacts flag (defaultEnabled: false)", () => {
157
+ test("enabling all CES flags does not change contacts flag (defaultEnabled: true)", () => {
158
158
  const overrides: Record<string, boolean> = {};
159
159
  for (const key of ALL_CES_FLAG_KEYS) {
160
160
  overrides[key] = true;
161
161
  }
162
162
  const config = makeConfig(overrides);
163
163
 
164
- // contacts defaults to false in the registry and should stay false
164
+ // contacts defaults to true in the registry and should stay true
165
165
  expect(
166
166
  isAssistantFeatureFlagEnabled("feature_flags.contacts.enabled", config),
167
- ).toBe(false);
167
+ ).toBe(true);
168
168
  });
169
169
  });
@@ -251,6 +251,9 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
251
251
  "tools/claude-code/claude-code.ts", // Claude Code tool API key lookup
252
252
  "workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
253
253
  "cli/commands/avatar.ts", // CLI avatar generation API key lookup
254
+ "config/bundled-skills/slack/tools/shared.ts", // Slack skill bot token lookup
255
+ "daemon/conversation-process.ts", // masked provider key display
256
+ "daemon/handlers/config-model.ts", // masked provider key display
254
257
  ]);
255
258
 
256
259
  const thisDir = dirname(fileURLToPath(import.meta.url));
@@ -102,13 +102,10 @@ mock.module("../daemon/handlers/config-slack-channel.js", () => ({
102
102
  slackChannelConfigCalls.push({ botToken, appToken });
103
103
 
104
104
  const { credentialKey } = await import("../security/credential-key.js");
105
- const {
106
- getSecureKeyAsync,
107
- setSecureKeyAsync,
108
- } = await import("../security/secure-keys.js");
109
- const { upsertCredentialMetadata } = await import(
110
- "../tools/credentials/metadata-store.js"
111
- );
105
+ const { getSecureKeyAsync, setSecureKeyAsync } =
106
+ await import("../security/secure-keys.js");
107
+ const { upsertCredentialMetadata } =
108
+ await import("../tools/credentials/metadata-store.js");
112
109
 
113
110
  const hasExistingBotToken = !!(await getSecureKeyAsync(
114
111
  credentialKey("slack_channel", "bot_token"),
@@ -646,9 +643,7 @@ describe("credential_store tool — prompt action", () => {
646
643
  );
647
644
  expect(appResult.isError).toBe(false);
648
645
  expect(manualConnectionStore["slack_channel"]).toBeUndefined();
649
- expect(slackChannelConfigCalls).toEqual([
650
- { appToken: "xapp-test-token" },
651
- ]);
646
+ expect(slackChannelConfigCalls).toEqual([{ appToken: "xapp-test-token" }]);
652
647
  expect(appResult.content).toContain("connection incomplete");
653
648
 
654
649
  const botResult = await credentialStoreTool.execute(
@@ -30,6 +30,7 @@ function buildMockContext(
30
30
  lastSurfaceAction: new Map(),
31
31
  surfaceState: new Map(),
32
32
  surfaceUndoStacks: new Map(),
33
+ accumulatedSurfaceState: new Map(),
33
34
  surfaceActionRequestIds: new Set(),
34
35
  currentTurnSurfaces: [],
35
36
  hostCuProxy,