@vellumai/assistant 0.5.1 → 0.5.3

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 (405) hide show
  1. package/ARCHITECTURE.md +163 -54
  2. package/docs/architecture/integrations.md +62 -67
  3. package/docs/credential-execution-service.md +3 -3
  4. package/docs/skills.md +100 -0
  5. package/package.json +1 -1
  6. package/src/__tests__/agent-loop.test.ts +111 -0
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
  8. package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
  9. package/src/__tests__/app-dir-path-guard.test.ts +78 -0
  10. package/src/__tests__/app-executors.test.ts +1 -291
  11. package/src/__tests__/app-git-history.test.ts +4 -4
  12. package/src/__tests__/app-routes-csp.test.ts +1 -0
  13. package/src/__tests__/app-store-dir-names.test.ts +426 -0
  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 +156 -5
  26. package/src/__tests__/conversation-agent-loop.test.ts +297 -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-memory-dirty-tail.test.ts +150 -0
  37. package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
  38. package/src/__tests__/conversation-queue.test.ts +36 -1
  39. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  40. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  42. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  43. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  44. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  45. package/src/__tests__/conversation-store.test.ts +24 -21
  46. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  47. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  48. package/src/__tests__/conversation-title-service.test.ts +137 -0
  49. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  50. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  51. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  52. package/src/__tests__/conversation-wipe.test.ts +226 -0
  53. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  54. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  55. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  56. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  57. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  58. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  59. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  60. package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
  61. package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
  62. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
  63. package/src/__tests__/diagnostics-export.test.ts +70 -1
  64. package/src/__tests__/first-greeting.test.ts +80 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  66. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  67. package/src/__tests__/history-repair.test.ts +32 -10
  68. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  69. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  70. package/src/__tests__/inline-command-runner.test.ts +311 -0
  71. package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
  72. package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
  73. package/src/__tests__/list-messages-attachments.test.ts +96 -0
  74. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  75. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  76. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  77. package/src/__tests__/media-generate-image.test.ts +47 -94
  78. package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
  79. package/src/__tests__/memory-brief-time.test.ts +285 -0
  80. package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
  81. package/src/__tests__/memory-chunk-archive.test.ts +400 -0
  82. package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
  83. package/src/__tests__/memory-episode-archive.test.ts +370 -0
  84. package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  86. package/src/__tests__/memory-observation-archive.test.ts +375 -0
  87. package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
  88. package/src/__tests__/memory-recall-quality.test.ts +7 -7
  89. package/src/__tests__/memory-reducer-store.test.ts +728 -0
  90. package/src/__tests__/memory-reducer-types.test.ts +699 -0
  91. package/src/__tests__/memory-reducer.test.ts +698 -0
  92. package/src/__tests__/memory-regressions.test.ts +6 -4
  93. package/src/__tests__/memory-simplified-config.test.ts +281 -0
  94. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  95. package/src/__tests__/migration-export-http.test.ts +3 -1
  96. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  97. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  98. package/src/__tests__/mime-builder.test.ts +3 -2
  99. package/src/__tests__/non-member-access-request.test.ts +12 -1
  100. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  101. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  102. package/src/__tests__/oauth-store.test.ts +115 -0
  103. package/src/__tests__/parse-identity-fields.test.ts +129 -0
  104. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  105. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  106. package/src/__tests__/recording-handler.test.ts +17 -0
  107. package/src/__tests__/registry.test.ts +3 -8
  108. package/src/__tests__/relay-server.test.ts +1 -1
  109. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  110. package/src/__tests__/schema-transforms.test.ts +165 -5
  111. package/src/__tests__/server-history-render.test.ts +2 -2
  112. package/src/__tests__/skill-load-inline-command.test.ts +598 -0
  113. package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
  114. package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
  115. package/src/__tests__/skills-transitive-hash.test.ts +333 -0
  116. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  117. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  118. package/src/__tests__/starter-task-flow.test.ts +1 -0
  119. package/src/__tests__/suggestion-routes.test.ts +443 -0
  120. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  121. package/src/__tests__/swarm-recursion.test.ts +1 -0
  122. package/src/__tests__/swarm-tool.test.ts +1 -0
  123. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  124. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  125. package/src/__tests__/top-level-renderer.test.ts +22 -0
  126. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  127. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
  128. package/src/__tests__/web-fetch.test.ts +6 -2
  129. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  130. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  131. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  132. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  133. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  134. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  135. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  136. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  137. package/src/agent/attachments.ts +27 -1
  138. package/src/agent/loop.ts +29 -1
  139. package/src/avatar/traits-png-sync.ts +80 -25
  140. package/src/bundler/app-bundler.ts +4 -4
  141. package/src/calls/call-domain.ts +1 -0
  142. package/src/calls/voice-session-bridge.ts +1 -0
  143. package/src/cli/commands/auth.ts +92 -0
  144. package/src/cli/commands/avatar.ts +7 -6
  145. package/src/cli/commands/config.ts +2 -0
  146. package/src/cli/commands/oauth/providers.ts +29 -0
  147. package/src/cli/program.ts +12 -0
  148. package/src/cli.ts +15 -48
  149. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  150. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  151. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  152. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  153. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  154. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  155. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  156. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  157. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  158. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  159. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  160. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  161. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  162. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  163. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  164. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  165. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  166. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  170. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  171. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  172. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  173. package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
  174. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  175. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  176. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  177. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  178. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  179. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  180. package/src/config/bundled-tool-registry.ts +2 -14
  181. package/src/config/feature-flag-registry.json +24 -0
  182. package/src/config/loader.ts +65 -0
  183. package/src/config/raw-config-utils.ts +58 -0
  184. package/src/config/schema-utils.ts +28 -7
  185. package/src/config/schema.ts +20 -0
  186. package/src/config/schemas/elevenlabs.ts +18 -0
  187. package/src/config/schemas/memory-lifecycle.ts +4 -2
  188. package/src/config/schemas/memory-simplified.ts +101 -0
  189. package/src/config/schemas/memory-storage.ts +1 -1
  190. package/src/config/schemas/memory.ts +4 -0
  191. package/src/config/schemas/services.ts +8 -6
  192. package/src/config/skills.ts +50 -4
  193. package/src/contacts/contact-store.ts +13 -6
  194. package/src/contacts/contacts-write.ts +0 -1
  195. package/src/context/window-manager.ts +13 -2
  196. package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
  197. package/src/daemon/conversation-agent-loop.ts +127 -20
  198. package/src/daemon/conversation-attachments.ts +18 -36
  199. package/src/daemon/conversation-error.ts +2 -1
  200. package/src/daemon/conversation-history.ts +18 -4
  201. package/src/daemon/conversation-lifecycle.ts +50 -16
  202. package/src/daemon/conversation-messaging.ts +70 -26
  203. package/src/daemon/conversation-process.ts +58 -34
  204. package/src/daemon/conversation-runtime-assembly.ts +22 -38
  205. package/src/daemon/conversation-slash.ts +121 -256
  206. package/src/daemon/conversation-surfaces.ts +170 -24
  207. package/src/daemon/conversation-tool-setup.ts +0 -6
  208. package/src/daemon/conversation-workspace.ts +21 -1
  209. package/src/daemon/conversation.ts +69 -30
  210. package/src/daemon/first-greeting.ts +35 -0
  211. package/src/daemon/handlers/config-embeddings.ts +156 -0
  212. package/src/daemon/handlers/config-model.ts +62 -26
  213. package/src/daemon/handlers/conversations.ts +0 -23
  214. package/src/daemon/handlers/identity.ts +12 -1
  215. package/src/daemon/handlers/recording.ts +26 -21
  216. package/src/daemon/host-cu-proxy.ts +2 -2
  217. package/src/daemon/lifecycle.ts +115 -65
  218. package/src/daemon/message-protocol.ts +3 -0
  219. package/src/daemon/message-types/conversations.ts +18 -0
  220. package/src/daemon/message-types/messages.ts +1 -0
  221. package/src/daemon/message-types/shared.ts +2 -0
  222. package/src/daemon/message-types/surfaces.ts +2 -0
  223. package/src/daemon/message-types/upgrades.ts +23 -0
  224. package/src/daemon/server.ts +83 -12
  225. package/src/daemon/shutdown-handlers.ts +8 -5
  226. package/src/daemon/startup-error.ts +9 -0
  227. package/src/daemon/tool-side-effects.ts +11 -28
  228. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  229. package/src/followups/followup-store.ts +47 -1
  230. package/src/instrument.ts +0 -4
  231. package/src/media/app-icon-generator.ts +2 -2
  232. package/src/memory/app-git-service.ts +28 -16
  233. package/src/memory/app-store.ts +230 -41
  234. package/src/memory/archive-store.ts +400 -0
  235. package/src/memory/attachments-store.ts +558 -130
  236. package/src/memory/brief-formatting.ts +33 -0
  237. package/src/memory/brief-open-loops.ts +266 -0
  238. package/src/memory/brief-time.ts +161 -0
  239. package/src/memory/brief.ts +75 -0
  240. package/src/memory/conversation-attention-store.ts +70 -0
  241. package/src/memory/conversation-crud.ts +591 -8
  242. package/src/memory/conversation-directories.ts +125 -0
  243. package/src/memory/conversation-disk-view.ts +390 -0
  244. package/src/memory/conversation-key-store.ts +17 -5
  245. package/src/memory/conversation-queries.ts +5 -1
  246. package/src/memory/conversation-title-service.ts +21 -49
  247. package/src/memory/db-init.ts +40 -0
  248. package/src/memory/embedding-backend.ts +42 -53
  249. package/src/memory/embedding-gemini.test.ts +4 -4
  250. package/src/memory/embedding-local.ts +1 -3
  251. package/src/memory/embedding-ollama.ts +1 -3
  252. package/src/memory/embedding-openai.ts +1 -3
  253. package/src/memory/indexer.ts +114 -21
  254. package/src/memory/items-extractor.ts +42 -13
  255. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  256. package/src/memory/job-handlers/embedding.test.ts +2 -4
  257. package/src/memory/job-handlers/embedding.ts +83 -0
  258. package/src/memory/job-utils.ts +1 -1
  259. package/src/memory/jobs-store.ts +6 -0
  260. package/src/memory/jobs-worker.ts +12 -0
  261. package/src/memory/llm-request-log-store.ts +100 -1
  262. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  263. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  264. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  265. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  266. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  267. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  268. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  269. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  270. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  271. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  272. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  273. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  274. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  275. package/src/memory/migrations/185-memory-brief-state.ts +52 -0
  276. package/src/memory/migrations/186-memory-archive.ts +109 -0
  277. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
  278. package/src/memory/migrations/index.ts +10 -0
  279. package/src/memory/migrations/registry.ts +13 -0
  280. package/src/memory/qdrant-client.ts +23 -4
  281. package/src/memory/reducer-store.ts +271 -0
  282. package/src/memory/reducer-types.ts +99 -0
  283. package/src/memory/reducer.ts +453 -0
  284. package/src/memory/retriever.test.ts +601 -2
  285. package/src/memory/retriever.ts +85 -9
  286. package/src/memory/schema/conversations.ts +9 -0
  287. package/src/memory/schema/index.ts +2 -0
  288. package/src/memory/schema/infrastructure.ts +13 -7
  289. package/src/memory/schema/memory-archive.ts +121 -0
  290. package/src/memory/schema/memory-brief.ts +55 -0
  291. package/src/memory/schema/oauth.ts +6 -0
  292. package/src/memory/search/semantic.ts +17 -4
  293. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  294. package/src/notifications/copy-composer.ts +26 -0
  295. package/src/notifications/decision-engine.ts +14 -1
  296. package/src/notifications/emit-signal.ts +1 -1
  297. package/src/notifications/signal.ts +36 -0
  298. package/src/oauth/byo-connection.test.ts +1 -45
  299. package/src/oauth/byo-connection.ts +2 -8
  300. package/src/oauth/connect-orchestrator.ts +15 -11
  301. package/src/oauth/connection-resolver.test.ts +191 -0
  302. package/src/oauth/connection-resolver.ts +66 -38
  303. package/src/oauth/connection.ts +0 -1
  304. package/src/oauth/oauth-store.ts +99 -47
  305. package/src/oauth/platform-connection.test.ts +0 -1
  306. package/src/oauth/platform-connection.ts +11 -3
  307. package/src/oauth/seed-providers.ts +78 -3
  308. package/src/oauth/token-persistence.ts +16 -10
  309. package/src/permissions/checker.ts +160 -14
  310. package/src/permissions/defaults.ts +14 -0
  311. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  312. package/src/providers/anthropic/client.ts +8 -1
  313. package/src/providers/failover.ts +4 -1
  314. package/src/providers/gemini/client.ts +50 -0
  315. package/src/providers/model-catalog.ts +92 -0
  316. package/src/providers/model-intents.ts +29 -20
  317. package/src/providers/openai/client.ts +49 -0
  318. package/src/providers/types.ts +2 -0
  319. package/src/runtime/access-request-helper.ts +16 -7
  320. package/src/runtime/auth/credential-service.ts +3 -1
  321. package/src/runtime/auth/route-policy.ts +14 -1
  322. package/src/runtime/btw-sidechain.ts +101 -0
  323. package/src/runtime/channel-reply-delivery.ts +17 -1
  324. package/src/runtime/http-router.ts +3 -1
  325. package/src/runtime/http-server.ts +196 -141
  326. package/src/runtime/http-types.ts +1 -0
  327. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  328. package/src/runtime/routes/access-request-decision.ts +41 -0
  329. package/src/runtime/routes/app-management-routes.ts +6 -3
  330. package/src/runtime/routes/app-routes.ts +7 -3
  331. package/src/runtime/routes/approval-routes.ts +1 -0
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  333. package/src/runtime/routes/attachment-routes.ts +45 -15
  334. package/src/runtime/routes/btw-routes.ts +21 -61
  335. package/src/runtime/routes/conversation-management-routes.ts +74 -0
  336. package/src/runtime/routes/conversation-query-routes.ts +187 -10
  337. package/src/runtime/routes/conversation-routes.ts +269 -28
  338. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  339. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  340. package/src/runtime/routes/identity-routes.ts +2 -35
  341. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  342. package/src/runtime/routes/llm-context-normalization.ts +1212 -0
  343. package/src/runtime/routes/log-export-routes.ts +3 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  345. package/src/runtime/routes/memory-item-routes.ts +94 -5
  346. package/src/runtime/routes/migration-routes.ts +4 -1
  347. package/src/runtime/routes/oauth-apps.ts +291 -0
  348. package/src/runtime/routes/secret-routes.ts +30 -1
  349. package/src/runtime/routes/settings-routes.ts +14 -0
  350. package/src/runtime/routes/surface-action-routes.ts +68 -1
  351. package/src/runtime/routes/trace-event-routes.ts +4 -1
  352. package/src/schedule/schedule-store.ts +30 -21
  353. package/src/security/secure-keys.ts +21 -0
  354. package/src/signals/bash.ts +1 -1
  355. package/src/skills/inline-command-expansions.ts +204 -0
  356. package/src/skills/inline-command-render.ts +127 -0
  357. package/src/skills/inline-command-runner.ts +242 -0
  358. package/src/skills/transitive-version-hash.ts +88 -0
  359. package/src/swarm/backend-claude-code.ts +3 -6
  360. package/src/tasks/task-store.ts +43 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  362. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  363. package/src/tools/AGENTS.md +6 -10
  364. package/src/tools/apps/executors.ts +17 -232
  365. package/src/tools/claude-code/claude-code.ts +2 -3
  366. package/src/tools/credentials/vault.ts +7 -12
  367. package/src/tools/host-filesystem/read.ts +13 -10
  368. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  369. package/src/tools/permission-checker.ts +8 -1
  370. package/src/tools/schedule/list.ts +2 -7
  371. package/src/tools/schema-transforms.ts +5 -0
  372. package/src/tools/shared/filesystem/format-diff.ts +2 -7
  373. package/src/tools/skills/execute.ts +1 -1
  374. package/src/tools/skills/load.ts +140 -6
  375. package/src/tools/tool-manifest.ts +0 -6
  376. package/src/tools/ui-surface/definitions.ts +2 -2
  377. package/src/util/device-id.ts +28 -5
  378. package/src/util/platform.ts +24 -0
  379. package/src/util/pricing.ts +1 -0
  380. package/src/util/retry.ts +1 -3
  381. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  382. package/src/workspace/migrations/006-services-config.ts +5 -0
  383. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  384. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  385. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  386. package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
  387. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  388. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  389. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  390. package/src/workspace/migrations/registry.ts +11 -1
  391. package/src/workspace/top-level-renderer.ts +12 -0
  392. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  393. package/src/__tests__/asset-search-tool.test.ts +0 -536
  394. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  395. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  396. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  397. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  398. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  399. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  400. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  401. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  402. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  403. package/src/daemon/media-visibility-policy.ts +0 -59
  404. package/src/tools/assets/materialize.ts +0 -248
  405. package/src/tools/assets/search.ts +0 -400
@@ -28,3 +28,61 @@ export function setServiceField(
28
28
  services[service] = svc;
29
29
  raw.services = services;
30
30
  }
31
+
32
+ /**
33
+ * Safely set a nested field on a raw config object's `memory.embeddings` map.
34
+ *
35
+ * Ensures the `memory` and `embeddings` objects exist before writing,
36
+ * so callers don't need to guard against undefined intermediate keys.
37
+ *
38
+ * Example: `setMemoryEmbeddingField(raw, "provider", "openai")`
39
+ * produces `raw.memory.embeddings.provider = "openai"`.
40
+ */
41
+ export function setMemoryEmbeddingField(
42
+ raw: Record<string, unknown>,
43
+ field: string,
44
+ value: unknown,
45
+ ): void {
46
+ const memory: Record<string, unknown> =
47
+ raw.memory != null &&
48
+ typeof raw.memory === "object" &&
49
+ !Array.isArray(raw.memory)
50
+ ? (raw.memory as Record<string, unknown>)
51
+ : {};
52
+ const existing = memory.embeddings;
53
+ const embeddings: Record<string, unknown> =
54
+ existing != null && typeof existing === "object" && !Array.isArray(existing)
55
+ ? (existing as Record<string, unknown>)
56
+ : {};
57
+ embeddings[field] = value;
58
+ memory.embeddings = embeddings;
59
+ raw.memory = memory;
60
+ }
61
+
62
+ /**
63
+ * Safely delete a nested field from a raw config object's `memory.embeddings`
64
+ * map, allowing Zod schema defaults to take effect on the next config reload.
65
+ */
66
+ export function deleteMemoryEmbeddingField(
67
+ raw: Record<string, unknown>,
68
+ field: string,
69
+ ): void {
70
+ if (
71
+ raw.memory == null ||
72
+ typeof raw.memory !== "object" ||
73
+ Array.isArray(raw.memory)
74
+ ) {
75
+ return;
76
+ }
77
+ const memory = raw.memory as Record<string, unknown>;
78
+ const existing = memory.embeddings;
79
+ if (
80
+ existing == null ||
81
+ typeof existing !== "object" ||
82
+ Array.isArray(existing)
83
+ ) {
84
+ return;
85
+ }
86
+ const embeddings = existing as Record<string, unknown>;
87
+ delete embeddings[field];
88
+ }
@@ -1,8 +1,34 @@
1
1
  import type { z } from "zod";
2
2
 
3
+ /**
4
+ * Unwrap a Zod schema to reach its inner object shape, handling:
5
+ * - default/optional/nullable wrappers (innerType)
6
+ * - pipe/transform wrappers (in — the input side)
7
+ */
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ function unwrapToShape(schema: any): any {
10
+ let current = schema;
11
+ while (current && !current.shape) {
12
+ const def = current._zod?.def;
13
+ if (!def) break;
14
+ // Pipe/transform: follow the input side to get the pre-transform schema
15
+ if (def.type === "pipe" && def.in) {
16
+ current = def.in;
17
+ continue;
18
+ }
19
+ // Default/optional/nullable: follow innerType
20
+ if (def.innerType) {
21
+ current = def.innerType;
22
+ continue;
23
+ }
24
+ break;
25
+ }
26
+ return current;
27
+ }
28
+
3
29
  /**
4
30
  * Navigate a Zod schema by dotted path, unwrapping wrapper types
5
- * (default, optional, nullable) to reach inner object shapes.
31
+ * (default, optional, nullable, pipe/transform) to reach inner object shapes.
6
32
  * Returns the Zod schema at the given path, or null if the path is invalid.
7
33
  */
8
34
  export function getSchemaAtPath(
@@ -13,12 +39,7 @@ export function getSchemaAtPath(
13
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
40
  let current: any = schema;
15
41
  for (const key of keys) {
16
- // Unwrap default/optional/nullable wrappers to find inner object shape
17
- while (current && !current.shape) {
18
- const inner = current._zod?.def?.innerType;
19
- if (!inner) break;
20
- current = inner;
21
- }
42
+ current = unwrapToShape(current);
22
43
  if (!current || !current.shape) return null;
23
44
  current = current.shape[key];
24
45
  if (!current) return null;
@@ -38,6 +38,7 @@ export type { ElevenLabsConfig } from "./schemas/elevenlabs.js";
38
38
  export {
39
39
  DEFAULT_ELEVENLABS_VOICE_ID,
40
40
  ElevenLabsConfigSchema,
41
+ VALID_CONVERSATION_TIMEOUTS,
41
42
  } from "./schemas/elevenlabs.js";
42
43
  export type { HeartbeatConfig } from "./schemas/heartbeat.js";
43
44
  export { HeartbeatConfigSchema } from "./schemas/heartbeat.js";
@@ -105,6 +106,18 @@ export {
105
106
  MemoryDynamicBudgetConfigSchema,
106
107
  MemoryRetrievalConfigSchema,
107
108
  } from "./schemas/memory-retrieval.js";
109
+ export type {
110
+ MemorySimplifiedArchiveRecallConfig,
111
+ MemorySimplifiedBriefConfig,
112
+ MemorySimplifiedConfig,
113
+ MemorySimplifiedReducerConfig,
114
+ } from "./schemas/memory-simplified.js";
115
+ export {
116
+ MemorySimplifiedArchiveRecallConfigSchema,
117
+ MemorySimplifiedBriefConfigSchema,
118
+ MemorySimplifiedConfigSchema,
119
+ MemorySimplifiedReducerConfigSchema,
120
+ } from "./schemas/memory-simplified.js";
108
121
  export type {
109
122
  MemoryEmbeddingsConfig,
110
123
  MemorySegmentationConfig,
@@ -321,6 +334,13 @@ export const AssistantConfigSchema = z
321
334
  .boolean()
322
335
  .default(true)
323
336
  .describe("Whether to send diagnostic/crash reports"),
337
+ maxStepsPerSession: z
338
+ .number({ error: "maxStepsPerSession must be a number" })
339
+ .int("maxStepsPerSession must be an integer")
340
+ .min(1, "maxStepsPerSession must be >= 1")
341
+ .max(200, "maxStepsPerSession must be <= 200")
342
+ .default(50)
343
+ .describe("Maximum number of computer-use steps per session"),
324
344
  })
325
345
  .superRefine((config, ctx) => {
326
346
  if (
@@ -5,6 +5,9 @@ import { z } from "zod";
5
5
  // Mirrored in: clients/macos/.../OpenAIVoiceService.swift (defaultVoiceId)
6
6
  export const DEFAULT_ELEVENLABS_VOICE_ID = "ZF6FPAbjXT4488VcRRnw";
7
7
 
8
+ /** Valid conversation timeout values (seconds). Shared with voice-config-update tool. */
9
+ export const VALID_CONVERSATION_TIMEOUTS = [5, 10, 15, 30, 60] as const;
10
+
8
11
  export const ElevenLabsConfigSchema = z
9
12
  .object({
10
13
  voiceId: z
@@ -42,6 +45,21 @@ export const ElevenLabsConfigSchema = z
42
45
  .describe(
43
46
  "How closely the output matches the original voice — higher values increase similarity",
44
47
  ),
48
+ conversationTimeoutSeconds: z
49
+ .number({
50
+ error: "elevenlabs.conversationTimeoutSeconds must be a number",
51
+ })
52
+ .refine(
53
+ (v) =>
54
+ VALID_CONVERSATION_TIMEOUTS.includes(
55
+ v as (typeof VALID_CONVERSATION_TIMEOUTS)[number],
56
+ ),
57
+ {
58
+ message: `elevenlabs.conversationTimeoutSeconds must be one of: ${VALID_CONVERSATION_TIMEOUTS.join(", ")}`,
59
+ },
60
+ )
61
+ .default(30)
62
+ .describe("Seconds of silence before voice conversation auto-ends"),
45
63
  })
46
64
  .describe("ElevenLabs text-to-speech configuration");
47
65
 
@@ -68,8 +68,10 @@ export const MemoryCleanupConfigSchema = z
68
68
  .nonnegative(
69
69
  "memory.cleanup.conversationRetentionDays must be non-negative",
70
70
  )
71
- .default(90)
72
- .describe("Number of days to retain conversation data before cleanup"),
71
+ .default(0)
72
+ .describe(
73
+ "Number of days to retain conversation data before cleanup (0 disables pruning)",
74
+ ),
73
75
  })
74
76
  .describe("Automatic memory cleanup and garbage collection settings");
75
77
 
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+
3
+ export const MemorySimplifiedBriefConfigSchema = z
4
+ .object({
5
+ maxTokens: z
6
+ .number({
7
+ error: "memory.simplified.brief.maxTokens must be a number",
8
+ })
9
+ .int("memory.simplified.brief.maxTokens must be an integer")
10
+ .positive("memory.simplified.brief.maxTokens must be a positive integer")
11
+ .default(4000)
12
+ .describe(
13
+ "Maximum token budget for the memory brief injected into conversation context",
14
+ ),
15
+ })
16
+ .describe("Controls the memory brief that is injected into conversations");
17
+
18
+ export const MemorySimplifiedReducerConfigSchema = z
19
+ .object({
20
+ idleDelayMs: z
21
+ .number({
22
+ error: "memory.simplified.reducer.idleDelayMs must be a number",
23
+ })
24
+ .int("memory.simplified.reducer.idleDelayMs must be an integer")
25
+ .positive(
26
+ "memory.simplified.reducer.idleDelayMs must be a positive integer",
27
+ )
28
+ .default(30_000)
29
+ .describe(
30
+ "Milliseconds of idle time before the reducer processes new conversation turns into memory",
31
+ ),
32
+ switchWaitMs: z
33
+ .number({
34
+ error: "memory.simplified.reducer.switchWaitMs must be a number",
35
+ })
36
+ .int("memory.simplified.reducer.switchWaitMs must be an integer")
37
+ .positive(
38
+ "memory.simplified.reducer.switchWaitMs must be a positive integer",
39
+ )
40
+ .default(5_000)
41
+ .describe(
42
+ "Milliseconds to wait after a conversation switch before running the reducer",
43
+ ),
44
+ })
45
+ .describe(
46
+ "Controls when the memory reducer runs to process conversation turns into persistent memory",
47
+ );
48
+
49
+ export const MemorySimplifiedArchiveRecallConfigSchema = z
50
+ .object({
51
+ maxSnippets: z
52
+ .number({
53
+ error: "memory.simplified.archiveRecall.maxSnippets must be a number",
54
+ })
55
+ .int("memory.simplified.archiveRecall.maxSnippets must be an integer")
56
+ .positive(
57
+ "memory.simplified.archiveRecall.maxSnippets must be a positive integer",
58
+ )
59
+ .default(10)
60
+ .describe(
61
+ "Maximum number of archive snippets to recall when supplementing the brief with semantic search",
62
+ ),
63
+ })
64
+ .describe(
65
+ "Controls how archived memory snippets are recalled via semantic search",
66
+ );
67
+
68
+ export const MemorySimplifiedConfigSchema = z
69
+ .object({
70
+ enabled: z
71
+ .boolean({
72
+ error: "memory.simplified.enabled must be a boolean",
73
+ })
74
+ .default(false)
75
+ .describe("Whether the simplified memory system is enabled"),
76
+ brief: MemorySimplifiedBriefConfigSchema.default(
77
+ MemorySimplifiedBriefConfigSchema.parse({}),
78
+ ),
79
+ reducer: MemorySimplifiedReducerConfigSchema.default(
80
+ MemorySimplifiedReducerConfigSchema.parse({}),
81
+ ),
82
+ archiveRecall: MemorySimplifiedArchiveRecallConfigSchema.default(
83
+ MemorySimplifiedArchiveRecallConfigSchema.parse({}),
84
+ ),
85
+ })
86
+ .describe(
87
+ "Simplified two-layer memory system — a brief plus archive recall, replacing the legacy item/tier/staleness model",
88
+ );
89
+
90
+ export type MemorySimplifiedConfig = z.infer<
91
+ typeof MemorySimplifiedConfigSchema
92
+ >;
93
+ export type MemorySimplifiedBriefConfig = z.infer<
94
+ typeof MemorySimplifiedBriefConfigSchema
95
+ >;
96
+ export type MemorySimplifiedReducerConfig = z.infer<
97
+ typeof MemorySimplifiedReducerConfigSchema
98
+ >;
99
+ export type MemorySimplifiedArchiveRecallConfig = z.infer<
100
+ typeof MemorySimplifiedArchiveRecallConfigSchema
101
+ >;
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
 
3
- const VALID_MEMORY_EMBEDDING_PROVIDERS = [
3
+ export const VALID_MEMORY_EMBEDDING_PROVIDERS = [
4
4
  "auto",
5
5
  "local",
6
6
  "openai",
@@ -10,6 +10,7 @@ import {
10
10
  MemorySummarizationConfigSchema,
11
11
  } from "./memory-processing.js";
12
12
  import { MemoryRetrievalConfigSchema } from "./memory-retrieval.js";
13
+ import { MemorySimplifiedConfigSchema } from "./memory-simplified.js";
13
14
  import {
14
15
  MemoryEmbeddingsConfigSchema,
15
16
  MemorySegmentationConfigSchema,
@@ -45,6 +46,9 @@ export const MemoryConfigSchema = z
45
46
  summarization: MemorySummarizationConfigSchema.default(
46
47
  MemorySummarizationConfigSchema.parse({}),
47
48
  ),
49
+ simplified: MemorySimplifiedConfigSchema.default(
50
+ MemorySimplifiedConfigSchema.parse({}),
51
+ ),
48
52
  })
49
53
  .describe(
50
54
  "Long-term memory system — stores, retrieves, and manages persistent knowledge across conversations",
@@ -20,15 +20,18 @@ export const VALID_WEB_SEARCH_PROVIDERS = [
20
20
  "inference-provider-native",
21
21
  ] as const;
22
22
 
23
- export const InferenceServiceSchema = z.object({
23
+ export const BaseServiceSchema = z.object({
24
24
  mode: ServiceModeSchema.default("your-own"),
25
+ });
26
+ export type BaseService = z.infer<typeof BaseServiceSchema>;
27
+
28
+ export const InferenceServiceSchema = BaseServiceSchema.extend({
25
29
  provider: z.enum(VALID_INFERENCE_PROVIDERS).default("anthropic"),
26
30
  model: z.string().default("claude-opus-4-6"),
27
31
  });
28
32
  export type InferenceService = z.infer<typeof InferenceServiceSchema>;
29
33
 
30
- export const ImageGenerationServiceSchema = z.object({
31
- mode: ServiceModeSchema.default("your-own"),
34
+ export const ImageGenerationServiceSchema = BaseServiceSchema.extend({
32
35
  provider: z.enum(VALID_IMAGE_GEN_PROVIDERS).default("gemini"),
33
36
  model: z.string().default("gemini-3.1-flash-image-preview"),
34
37
  });
@@ -36,15 +39,14 @@ export type ImageGenerationService = z.infer<
36
39
  typeof ImageGenerationServiceSchema
37
40
  >;
38
41
 
39
- export const WebSearchServiceSchema = z.object({
40
- mode: ServiceModeSchema.default("your-own"),
42
+ export const WebSearchServiceSchema = BaseServiceSchema.extend({
41
43
  provider: z
42
44
  .enum(VALID_WEB_SEARCH_PROVIDERS)
43
45
  .default("inference-provider-native"),
44
46
  });
45
47
  export type WebSearchService = z.infer<typeof WebSearchServiceSchema>;
46
48
 
47
- export const GoogleOAuthServiceSchema = z.object({
49
+ export const GoogleOAuthServiceSchema = BaseServiceSchema.extend({
48
50
  mode: ServiceModeSchema.default("managed"),
49
51
  });
50
52
  export type GoogleOAuthService = z.infer<typeof GoogleOAuthServiceSchema>;
@@ -25,10 +25,15 @@ import {
25
25
  userMessage,
26
26
  } from "../providers/provider-send-message.js";
27
27
  import { parseFrontmatterFields } from "../skills/frontmatter.js";
28
+ import type { InlineCommandExpansion } from "../skills/inline-command-expansions.js";
29
+ import { parseInlineCommandExpansions } from "../skills/inline-command-expansions.js";
28
30
  import { parseToolManifestFile } from "../skills/tool-manifest.js";
29
31
  import { computeSkillVersionHash } from "../skills/version-hash.js";
30
32
  import { getLogger } from "../util/logger.js";
31
- import { getWorkspaceSkillsDir } from "../util/platform.js";
33
+ import {
34
+ getWorkspaceDirDisplay,
35
+ getWorkspaceSkillsDir,
36
+ } from "../util/platform.js";
32
37
  import { isAssistantFeatureFlagEnabled } from "./assistant-feature-flags.js";
33
38
  import { getConfig } from "./loader.js";
34
39
 
@@ -80,6 +85,8 @@ export interface SkillSummary {
80
85
  activationHints?: string[];
81
86
  /** Conditions under which this skill should NOT be loaded. */
82
87
  avoidWhen?: string[];
88
+ /** Parsed inline command expansion descriptors (`!\`command\``) found in the skill body. */
89
+ inlineCommandExpansions?: InlineCommandExpansion[];
83
90
  }
84
91
 
85
92
  export interface SkillDefinition extends SkillSummary {
@@ -198,6 +205,7 @@ interface ParsedFrontmatter {
198
205
  featureFlag?: string;
199
206
  activationHints?: string[];
200
207
  avoidWhen?: string[];
208
+ inlineCommandExpansions?: InlineCommandExpansion[];
201
209
  }
202
210
 
203
211
  function normalizeStringArray(raw: unknown): string[] | undefined {
@@ -302,16 +310,29 @@ function parseFrontmatter(
302
310
  const activationHints = normalizeStringArray(vellum?.["activation-hints"]);
303
311
  const avoidWhen = normalizeStringArray(vellum?.["avoid-when"]);
304
312
 
313
+ const strippedBody = stripCommentLines(body);
314
+
315
+ // Parse inline command expansions from the body (after frontmatter/comment stripping)
316
+ const expansionResult = parseInlineCommandExpansions(strippedBody);
317
+ const inlineCommandExpansions =
318
+ expansionResult.expansions.length > 0
319
+ ? expansionResult.expansions
320
+ : undefined;
321
+
322
+ // Fail closed: if there are malformed tokens, log and exclude from parsed expansions
323
+ // (errors are already logged inside parseInlineCommandExpansions)
324
+
305
325
  return {
306
326
  name,
307
327
  displayName,
308
328
  description,
309
- body: stripCommentLines(body),
329
+ body: strippedBody,
310
330
  emoji,
311
331
  includes,
312
332
  featureFlag,
313
333
  activationHints,
314
334
  avoidWhen,
335
+ inlineCommandExpansions,
315
336
  };
316
337
  }
317
338
 
@@ -466,6 +487,7 @@ function readSkillFromDirectory(
466
487
  featureFlag: parsed.featureFlag,
467
488
  activationHints: parsed.activationHints,
468
489
  avoidWhen: parsed.avoidWhen,
490
+ inlineCommandExpansions: parsed.inlineCommandExpansions,
469
491
  };
470
492
  } catch (err) {
471
493
  log.warn({ err, skillFilePath }, "Failed to read skill file");
@@ -516,6 +538,7 @@ function readBundledSkillFromDirectory(
516
538
  featureFlag: parsed.featureFlag,
517
539
  activationHints: parsed.activationHints,
518
540
  avoidWhen: parsed.avoidWhen,
541
+ inlineCommandExpansions: parsed.inlineCommandExpansions,
519
542
  };
520
543
  } catch (err) {
521
544
  log.warn({ err, skillFilePath }, "Failed to read bundled skill file");
@@ -574,6 +597,7 @@ function loadBundledSkills(): SkillSummary[] {
574
597
  featureFlag: skill.featureFlag,
575
598
  activationHints: skill.activationHints,
576
599
  avoidWhen: skill.avoidWhen,
600
+ inlineCommandExpansions: skill.inlineCommandExpansions,
577
601
  });
578
602
  }
579
603
 
@@ -710,6 +734,7 @@ function skillSummaryFromDefinition(
710
734
  featureFlag: skill.featureFlag,
711
735
  activationHints: skill.activationHints,
712
736
  avoidWhen: skill.avoidWhen,
737
+ inlineCommandExpansions: skill.inlineCommandExpansions,
713
738
  };
714
739
  }
715
740
 
@@ -760,6 +785,7 @@ export function loadSkillCatalog(
760
785
  toolManifest: detectToolManifest(directory),
761
786
  includes: parsed.includes,
762
787
  featureFlag: parsed.featureFlag,
788
+ inlineCommandExpansions: parsed.inlineCommandExpansions,
763
789
  });
764
790
  } catch (err) {
765
791
  log.warn({ err, directory }, "Failed to read skill from extraDirs");
@@ -854,6 +880,7 @@ export function loadSkillCatalog(
854
880
  toolManifest: detectToolManifest(directory),
855
881
  includes: parsed.includes,
856
882
  featureFlag: parsed.featureFlag,
883
+ inlineCommandExpansions: parsed.inlineCommandExpansions,
857
884
  };
858
885
 
859
886
  if (seenIds.has(id)) {
@@ -1001,8 +1028,28 @@ function loadSkillDefinition(skill: SkillSummary): SkillLookupResult {
1001
1028
  }
1002
1029
  // Replace {baseDir} placeholders with the actual skill directory path
1003
1030
  loaded.body = loaded.body.replaceAll("{baseDir}", loaded.directoryPath);
1031
+ // Replace {workspaceDir} placeholders with the runtime workspace display path
1032
+ loaded.body = loaded.body.replaceAll(
1033
+ "{workspaceDir}",
1034
+ getWorkspaceDirDisplay(),
1035
+ );
1004
1036
  // Strip feature-gated sections based on assistant feature flags
1005
1037
  loaded.body = applyFeatureGatedSections(loaded.body);
1038
+
1039
+ // Re-parse inline command expansions after placeholder substitution.
1040
+ // The initial parse (during SKILL.md parsing) produces byte offsets against
1041
+ // the pre-substitution body. Since {baseDir} and {workspaceDir} replacements
1042
+ // change the body length, those offsets become stale. Re-parsing ensures the
1043
+ // offsets match the final body that renderInlineCommands will operate on.
1044
+ if (
1045
+ loaded.inlineCommandExpansions &&
1046
+ loaded.inlineCommandExpansions.length > 0
1047
+ ) {
1048
+ const reparse = parseInlineCommandExpansions(loaded.body);
1049
+ loaded.inlineCommandExpansions =
1050
+ reparse.expansions.length > 0 ? reparse.expansions : undefined;
1051
+ }
1052
+
1006
1053
  return { skill: loaded };
1007
1054
  }
1008
1055
 
@@ -1021,8 +1068,7 @@ export function resolveSkillSelector(
1021
1068
  const catalog = loadSkillCatalog(workspaceSkillsDir);
1022
1069
  if (catalog.length === 0) {
1023
1070
  return {
1024
- error:
1025
- "No skills are available. Configure ~/.vellum/workspace/skills/SKILLS.md or add skill directories.",
1071
+ error: `No skills are available. Configure ${getWorkspaceDirDisplay()}/skills/SKILLS.md or add skill directories.`,
1026
1072
  errorCode: "empty_catalog",
1027
1073
  };
1028
1074
  }
@@ -295,21 +295,28 @@ function syncChannels(
295
295
  .get();
296
296
 
297
297
  if (existing) {
298
+ // Preserve guardian blocks: if the channel is blocked, do not overwrite
299
+ // its status/policy — mirrors the guard in the cross-contact reassignment
300
+ // path so a blocked channel cannot be unblocked via a same-contact sync.
301
+ const isBlocked = existing.status === "blocked";
302
+
298
303
  const updateSet: Record<string, unknown> = {};
299
304
  if (ch.isPrimary !== undefined) updateSet.isPrimary = ch.isPrimary;
300
305
  if (ch.externalUserId !== undefined)
301
306
  updateSet.externalUserId = ch.externalUserId;
302
307
  if (ch.externalChatId !== undefined)
303
308
  updateSet.externalChatId = ch.externalChatId;
304
- if (ch.status !== undefined) updateSet.status = ch.status;
305
- if (ch.policy !== undefined) updateSet.policy = ch.policy;
309
+ if (!isBlocked) {
310
+ if (ch.status !== undefined) updateSet.status = ch.status;
311
+ if (ch.policy !== undefined) updateSet.policy = ch.policy;
312
+ if (ch.revokedReason !== undefined)
313
+ updateSet.revokedReason = ch.revokedReason;
314
+ if (ch.blockedReason !== undefined)
315
+ updateSet.blockedReason = ch.blockedReason;
316
+ }
306
317
  if (ch.verifiedAt !== undefined) updateSet.verifiedAt = ch.verifiedAt;
307
318
  if (ch.verifiedVia !== undefined) updateSet.verifiedVia = ch.verifiedVia;
308
319
  if (ch.inviteId !== undefined) updateSet.inviteId = ch.inviteId;
309
- if (ch.revokedReason !== undefined)
310
- updateSet.revokedReason = ch.revokedReason;
311
- if (ch.blockedReason !== undefined)
312
- updateSet.blockedReason = ch.blockedReason;
313
320
 
314
321
  if (Object.keys(updateSet).length > 0) {
315
322
  updateSet.updatedAt = now;
@@ -145,7 +145,6 @@ export function upsertContactChannel(params: {
145
145
  policy?: string;
146
146
  status?: string;
147
147
  inviteId?: string;
148
- sourceConversationId?: string;
149
148
  verifiedAt?: number;
150
149
  verifiedVia?: string;
151
150
  role?: ContactRole;
@@ -681,13 +681,24 @@ function countPersistedMessages(messages: Message[]): number {
681
681
  }).length;
682
682
  }
683
683
 
684
- /** A user message that contains ONLY tool_result blocks (no text or other content). */
684
+ function isSystemNoticeBlock(block: ContentBlock): boolean {
685
+ if (block.type !== "text") return false;
686
+ const text = (block as { text?: string }).text ?? "";
687
+ return (
688
+ text.startsWith("<system_notice>") && text.endsWith("</system_notice>")
689
+ );
690
+ }
691
+
692
+ /** A user message that contains ONLY tool_result blocks (no text or other content).
693
+ * System notice text blocks (retry nudges, progress checks) do not count as user content. */
685
694
  function isToolResultOnly(message: Message): boolean {
686
695
  return (
687
696
  message.content.length > 0 &&
688
697
  message.content.every(
689
698
  (block) =>
690
- block.type === "tool_result" || block.type === "web_search_tool_result",
699
+ block.type === "tool_result" ||
700
+ block.type === "web_search_tool_result" ||
701
+ isSystemNoticeBlock(block),
691
702
  )
692
703
  );
693
704
  }