vellum 0.0.16 → 0.2.0

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 (838) hide show
  1. package/.dockerignore +27 -0
  2. package/.env.example +22 -0
  3. package/Dockerfile +99 -0
  4. package/Dockerfile.sandbox +5 -0
  5. package/README.md +150 -3
  6. package/bun.lock +1768 -0
  7. package/bunfig.toml +2 -0
  8. package/docs/skills.md +158 -0
  9. package/drizzle/0000_dizzy_maggott.sql +301 -0
  10. package/drizzle/meta/0000_snapshot.json +1999 -0
  11. package/drizzle/meta/_journal.json +13 -0
  12. package/drizzle.config.ts +7 -0
  13. package/eslint.config.mjs +17 -0
  14. package/hook-templates/debug-prompt-logger/hook.json +7 -0
  15. package/hook-templates/debug-prompt-logger/run.sh +68 -0
  16. package/knip.json +9 -0
  17. package/package.json +60 -10
  18. package/scripts/ipc/check-contract-inventory.ts +104 -0
  19. package/scripts/ipc/check-swift-decoder-drift.ts +163 -0
  20. package/scripts/ipc/generate-swift.ts +492 -0
  21. package/scripts/test-filesystem-tools.sh +48 -0
  22. package/scripts/test.sh +122 -0
  23. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2079 -0
  24. package/src/__tests__/account-registry.test.ts +244 -0
  25. package/src/__tests__/active-skill-tools.test.ts +378 -0
  26. package/src/__tests__/agent-loop-thinking.test.ts +81 -0
  27. package/src/__tests__/agent-loop.test.ts +1135 -0
  28. package/src/__tests__/anthropic-provider.test.ts +778 -0
  29. package/src/__tests__/app-builder-tool-scripts.test.ts +290 -0
  30. package/src/__tests__/app-bundler.test.ts +313 -0
  31. package/src/__tests__/app-executors.test.ts +613 -0
  32. package/src/__tests__/app-open-proxy.test.ts +62 -0
  33. package/src/__tests__/asset-materialize-tool.test.ts +451 -0
  34. package/src/__tests__/asset-search-tool.test.ts +476 -0
  35. package/src/__tests__/assistant-attachment-directive.test.ts +401 -0
  36. package/src/__tests__/assistant-attachments.test.ts +437 -0
  37. package/src/__tests__/assistant-event-hub.test.ts +226 -0
  38. package/src/__tests__/assistant-event.test.ts +123 -0
  39. package/src/__tests__/attachments-store.test.ts +547 -0
  40. package/src/__tests__/attachments.test.ts +134 -0
  41. package/src/__tests__/audit-log-rotation.test.ts +154 -0
  42. package/src/__tests__/browser-fill-credential.test.ts +309 -0
  43. package/src/__tests__/browser-manager.test.ts +203 -0
  44. package/src/__tests__/browser-runtime-check.test.ts +55 -0
  45. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +67 -0
  46. package/src/__tests__/browser-skill-endstate.test.ts +198 -0
  47. package/src/__tests__/bundle-scanner.test.ts +313 -0
  48. package/src/__tests__/checker.test.ts +3856 -0
  49. package/src/__tests__/clarification-resolver.test.ts +159 -0
  50. package/src/__tests__/classifier.test.ts +67 -0
  51. package/src/__tests__/claude-code-skill-regression.test.ts +127 -0
  52. package/src/__tests__/claude-code-tool-profiles.test.ts +88 -0
  53. package/src/__tests__/cli-discover.test.ts +85 -0
  54. package/src/__tests__/cli.test.ts +81 -0
  55. package/src/__tests__/clipboard.test.ts +80 -0
  56. package/src/__tests__/commit-guarantee.test.ts +335 -0
  57. package/src/__tests__/computer-use-session-compaction.test.ts +132 -0
  58. package/src/__tests__/computer-use-session-lifecycle.test.ts +293 -0
  59. package/src/__tests__/computer-use-session-working-dir.test.ts +117 -0
  60. package/src/__tests__/computer-use-skill-baseline.test.ts +74 -0
  61. package/src/__tests__/computer-use-skill-endstate.test.ts +89 -0
  62. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +217 -0
  63. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +107 -0
  64. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +54 -0
  65. package/src/__tests__/config-schema.test.ts +720 -0
  66. package/src/__tests__/conflict-store.test.ts +329 -0
  67. package/src/__tests__/connection-policy.test.ts +102 -0
  68. package/src/__tests__/context-memory-e2e.test.ts +434 -0
  69. package/src/__tests__/context-token-estimator.test.ts +135 -0
  70. package/src/__tests__/context-window-manager.test.ts +376 -0
  71. package/src/__tests__/contradiction-checker.test.ts +216 -0
  72. package/src/__tests__/conversation-store.test.ts +614 -0
  73. package/src/__tests__/credential-broker-browser-fill.test.ts +517 -0
  74. package/src/__tests__/credential-broker-server-use.test.ts +554 -0
  75. package/src/__tests__/credential-broker.test.ts +167 -0
  76. package/src/__tests__/credential-host-pattern-match.test.ts +104 -0
  77. package/src/__tests__/credential-metadata-store.test.ts +779 -0
  78. package/src/__tests__/credential-policy-validate.test.ts +121 -0
  79. package/src/__tests__/credential-resolve.test.ts +328 -0
  80. package/src/__tests__/credential-security-e2e.test.ts +352 -0
  81. package/src/__tests__/credential-security-invariants.test.ts +563 -0
  82. package/src/__tests__/credential-selection.test.ts +354 -0
  83. package/src/__tests__/credential-vault.test.ts +852 -0
  84. package/src/__tests__/daemon-assistant-events.test.ts +164 -0
  85. package/src/__tests__/daemon-server-session-init.test.ts +522 -0
  86. package/src/__tests__/delete-managed-skill-tool.test.ts +97 -0
  87. package/src/__tests__/diff.test.ts +121 -0
  88. package/src/__tests__/domain-normalize.test.ts +112 -0
  89. package/src/__tests__/domain-policy.test.ts +124 -0
  90. package/src/__tests__/doordash-client.test.ts +186 -0
  91. package/src/__tests__/doordash-session.test.ts +143 -0
  92. package/src/__tests__/dynamic-page-surface.test.ts +91 -0
  93. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +132 -0
  94. package/src/__tests__/edit-engine.test.ts +180 -0
  95. package/src/__tests__/email-cli.test.ts +283 -0
  96. package/src/__tests__/encrypted-store.test.ts +332 -0
  97. package/src/__tests__/entity-extractor.test.ts +190 -0
  98. package/src/__tests__/ephemeral-permissions.test.ts +312 -0
  99. package/src/__tests__/evaluate-typescript-tool.test.ts +286 -0
  100. package/src/__tests__/event-bus.test.ts +222 -0
  101. package/src/__tests__/file-edit-tool.test.ts +122 -0
  102. package/src/__tests__/file-ops-service.test.ts +330 -0
  103. package/src/__tests__/file-read-tool.test.ts +75 -0
  104. package/src/__tests__/file-write-tool.test.ts +113 -0
  105. package/src/__tests__/fixtures/credential-security-fixtures.ts +181 -0
  106. package/src/__tests__/fixtures/media-reuse-fixtures.ts +126 -0
  107. package/src/__tests__/fixtures/mock-signup-server.ts +387 -0
  108. package/src/__tests__/fixtures/proxy-fixtures.ts +147 -0
  109. package/src/__tests__/fuzzy-match-property.test.ts +216 -0
  110. package/src/__tests__/fuzzy-match.test.ts +138 -0
  111. package/src/__tests__/gemini-image-service.test.ts +261 -0
  112. package/src/__tests__/gemini-provider.test.ts +651 -0
  113. package/src/__tests__/get-weather.test.ts +318 -0
  114. package/src/__tests__/gmail-integration.test.ts +73 -0
  115. package/src/__tests__/handlers-cu-observation-blob.test.ts +351 -0
  116. package/src/__tests__/handlers-ipc-blob-probe.test.ts +190 -0
  117. package/src/__tests__/handlers-slack-config.test.ts +199 -0
  118. package/src/__tests__/handlers-task-submit-slash.test.ts +38 -0
  119. package/src/__tests__/headless-browser-interactions.test.ts +536 -0
  120. package/src/__tests__/headless-browser-navigate.test.ts +211 -0
  121. package/src/__tests__/headless-browser-read-tools.test.ts +261 -0
  122. package/src/__tests__/headless-browser-snapshot.test.ts +185 -0
  123. package/src/__tests__/history-repair-observability.test.ts +56 -0
  124. package/src/__tests__/history-repair.test.ts +510 -0
  125. package/src/__tests__/home-base-bootstrap.test.ts +77 -0
  126. package/src/__tests__/hooks-blocking.test.ts +128 -0
  127. package/src/__tests__/hooks-cli.test.ts +144 -0
  128. package/src/__tests__/hooks-config.test.ts +93 -0
  129. package/src/__tests__/hooks-discovery.test.ts +199 -0
  130. package/src/__tests__/hooks-integration.test.ts +189 -0
  131. package/src/__tests__/hooks-manager.test.ts +187 -0
  132. package/src/__tests__/hooks-runner.test.ts +178 -0
  133. package/src/__tests__/hooks-settings.test.ts +154 -0
  134. package/src/__tests__/hooks-templates.test.ts +137 -0
  135. package/src/__tests__/hooks-ts-runner.test.ts +125 -0
  136. package/src/__tests__/hooks-watch.test.ts +100 -0
  137. package/src/__tests__/host-file-edit-tool.test.ts +104 -0
  138. package/src/__tests__/host-file-read-tool.test.ts +61 -0
  139. package/src/__tests__/host-file-write-tool.test.ts +77 -0
  140. package/src/__tests__/host-shell-tool.test.ts +311 -0
  141. package/src/__tests__/intent-routing.test.ts +255 -0
  142. package/src/__tests__/ipc-blob-store.test.ts +315 -0
  143. package/src/__tests__/ipc-contract-inventory.test.ts +54 -0
  144. package/src/__tests__/ipc-contract.test.ts +74 -0
  145. package/src/__tests__/ipc-protocol.test.ts +113 -0
  146. package/src/__tests__/ipc-snapshot.test.ts +1560 -0
  147. package/src/__tests__/ipc-validate.test.ts +357 -0
  148. package/src/__tests__/key-migration.test.ts +183 -0
  149. package/src/__tests__/keychain.test.ts +258 -0
  150. package/src/__tests__/llm-usage-store.test.ts +226 -0
  151. package/src/__tests__/managed-skill-lifecycle.test.ts +257 -0
  152. package/src/__tests__/managed-store.test.ts +608 -0
  153. package/src/__tests__/media-generate-image.test.ts +238 -0
  154. package/src/__tests__/media-reuse-story.e2e.test.ts +676 -0
  155. package/src/__tests__/media-visibility-policy.test.ts +141 -0
  156. package/src/__tests__/memory-context-benchmark.test.ts +235 -0
  157. package/src/__tests__/memory-lifecycle-e2e.test.ts +481 -0
  158. package/src/__tests__/memory-query-builder.test.ts +59 -0
  159. package/src/__tests__/memory-recall-quality.test.ts +846 -0
  160. package/src/__tests__/memory-regressions.experimental.test.ts +538 -0
  161. package/src/__tests__/memory-regressions.test.ts +4238 -0
  162. package/src/__tests__/memory-retrieval-budget.test.ts +49 -0
  163. package/src/__tests__/migration-cli-flows.test.ts +169 -0
  164. package/src/__tests__/migration-ordering.test.ts +249 -0
  165. package/src/__tests__/mock-signup-server.test.ts +528 -0
  166. package/src/__tests__/onboarding-starter-tasks.test.ts +166 -0
  167. package/src/__tests__/onboarding-template-contract.test.ts +58 -0
  168. package/src/__tests__/openai-provider.test.ts +753 -0
  169. package/src/__tests__/parser.test.ts +472 -0
  170. package/src/__tests__/path-classifier.test.ts +73 -0
  171. package/src/__tests__/path-policy.test.ts +435 -0
  172. package/src/__tests__/platform-move-helper.test.ts +99 -0
  173. package/src/__tests__/platform-socket-path.test.ts +52 -0
  174. package/src/__tests__/platform-workspace-migration.test.ts +1000 -0
  175. package/src/__tests__/platform.test.ts +131 -0
  176. package/src/__tests__/prebuilt-home-base-seed.test.ts +71 -0
  177. package/src/__tests__/pricing.test.ts +256 -0
  178. package/src/__tests__/profile-compiler.test.ts +373 -0
  179. package/src/__tests__/provider-registry-ollama.test.ts +16 -0
  180. package/src/__tests__/proxy-approval-callback.test.ts +601 -0
  181. package/src/__tests__/ratelimit.test.ts +297 -0
  182. package/src/__tests__/registry.test.ts +487 -0
  183. package/src/__tests__/reminder-store.test.ts +220 -0
  184. package/src/__tests__/reminder.test.ts +263 -0
  185. package/src/__tests__/request-file-tool.test.ts +158 -0
  186. package/src/__tests__/run-orchestrator.test.ts +200 -0
  187. package/src/__tests__/runtime-attachment-metadata.test.ts +190 -0
  188. package/src/__tests__/runtime-runs-http.test.ts +451 -0
  189. package/src/__tests__/runtime-runs.test.ts +273 -0
  190. package/src/__tests__/sandbox-diagnostics.test.ts +408 -0
  191. package/src/__tests__/sandbox-host-parity.test.ts +950 -0
  192. package/src/__tests__/scaffold-managed-skill-tool.test.ts +253 -0
  193. package/src/__tests__/script-proxy-certs.test.ts +90 -0
  194. package/src/__tests__/script-proxy-connect-tunnel.test.ts +177 -0
  195. package/src/__tests__/script-proxy-decision-trace.test.ts +156 -0
  196. package/src/__tests__/script-proxy-http-forwarder.test.ts +281 -0
  197. package/src/__tests__/script-proxy-injection-runtime.test.ts +401 -0
  198. package/src/__tests__/script-proxy-mitm-handler.test.ts +407 -0
  199. package/src/__tests__/script-proxy-policy-runtime.test.ts +287 -0
  200. package/src/__tests__/script-proxy-policy.test.ts +310 -0
  201. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +135 -0
  202. package/src/__tests__/script-proxy-router.test.ts +180 -0
  203. package/src/__tests__/script-proxy-session-manager.test.ts +382 -0
  204. package/src/__tests__/script-proxy-session-runtime.test.ts +113 -0
  205. package/src/__tests__/secret-allowlist.test.ts +229 -0
  206. package/src/__tests__/secret-ingress-handler.test.ts +99 -0
  207. package/src/__tests__/secret-onetime-send.test.ts +130 -0
  208. package/src/__tests__/secret-prompt-log-hygiene.test.ts +106 -0
  209. package/src/__tests__/secret-response-routing.test.ts +93 -0
  210. package/src/__tests__/secret-scanner-executor.test.ts +348 -0
  211. package/src/__tests__/secret-scanner.test.ts +857 -0
  212. package/src/__tests__/secure-keys.test.ts +323 -0
  213. package/src/__tests__/server-history-render.test.ts +430 -0
  214. package/src/__tests__/session-abort-tool-results.test.ts +240 -0
  215. package/src/__tests__/session-conflict-gate.test.ts +697 -0
  216. package/src/__tests__/session-error.test.ts +341 -0
  217. package/src/__tests__/session-evictor.test.ts +188 -0
  218. package/src/__tests__/session-load-history-repair.test.ts +222 -0
  219. package/src/__tests__/session-pre-run-repair.test.ts +213 -0
  220. package/src/__tests__/session-profile-injection.test.ts +444 -0
  221. package/src/__tests__/session-provider-retry-repair.test.ts +306 -0
  222. package/src/__tests__/session-queue.test.ts +1462 -0
  223. package/src/__tests__/session-runtime-assembly.test.ts +315 -0
  224. package/src/__tests__/session-runtime-workspace.test.ts +183 -0
  225. package/src/__tests__/session-skill-tools.test.ts +2431 -0
  226. package/src/__tests__/session-slash-known.test.ts +368 -0
  227. package/src/__tests__/session-slash-queue.test.ts +288 -0
  228. package/src/__tests__/session-slash-unknown.test.ts +271 -0
  229. package/src/__tests__/session-tool-setup-app-refresh.test.ts +473 -0
  230. package/src/__tests__/session-tool-setup-memory-scope.test.ts +140 -0
  231. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +140 -0
  232. package/src/__tests__/session-undo.test.ts +75 -0
  233. package/src/__tests__/session-workspace-cache-state.test.ts +246 -0
  234. package/src/__tests__/session-workspace-injection.test.ts +327 -0
  235. package/src/__tests__/session-workspace-tool-tracking.test.ts +240 -0
  236. package/src/__tests__/shared-filesystem-errors.test.ts +78 -0
  237. package/src/__tests__/shell-credential-ref.test.ts +187 -0
  238. package/src/__tests__/shell-parser-fuzz.test.ts +544 -0
  239. package/src/__tests__/shell-parser-property.test.ts +433 -0
  240. package/src/__tests__/shell-tool-proxy-mode.test.ts +272 -0
  241. package/src/__tests__/signup-e2e.test.ts +352 -0
  242. package/src/__tests__/size-guard.test.ts +117 -0
  243. package/src/__tests__/skill-include-graph.test.ts +303 -0
  244. package/src/__tests__/skill-load-tool.test.ts +409 -0
  245. package/src/__tests__/skill-script-runner-host.test.ts +489 -0
  246. package/src/__tests__/skill-script-runner-sandbox.test.ts +349 -0
  247. package/src/__tests__/skill-tool-factory.test.ts +252 -0
  248. package/src/__tests__/skill-tool-manifest.test.ts +658 -0
  249. package/src/__tests__/skill-version-hash.test.ts +182 -0
  250. package/src/__tests__/skills.test.ts +597 -0
  251. package/src/__tests__/slash-commands-catalog.test.ts +86 -0
  252. package/src/__tests__/slash-commands-parser.test.ts +119 -0
  253. package/src/__tests__/slash-commands-resolver.test.ts +193 -0
  254. package/src/__tests__/slash-commands-rewrite.test.ts +39 -0
  255. package/src/__tests__/starter-bundle.test.ts +136 -0
  256. package/src/__tests__/starter-task-flow.test.ts +143 -0
  257. package/src/__tests__/subagent-manager-notify.test.ts +372 -0
  258. package/src/__tests__/subagent-tools.test.ts +118 -0
  259. package/src/__tests__/subagent-types.test.ts +78 -0
  260. package/src/__tests__/swarm-orchestrator.test.ts +428 -0
  261. package/src/__tests__/swarm-plan-validator.test.ts +330 -0
  262. package/src/__tests__/swarm-recursion.test.ts +165 -0
  263. package/src/__tests__/swarm-router-planner.test.ts +208 -0
  264. package/src/__tests__/swarm-session-integration.test.ts +274 -0
  265. package/src/__tests__/swarm-tool.test.ts +145 -0
  266. package/src/__tests__/swarm-worker-backend.test.ts +129 -0
  267. package/src/__tests__/swarm-worker-runner.test.ts +272 -0
  268. package/src/__tests__/system-prompt.test.ts +461 -0
  269. package/src/__tests__/task-compiler.test.ts +283 -0
  270. package/src/__tests__/task-runner.test.ts +215 -0
  271. package/src/__tests__/task-scheduler.test.ts +216 -0
  272. package/src/__tests__/task-tools.test.ts +602 -0
  273. package/src/__tests__/terminal-sandbox-docker.test.ts +1064 -0
  274. package/src/__tests__/terminal-sandbox.integration.test.ts +178 -0
  275. package/src/__tests__/terminal-sandbox.test.ts +202 -0
  276. package/src/__tests__/test-support/browser-skill-harness.ts +90 -0
  277. package/src/__tests__/test-support/computer-use-skill-harness.ts +45 -0
  278. package/src/__tests__/tool-audit-listener.test.ts +112 -0
  279. package/src/__tests__/tool-domain-event-publisher.test.ts +251 -0
  280. package/src/__tests__/tool-executor-lifecycle-events.test.ts +516 -0
  281. package/src/__tests__/tool-executor-redaction.test.ts +289 -0
  282. package/src/__tests__/tool-executor.test.ts +1971 -0
  283. package/src/__tests__/tool-metrics-listener.test.ts +225 -0
  284. package/src/__tests__/tool-notification-listener.test.ts +49 -0
  285. package/src/__tests__/tool-policy.test.ts +54 -0
  286. package/src/__tests__/tool-profiling-listener.test.ts +268 -0
  287. package/src/__tests__/tool-result-truncation.test.ts +217 -0
  288. package/src/__tests__/tool-trace-listener.test.ts +226 -0
  289. package/src/__tests__/top-level-renderer.test.ts +121 -0
  290. package/src/__tests__/top-level-scanner.test.ts +141 -0
  291. package/src/__tests__/trace-emitter.test.ts +173 -0
  292. package/src/__tests__/trust-store.test.ts +2030 -0
  293. package/src/__tests__/turn-commit.test.ts +219 -0
  294. package/src/__tests__/url-safety.test.ts +418 -0
  295. package/src/__tests__/weather-skill-regression.test.ts +225 -0
  296. package/src/__tests__/web-fetch.test.ts +869 -0
  297. package/src/__tests__/web-search.test.ts +584 -0
  298. package/src/__tests__/workspace-git-service.test.ts +750 -0
  299. package/src/__tests__/workspace-heartbeat-service.test.ts +347 -0
  300. package/src/__tests__/workspace-lifecycle.test.ts +292 -0
  301. package/src/agent/attachments.ts +35 -0
  302. package/src/agent/loop.ts +500 -0
  303. package/src/agent/message-types.ts +17 -0
  304. package/src/autonomy/autonomy-resolver.ts +60 -0
  305. package/src/autonomy/autonomy-store.ts +122 -0
  306. package/src/autonomy/disposition-mapper.ts +31 -0
  307. package/src/autonomy/index.ts +11 -0
  308. package/src/autonomy/types.ts +39 -0
  309. package/src/bundler/app-bundler.ts +274 -0
  310. package/src/bundler/bundle-scanner.ts +535 -0
  311. package/src/bundler/bundle-signer.ts +124 -0
  312. package/src/bundler/manifest.ts +21 -0
  313. package/src/bundler/signature-verifier.ts +184 -0
  314. package/src/cli/autonomy.ts +188 -0
  315. package/src/cli/contacts.ts +149 -0
  316. package/src/cli/doordash.ts +824 -0
  317. package/src/cli/email-guardrails.ts +200 -0
  318. package/src/cli/email.ts +405 -0
  319. package/src/cli/main-screen.tsx +155 -0
  320. package/src/cli.ts +935 -0
  321. package/src/config/bundled-skills/.gitkeep +0 -0
  322. package/src/config/bundled-skills/agentmail/SKILL.md +128 -0
  323. package/src/config/bundled-skills/agentmail/icon.svg +21 -0
  324. package/src/config/bundled-skills/app-builder/SKILL.md +1348 -0
  325. package/src/config/bundled-skills/app-builder/TOOLS.json +279 -0
  326. package/src/config/bundled-skills/app-builder/icon.svg +9 -0
  327. package/src/config/bundled-skills/app-builder/tools/app-create.ts +15 -0
  328. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +10 -0
  329. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +11 -0
  330. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +10 -0
  331. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +18 -0
  332. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +11 -0
  333. package/src/config/bundled-skills/app-builder/tools/app-list.ts +10 -0
  334. package/src/config/bundled-skills/app-builder/tools/app-query.ts +10 -0
  335. package/src/config/bundled-skills/app-builder/tools/app-update.ts +20 -0
  336. package/src/config/bundled-skills/browser/SKILL.md +28 -0
  337. package/src/config/bundled-skills/browser/TOOLS.json +234 -0
  338. package/src/config/bundled-skills/browser/tools/browser-click.ts +9 -0
  339. package/src/config/bundled-skills/browser/tools/browser-close.ts +9 -0
  340. package/src/config/bundled-skills/browser/tools/browser-extract.ts +9 -0
  341. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +9 -0
  342. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +9 -0
  343. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +9 -0
  344. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +9 -0
  345. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +9 -0
  346. package/src/config/bundled-skills/browser/tools/browser-type.ts +9 -0
  347. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +9 -0
  348. package/src/config/bundled-skills/claude-code/SKILL.md +50 -0
  349. package/src/config/bundled-skills/claude-code/TOOLS.json +40 -0
  350. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +9 -0
  351. package/src/config/bundled-skills/computer-use/SKILL.md +17 -0
  352. package/src/config/bundled-skills/computer-use/TOOLS.json +326 -0
  353. package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +9 -0
  354. package/src/config/bundled-skills/computer-use/tools/computer-use-done.ts +9 -0
  355. package/src/config/bundled-skills/computer-use/tools/computer-use-double-click.ts +9 -0
  356. package/src/config/bundled-skills/computer-use/tools/computer-use-drag.ts +9 -0
  357. package/src/config/bundled-skills/computer-use/tools/computer-use-key.ts +9 -0
  358. package/src/config/bundled-skills/computer-use/tools/computer-use-open-app.ts +9 -0
  359. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +9 -0
  360. package/src/config/bundled-skills/computer-use/tools/computer-use-respond.ts +9 -0
  361. package/src/config/bundled-skills/computer-use/tools/computer-use-right-click.ts +9 -0
  362. package/src/config/bundled-skills/computer-use/tools/computer-use-run-applescript.ts +9 -0
  363. package/src/config/bundled-skills/computer-use/tools/computer-use-scroll.ts +9 -0
  364. package/src/config/bundled-skills/computer-use/tools/computer-use-type-text.ts +9 -0
  365. package/src/config/bundled-skills/computer-use/tools/computer-use-wait.ts +9 -0
  366. package/src/config/bundled-skills/google-calendar/SKILL.md +51 -0
  367. package/src/config/bundled-skills/google-calendar/TOOLS.json +108 -0
  368. package/src/config/bundled-skills/google-calendar/calendar-client.ts +165 -0
  369. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +21 -0
  370. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +42 -0
  371. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +13 -0
  372. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +30 -0
  373. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +41 -0
  374. package/src/config/bundled-skills/google-calendar/tools/shared.ts +18 -0
  375. package/src/config/bundled-skills/google-calendar/types.ts +97 -0
  376. package/src/config/bundled-skills/image-studio/SKILL.md +32 -0
  377. package/src/config/bundled-skills/image-studio/TOOLS.json +42 -0
  378. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +137 -0
  379. package/src/config/bundled-skills/messaging/SKILL.md +126 -0
  380. package/src/config/bundled-skills/messaging/TOOLS.json +357 -0
  381. package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +23 -0
  382. package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +23 -0
  383. package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +25 -0
  384. package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +26 -0
  385. package/src/config/bundled-skills/messaging/tools/gmail-label.ts +25 -0
  386. package/src/config/bundled-skills/messaging/tools/gmail-trash.ts +23 -0
  387. package/src/config/bundled-skills/messaging/tools/gmail-unsubscribe.ts +84 -0
  388. package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +18 -0
  389. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +124 -0
  390. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +16 -0
  391. package/src/config/bundled-skills/messaging/tools/messaging-draft.ts +49 -0
  392. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +21 -0
  393. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +25 -0
  394. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +28 -0
  395. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +29 -0
  396. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +22 -0
  397. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +27 -0
  398. package/src/config/bundled-skills/messaging/tools/shared.ts +71 -0
  399. package/src/config/bundled-skills/messaging/tools/slack-add-reaction.ts +25 -0
  400. package/src/config/bundled-skills/messaging/tools/slack-leave-channel.ts +23 -0
  401. package/src/config/bundled-skills/self-upgrade/SKILL.md +74 -0
  402. package/src/config/bundled-skills/start-the-day/SKILL.md +70 -0
  403. package/src/config/bundled-skills/start-the-day/icon.svg +13 -0
  404. package/src/config/bundled-skills/weather/SKILL.md +37 -0
  405. package/src/config/bundled-skills/weather/TOOLS.json +32 -0
  406. package/src/config/bundled-skills/weather/icon.svg +24 -0
  407. package/src/config/bundled-skills/weather/tools/get-weather.ts +9 -0
  408. package/src/config/computer-use-prompt.ts +97 -0
  409. package/src/config/defaults.ts +186 -0
  410. package/src/config/loader.ts +336 -0
  411. package/src/config/schema.ts +1004 -0
  412. package/src/config/skill-state.ts +95 -0
  413. package/src/config/skills.ts +972 -0
  414. package/src/config/system-prompt.ts +927 -0
  415. package/src/config/templates/BOOTSTRAP.md +70 -0
  416. package/src/config/templates/IDENTITY.md +18 -0
  417. package/src/config/templates/LOOKS.md +25 -0
  418. package/src/config/templates/SOUL.md +37 -0
  419. package/src/config/templates/USER.md +19 -0
  420. package/src/config/types.ts +32 -0
  421. package/src/config/vellum-skills/deploy-fullstack-vercel/SKILL.md +179 -0
  422. package/src/config/vellum-skills/document-writer/SKILL.md +195 -0
  423. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +194 -0
  424. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +147 -0
  425. package/src/config/vellum-skills/telegram-setup/SKILL.md +105 -0
  426. package/src/contacts/contact-store.ts +410 -0
  427. package/src/contacts/index.ts +11 -0
  428. package/src/contacts/types.ts +28 -0
  429. package/src/context/token-estimator.ts +108 -0
  430. package/src/context/tool-result-truncation.ts +128 -0
  431. package/src/context/window-manager.ts +531 -0
  432. package/src/daemon/assistant-attachments.ts +679 -0
  433. package/src/daemon/classifier.ts +108 -0
  434. package/src/daemon/computer-use-session.ts +900 -0
  435. package/src/daemon/connection-policy.ts +41 -0
  436. package/src/daemon/handlers/apps.ts +446 -0
  437. package/src/daemon/handlers/computer-use.ts +181 -0
  438. package/src/daemon/handlers/config.ts +434 -0
  439. package/src/daemon/handlers/diagnostics.ts +334 -0
  440. package/src/daemon/handlers/documents.ts +184 -0
  441. package/src/daemon/handlers/home-base.ts +73 -0
  442. package/src/daemon/handlers/index.ts +355 -0
  443. package/src/daemon/handlers/misc.ts +323 -0
  444. package/src/daemon/handlers/open-bundle-handler.ts +80 -0
  445. package/src/daemon/handlers/publish.ts +182 -0
  446. package/src/daemon/handlers/sessions.ts +486 -0
  447. package/src/daemon/handlers/shared.ts +533 -0
  448. package/src/daemon/handlers/skills.ts +487 -0
  449. package/src/daemon/handlers/subagents.ts +122 -0
  450. package/src/daemon/handlers/work-items.ts +176 -0
  451. package/src/daemon/handlers.ts +17 -0
  452. package/src/daemon/history-repair.ts +214 -0
  453. package/src/daemon/ipc-blob-store.ts +231 -0
  454. package/src/daemon/ipc-contract-inventory.json +407 -0
  455. package/src/daemon/ipc-contract-inventory.ts +126 -0
  456. package/src/daemon/ipc-contract.ts +2102 -0
  457. package/src/daemon/ipc-protocol.ts +70 -0
  458. package/src/daemon/ipc-validate.ts +171 -0
  459. package/src/daemon/lifecycle.ts +503 -0
  460. package/src/daemon/main.ts +15 -0
  461. package/src/daemon/media-visibility-policy.ts +57 -0
  462. package/src/daemon/ride-shotgun-handler.ts +244 -0
  463. package/src/daemon/server.ts +1085 -0
  464. package/src/daemon/session-attachments.ts +173 -0
  465. package/src/daemon/session-conflict-gate.ts +219 -0
  466. package/src/daemon/session-dynamic-profile.ts +63 -0
  467. package/src/daemon/session-error.ts +269 -0
  468. package/src/daemon/session-evictor.ts +196 -0
  469. package/src/daemon/session-history.ts +437 -0
  470. package/src/daemon/session-memory.ts +212 -0
  471. package/src/daemon/session-process.ts +264 -0
  472. package/src/daemon/session-queue-manager.ts +81 -0
  473. package/src/daemon/session-runtime-assembly.ts +395 -0
  474. package/src/daemon/session-skill-tools.ts +237 -0
  475. package/src/daemon/session-slash.ts +302 -0
  476. package/src/daemon/session-surfaces.ts +624 -0
  477. package/src/daemon/session-tool-setup.ts +286 -0
  478. package/src/daemon/session-usage.ts +74 -0
  479. package/src/daemon/session-workspace.ts +19 -0
  480. package/src/daemon/session.ts +1651 -0
  481. package/src/daemon/trace-emitter.ts +82 -0
  482. package/src/daemon/watch-handler.ts +274 -0
  483. package/src/doordash/client.ts +905 -0
  484. package/src/doordash/queries.ts +1312 -0
  485. package/src/doordash/query-extractor.ts +93 -0
  486. package/src/doordash/session.ts +82 -0
  487. package/src/email/provider.ts +117 -0
  488. package/src/email/providers/agentmail.ts +317 -0
  489. package/src/email/providers/index.ts +58 -0
  490. package/src/email/service.ts +303 -0
  491. package/src/email/types.ts +126 -0
  492. package/src/events/bus.ts +157 -0
  493. package/src/events/domain-events.ts +83 -0
  494. package/src/events/index.ts +18 -0
  495. package/src/events/tool-audit-listener.ts +80 -0
  496. package/src/events/tool-domain-event-publisher.ts +111 -0
  497. package/src/events/tool-metrics-listener.ts +159 -0
  498. package/src/events/tool-notification-listener.ts +17 -0
  499. package/src/events/tool-profiling-listener.ts +158 -0
  500. package/src/events/tool-trace-listener.ts +75 -0
  501. package/src/export/formatter.ts +96 -0
  502. package/src/followups/followup-store.ts +166 -0
  503. package/src/followups/index.ts +10 -0
  504. package/src/followups/types.ts +23 -0
  505. package/src/gallery/default-gallery.ts +795 -0
  506. package/src/gallery/gallery-manifest.ts +24 -0
  507. package/src/home-base/app-link-store.ts +82 -0
  508. package/src/home-base/bootstrap.ts +66 -0
  509. package/src/home-base/prebuilt/index.html +662 -0
  510. package/src/home-base/prebuilt/seed-metadata.json +21 -0
  511. package/src/home-base/prebuilt/seed.ts +101 -0
  512. package/src/home-base/prebuilt-home-base-updater.ts +30 -0
  513. package/src/hooks/cli.ts +163 -0
  514. package/src/hooks/config.ts +88 -0
  515. package/src/hooks/discovery.ts +110 -0
  516. package/src/hooks/manager.ts +128 -0
  517. package/src/hooks/runner.ts +123 -0
  518. package/src/hooks/templates.ts +52 -0
  519. package/src/hooks/types.ts +72 -0
  520. package/src/index.ts +1194 -0
  521. package/src/instrument.ts +60 -0
  522. package/src/logfire.ts +99 -0
  523. package/src/media/gemini-image-service.ts +136 -0
  524. package/src/memory/account-store.ts +108 -0
  525. package/src/memory/admin.ts +211 -0
  526. package/src/memory/app-store.ts +556 -0
  527. package/src/memory/attachments-store.ts +453 -0
  528. package/src/memory/channel-delivery-store.ts +368 -0
  529. package/src/memory/checkpoints.ts +52 -0
  530. package/src/memory/clarification-resolver.ts +297 -0
  531. package/src/memory/conflict-store.ts +342 -0
  532. package/src/memory/contradiction-checker.ts +329 -0
  533. package/src/memory/conversation-key-store.ts +127 -0
  534. package/src/memory/conversation-store.ts +469 -0
  535. package/src/memory/db.ts +1105 -0
  536. package/src/memory/embedding-backend.ts +229 -0
  537. package/src/memory/embedding-gemini.ts +52 -0
  538. package/src/memory/embedding-local.ts +75 -0
  539. package/src/memory/embedding-ollama.ts +55 -0
  540. package/src/memory/embedding-openai.ts +25 -0
  541. package/src/memory/entity-extractor.ts +471 -0
  542. package/src/memory/fingerprint.ts +20 -0
  543. package/src/memory/indexer.ts +156 -0
  544. package/src/memory/items-extractor.ts +460 -0
  545. package/src/memory/job-handlers/backfill.ts +139 -0
  546. package/src/memory/job-handlers/cleanup.ts +58 -0
  547. package/src/memory/job-handlers/conflict.ts +99 -0
  548. package/src/memory/job-handlers/embedding.ts +61 -0
  549. package/src/memory/job-handlers/extraction.ts +123 -0
  550. package/src/memory/job-handlers/index-maintenance.ts +54 -0
  551. package/src/memory/job-handlers/summarization.ts +286 -0
  552. package/src/memory/job-utils.ts +170 -0
  553. package/src/memory/jobs-store.ts +400 -0
  554. package/src/memory/jobs-worker.ts +274 -0
  555. package/src/memory/llm-request-log-store.ts +45 -0
  556. package/src/memory/llm-usage-store.ts +62 -0
  557. package/src/memory/message-content.ts +54 -0
  558. package/src/memory/profile-compiler.ts +160 -0
  559. package/src/memory/published-pages-store.ts +137 -0
  560. package/src/memory/qdrant-client.ts +366 -0
  561. package/src/memory/qdrant-manager.ts +242 -0
  562. package/src/memory/query-builder.ts +45 -0
  563. package/src/memory/retrieval-budget.ts +30 -0
  564. package/src/memory/retriever.ts +653 -0
  565. package/src/memory/runs-store.ts +211 -0
  566. package/src/memory/schema.ts +529 -0
  567. package/src/memory/search/entity.ts +298 -0
  568. package/src/memory/search/formatting.ts +207 -0
  569. package/src/memory/search/lexical.ts +227 -0
  570. package/src/memory/search/ranking.ts +401 -0
  571. package/src/memory/search/semantic.ts +121 -0
  572. package/src/memory/search/types.ts +137 -0
  573. package/src/memory/segmenter.ts +68 -0
  574. package/src/memory/shared-app-links-store.ts +138 -0
  575. package/src/memory/tool-usage-store.ts +62 -0
  576. package/src/messaging/activity-analyzer.ts +76 -0
  577. package/src/messaging/draft-store.ts +88 -0
  578. package/src/messaging/index.ts +3 -0
  579. package/src/messaging/provider-types.ts +80 -0
  580. package/src/messaging/provider.ts +43 -0
  581. package/src/messaging/providers/gmail/adapter.ts +193 -0
  582. package/src/messaging/providers/gmail/client.ts +204 -0
  583. package/src/messaging/providers/gmail/types.ts +90 -0
  584. package/src/messaging/providers/slack/adapter.ts +202 -0
  585. package/src/messaging/providers/slack/client.ts +198 -0
  586. package/src/messaging/providers/slack/types.ts +119 -0
  587. package/src/messaging/registry.ts +34 -0
  588. package/src/messaging/style-analyzer.ts +158 -0
  589. package/src/messaging/thread-summarizer.ts +310 -0
  590. package/src/messaging/triage-engine.ts +321 -0
  591. package/src/messaging/types.ts +55 -0
  592. package/src/permissions/checker.ts +636 -0
  593. package/src/permissions/defaults.ts +243 -0
  594. package/src/permissions/prompter.ts +102 -0
  595. package/src/permissions/secret-prompter.ts +114 -0
  596. package/src/permissions/trust-store.ts +584 -0
  597. package/src/permissions/types.ts +62 -0
  598. package/src/playbooks/index.ts +2 -0
  599. package/src/playbooks/playbook-compiler.ts +90 -0
  600. package/src/playbooks/types.ts +55 -0
  601. package/src/providers/anthropic/client.ts +751 -0
  602. package/src/providers/failover.ts +129 -0
  603. package/src/providers/fireworks/client.ts +20 -0
  604. package/src/providers/gemini/client.ts +285 -0
  605. package/src/providers/ollama/client.ts +30 -0
  606. package/src/providers/openai/client.ts +337 -0
  607. package/src/providers/ratelimit.ts +93 -0
  608. package/src/providers/registry.ts +138 -0
  609. package/src/providers/retry.ts +106 -0
  610. package/src/providers/stream-timeout.ts +38 -0
  611. package/src/providers/types.ts +109 -0
  612. package/src/runtime/assistant-event-hub.ts +120 -0
  613. package/src/runtime/assistant-event.ts +82 -0
  614. package/src/runtime/http-server.ts +478 -0
  615. package/src/runtime/http-types.ts +68 -0
  616. package/src/runtime/routes/app-routes.ts +174 -0
  617. package/src/runtime/routes/attachment-routes.ts +134 -0
  618. package/src/runtime/routes/channel-routes.ts +342 -0
  619. package/src/runtime/routes/conversation-routes.ts +349 -0
  620. package/src/runtime/routes/run-routes.ts +223 -0
  621. package/src/runtime/routes/secret-routes.ts +76 -0
  622. package/src/runtime/run-orchestrator.ts +206 -0
  623. package/src/schedule/schedule-store.ts +452 -0
  624. package/src/schedule/scheduler.ts +168 -0
  625. package/src/security/encrypted-store.ts +238 -0
  626. package/src/security/keychain.ts +252 -0
  627. package/src/security/oauth2.ts +241 -0
  628. package/src/security/redaction.ts +89 -0
  629. package/src/security/secret-allowlist.ts +118 -0
  630. package/src/security/secret-ingress.ts +57 -0
  631. package/src/security/secret-scanner.ts +543 -0
  632. package/src/security/secure-keys.ts +180 -0
  633. package/src/security/token-manager.ts +141 -0
  634. package/src/services/published-app-updater.ts +69 -0
  635. package/src/services/vercel-deploy.ts +73 -0
  636. package/src/skills/active-skill-tools.ts +81 -0
  637. package/src/skills/clawhub.ts +414 -0
  638. package/src/skills/include-graph.ts +146 -0
  639. package/src/skills/managed-store.ts +233 -0
  640. package/src/skills/path-classifier.ts +128 -0
  641. package/src/skills/slash-commands.ts +174 -0
  642. package/src/skills/tool-manifest.ts +165 -0
  643. package/src/skills/version-hash.ts +110 -0
  644. package/src/slack/slack-webhook.ts +61 -0
  645. package/src/subagent/index.ts +19 -0
  646. package/src/subagent/manager.ts +477 -0
  647. package/src/subagent/types.ts +69 -0
  648. package/src/swarm/backend-claude-code.ts +90 -0
  649. package/src/swarm/index.ts +44 -0
  650. package/src/swarm/limits.ts +37 -0
  651. package/src/swarm/orchestrator.ts +279 -0
  652. package/src/swarm/plan-validator.ts +151 -0
  653. package/src/swarm/router-planner.ts +100 -0
  654. package/src/swarm/router-prompts.ts +36 -0
  655. package/src/swarm/synthesizer.ts +62 -0
  656. package/src/swarm/types.ts +62 -0
  657. package/src/swarm/worker-backend.ts +121 -0
  658. package/src/swarm/worker-prompts.ts +78 -0
  659. package/src/swarm/worker-runner.ts +164 -0
  660. package/src/tasks/SPEC.md +133 -0
  661. package/src/tasks/candidate-store.ts +86 -0
  662. package/src/tasks/ephemeral-permissions.ts +41 -0
  663. package/src/tasks/task-compiler.ts +198 -0
  664. package/src/tasks/task-runner.ts +85 -0
  665. package/src/tasks/task-scheduler.ts +20 -0
  666. package/src/tasks/task-store.ts +127 -0
  667. package/src/tools/apps/definitions.ts +59 -0
  668. package/src/tools/apps/executors.ts +313 -0
  669. package/src/tools/apps/open-proxy.ts +43 -0
  670. package/src/tools/apps/registry.ts +16 -0
  671. package/src/tools/assets/materialize.ts +218 -0
  672. package/src/tools/assets/search.ts +396 -0
  673. package/src/tools/browser/__tests__/auth-cache.test.ts +219 -0
  674. package/src/tools/browser/__tests__/auth-detector.test.ts +362 -0
  675. package/src/tools/browser/__tests__/jit-auth.test.ts +189 -0
  676. package/src/tools/browser/auth-cache.ts +149 -0
  677. package/src/tools/browser/auth-detector.ts +347 -0
  678. package/src/tools/browser/browser-execution.ts +979 -0
  679. package/src/tools/browser/browser-handoff.ts +79 -0
  680. package/src/tools/browser/browser-manager.ts +715 -0
  681. package/src/tools/browser/browser-screencast.ts +217 -0
  682. package/src/tools/browser/headless-browser.ts +450 -0
  683. package/src/tools/browser/jit-auth.ts +51 -0
  684. package/src/tools/browser/network-recorder.ts +348 -0
  685. package/src/tools/browser/network-recording-types.ts +49 -0
  686. package/src/tools/browser/recording-store.ts +49 -0
  687. package/src/tools/browser/runtime-check.ts +43 -0
  688. package/src/tools/claude-code/claude-code.ts +232 -0
  689. package/src/tools/computer-use/definitions.ts +443 -0
  690. package/src/tools/computer-use/registry.ts +22 -0
  691. package/src/tools/computer-use/request-computer-control.ts +53 -0
  692. package/src/tools/computer-use/skill-proxy-bridge.ts +28 -0
  693. package/src/tools/contacts/contact-merge.ts +87 -0
  694. package/src/tools/contacts/contact-search.ts +102 -0
  695. package/src/tools/contacts/contact-upsert.ts +137 -0
  696. package/src/tools/contacts/index.ts +4 -0
  697. package/src/tools/credentials/account-registry.ts +127 -0
  698. package/src/tools/credentials/broker-types.ts +107 -0
  699. package/src/tools/credentials/broker.ts +372 -0
  700. package/src/tools/credentials/domain-policy.ts +51 -0
  701. package/src/tools/credentials/host-pattern-match.ts +60 -0
  702. package/src/tools/credentials/metadata-store.ts +335 -0
  703. package/src/tools/credentials/policy-types.ts +52 -0
  704. package/src/tools/credentials/policy-validate.ts +80 -0
  705. package/src/tools/credentials/resolve.ts +122 -0
  706. package/src/tools/credentials/selection.ts +159 -0
  707. package/src/tools/credentials/tool-policy.ts +25 -0
  708. package/src/tools/credentials/vault.ts +641 -0
  709. package/src/tools/document/document-tool.ts +165 -0
  710. package/src/tools/document/editor-template.ts +237 -0
  711. package/src/tools/document/index.ts +5 -0
  712. package/src/tools/executor.ts +825 -0
  713. package/src/tools/filesystem/edit.ts +127 -0
  714. package/src/tools/filesystem/fuzzy-match.ts +202 -0
  715. package/src/tools/filesystem/read.ts +71 -0
  716. package/src/tools/filesystem/view-image.ts +199 -0
  717. package/src/tools/filesystem/write.ts +79 -0
  718. package/src/tools/followups/followup_create.ts +118 -0
  719. package/src/tools/followups/followup_list.ts +100 -0
  720. package/src/tools/followups/followup_resolve.ts +91 -0
  721. package/src/tools/followups/index.ts +3 -0
  722. package/src/tools/host-filesystem/edit.ts +125 -0
  723. package/src/tools/host-filesystem/read.ts +80 -0
  724. package/src/tools/host-filesystem/write.ts +76 -0
  725. package/src/tools/host-terminal/cli-discover.ts +179 -0
  726. package/src/tools/host-terminal/host-shell.ts +181 -0
  727. package/src/tools/memory/definitions.ts +69 -0
  728. package/src/tools/memory/handlers.ts +245 -0
  729. package/src/tools/memory/register.ts +66 -0
  730. package/src/tools/network/domain-normalize.ts +85 -0
  731. package/src/tools/network/script-proxy/certs.ts +237 -0
  732. package/src/tools/network/script-proxy/connect-tunnel.ts +82 -0
  733. package/src/tools/network/script-proxy/http-forwarder.ts +151 -0
  734. package/src/tools/network/script-proxy/index.ts +28 -0
  735. package/src/tools/network/script-proxy/logging.ts +196 -0
  736. package/src/tools/network/script-proxy/mitm-handler.ts +269 -0
  737. package/src/tools/network/script-proxy/policy.ts +152 -0
  738. package/src/tools/network/script-proxy/router.ts +60 -0
  739. package/src/tools/network/script-proxy/server.ts +136 -0
  740. package/src/tools/network/script-proxy/session-manager.ts +534 -0
  741. package/src/tools/network/script-proxy/types.ts +125 -0
  742. package/src/tools/network/url-safety.ts +227 -0
  743. package/src/tools/network/web-fetch.ts +701 -0
  744. package/src/tools/network/web-search.ts +319 -0
  745. package/src/tools/playbooks/index.ts +5 -0
  746. package/src/tools/playbooks/playbook-create.ts +140 -0
  747. package/src/tools/playbooks/playbook-delete.ts +76 -0
  748. package/src/tools/playbooks/playbook-list.ts +101 -0
  749. package/src/tools/playbooks/playbook-update.ts +159 -0
  750. package/src/tools/registry.ts +297 -0
  751. package/src/tools/reminder/reminder-store.ts +148 -0
  752. package/src/tools/reminder/reminder.ts +153 -0
  753. package/src/tools/schedule/create.ts +86 -0
  754. package/src/tools/schedule/delete.ts +54 -0
  755. package/src/tools/schedule/list.ts +88 -0
  756. package/src/tools/schedule/update.ts +97 -0
  757. package/src/tools/shared/filesystem/edit-engine.ts +56 -0
  758. package/src/tools/shared/filesystem/errors.ts +85 -0
  759. package/src/tools/shared/filesystem/file-ops-service.ts +215 -0
  760. package/src/tools/shared/filesystem/format-diff.ts +35 -0
  761. package/src/tools/shared/filesystem/path-policy.ts +125 -0
  762. package/src/tools/shared/filesystem/size-guard.ts +41 -0
  763. package/src/tools/shared/filesystem/types.ts +80 -0
  764. package/src/tools/shared/shell-output.ts +52 -0
  765. package/src/tools/skills/delete-managed.ts +60 -0
  766. package/src/tools/skills/load.ts +139 -0
  767. package/src/tools/skills/sandbox-runner.ts +279 -0
  768. package/src/tools/skills/scaffold-managed.ts +150 -0
  769. package/src/tools/skills/script-contract.ts +6 -0
  770. package/src/tools/skills/skill-script-runner.ts +86 -0
  771. package/src/tools/skills/skill-tool-factory.ts +64 -0
  772. package/src/tools/skills/vellum-catalog.ts +217 -0
  773. package/src/tools/subagent/abort.ts +62 -0
  774. package/src/tools/subagent/index.ts +5 -0
  775. package/src/tools/subagent/message.ts +72 -0
  776. package/src/tools/subagent/read.ts +98 -0
  777. package/src/tools/subagent/spawn.ts +85 -0
  778. package/src/tools/subagent/status.ts +74 -0
  779. package/src/tools/swarm/delegate.ts +182 -0
  780. package/src/tools/system/request-permission.ts +98 -0
  781. package/src/tools/tasks/index.ts +25 -0
  782. package/src/tools/tasks/task-delete.ts +69 -0
  783. package/src/tools/tasks/task-list.ts +65 -0
  784. package/src/tools/tasks/task-run.ts +125 -0
  785. package/src/tools/tasks/task-save.ts +79 -0
  786. package/src/tools/tasks/work-item-enqueue.ts +176 -0
  787. package/src/tools/tasks/work-item-list.ts +86 -0
  788. package/src/tools/terminal/backends/docker.ts +372 -0
  789. package/src/tools/terminal/backends/native.ts +188 -0
  790. package/src/tools/terminal/backends/types.ts +26 -0
  791. package/src/tools/terminal/evaluate-typescript.ts +275 -0
  792. package/src/tools/terminal/parser.ts +393 -0
  793. package/src/tools/terminal/safe-env.ts +37 -0
  794. package/src/tools/terminal/sandbox-diagnostics.ts +149 -0
  795. package/src/tools/terminal/sandbox.ts +44 -0
  796. package/src/tools/terminal/shell.ts +257 -0
  797. package/src/tools/tool-manifest.ts +250 -0
  798. package/src/tools/types.ts +177 -0
  799. package/src/tools/ui-surface/definitions.ts +232 -0
  800. package/src/tools/ui-surface/registry.ts +14 -0
  801. package/src/tools/watch/screen-watch.ts +128 -0
  802. package/src/tools/watch/watch-state.ts +119 -0
  803. package/src/tools/watcher/create.ts +110 -0
  804. package/src/tools/watcher/delete.ts +53 -0
  805. package/src/tools/watcher/digest.ts +84 -0
  806. package/src/tools/watcher/list.ts +90 -0
  807. package/src/tools/watcher/update.ts +102 -0
  808. package/src/tools/weather/service.ts +551 -0
  809. package/src/usage/actors.ts +24 -0
  810. package/src/usage/types.ts +38 -0
  811. package/src/util/clipboard.ts +33 -0
  812. package/src/util/content-id.ts +16 -0
  813. package/src/util/diff.ts +181 -0
  814. package/src/util/errors.ts +129 -0
  815. package/src/util/logger.ts +243 -0
  816. package/src/util/platform.ts +607 -0
  817. package/src/util/pricing.ts +150 -0
  818. package/src/util/spinner.ts +51 -0
  819. package/src/util/time.ts +16 -0
  820. package/src/util/xml.ts +4 -0
  821. package/src/version.ts +3 -0
  822. package/src/watcher/constants.ts +11 -0
  823. package/src/watcher/engine.ts +199 -0
  824. package/src/watcher/provider-registry.ts +15 -0
  825. package/src/watcher/provider-types.ts +48 -0
  826. package/src/watcher/providers/gmail.ts +198 -0
  827. package/src/watcher/providers/google-calendar.ts +228 -0
  828. package/src/watcher/providers/slack.ts +128 -0
  829. package/src/watcher/watcher-store.ts +418 -0
  830. package/src/work-items/work-item-store.ts +91 -0
  831. package/src/workspace/git-service.ts +620 -0
  832. package/src/workspace/heartbeat-service.ts +288 -0
  833. package/src/workspace/top-level-renderer.ts +19 -0
  834. package/src/workspace/top-level-scanner.ts +41 -0
  835. package/src/workspace/turn-commit.ts +122 -0
  836. package/tsconfig.json +21 -0
  837. package/LICENSE +0 -674
  838. package/dist/cli.js +0 -569
@@ -0,0 +1,750 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { execFileSync } from 'node:child_process';
6
+ import {
7
+ WorkspaceGitService,
8
+ getWorkspaceGitService,
9
+ _resetGitServiceRegistry,
10
+ } from '../workspace/git-service.js';
11
+
12
+ describe('WorkspaceGitService', () => {
13
+ let testDir: string;
14
+
15
+ beforeEach(() => {
16
+ // Create a unique test directory for each test
17
+ testDir = join(tmpdir(), `vellum-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
18
+ mkdirSync(testDir, { recursive: true });
19
+ _resetGitServiceRegistry();
20
+ });
21
+
22
+ afterEach(() => {
23
+ // Clean up test directory
24
+ if (existsSync(testDir)) {
25
+ rmSync(testDir, { recursive: true, force: true });
26
+ }
27
+ });
28
+
29
+ describe('lazy initialization', () => {
30
+ test('initializes git repo on first ensureInitialized call', async () => {
31
+ const service = new WorkspaceGitService(testDir);
32
+
33
+ expect(service.isInitialized()).toBe(false);
34
+
35
+ await service.ensureInitialized();
36
+
37
+ expect(service.isInitialized()).toBe(true);
38
+ expect(existsSync(join(testDir, '.git'))).toBe(true);
39
+ });
40
+
41
+ test('creates .gitignore with proper exclusions', async () => {
42
+ const service = new WorkspaceGitService(testDir);
43
+ await service.ensureInitialized();
44
+
45
+ const gitignorePath = join(testDir, '.gitignore');
46
+ expect(existsSync(gitignorePath)).toBe(true);
47
+
48
+ const content = readFileSync(gitignorePath, 'utf-8');
49
+ expect(content).toContain('data/db/');
50
+ expect(content).toContain('data/qdrant/');
51
+ expect(content).toContain('data/ipc-blobs/');
52
+ expect(content).toContain('*.log');
53
+ expect(content).toContain('*.sock');
54
+ expect(content).toContain('*.pid');
55
+ expect(content).toContain('vellum.sock');
56
+ expect(content).toContain('session-token');
57
+ });
58
+
59
+ test('sets git identity correctly', async () => {
60
+ const service = new WorkspaceGitService(testDir);
61
+ await service.ensureInitialized();
62
+
63
+ const userName = execFileSync('git', ['config', 'user.name'], {
64
+ cwd: testDir,
65
+ encoding: 'utf-8',
66
+ }).trim();
67
+ const userEmail = execFileSync('git', ['config', 'user.email'], {
68
+ cwd: testDir,
69
+ encoding: 'utf-8',
70
+ }).trim();
71
+
72
+ expect(userName).toBe('Vellum Assistant');
73
+ expect(userEmail).toBe('assistant@vellum.ai');
74
+ });
75
+
76
+ test('multiple ensureInitialized calls are idempotent', async () => {
77
+ const service = new WorkspaceGitService(testDir);
78
+
79
+ await service.ensureInitialized();
80
+ await service.ensureInitialized();
81
+ await service.ensureInitialized();
82
+
83
+ expect(service.isInitialized()).toBe(true);
84
+ });
85
+
86
+ test('handles concurrent ensureInitialized calls', async () => {
87
+ const service = new WorkspaceGitService(testDir);
88
+
89
+ // Start multiple initialization calls concurrently
90
+ const promises = [
91
+ service.ensureInitialized(),
92
+ service.ensureInitialized(),
93
+ service.ensureInitialized(),
94
+ ];
95
+
96
+ await Promise.all(promises);
97
+
98
+ expect(service.isInitialized()).toBe(true);
99
+ });
100
+ });
101
+
102
+ describe('initial commit', () => {
103
+ test('creates initial commit for new empty workspace', async () => {
104
+ const service = new WorkspaceGitService(testDir);
105
+ await service.ensureInitialized();
106
+
107
+ const log = execFileSync('git', ['log', '--oneline'], {
108
+ cwd: testDir,
109
+ encoding: 'utf-8',
110
+ });
111
+
112
+ expect(log).toContain('Initial commit: new workspace');
113
+ });
114
+
115
+ test('creates initial commit for existing workspace with files', async () => {
116
+ // Create some files before initializing git
117
+ writeFileSync(join(testDir, 'README.md'), '# Test\n');
118
+ writeFileSync(join(testDir, 'config.json'), '{}');
119
+ mkdirSync(join(testDir, 'subdir'));
120
+ writeFileSync(join(testDir, 'subdir', 'file.txt'), 'content');
121
+
122
+ const service = new WorkspaceGitService(testDir);
123
+ await service.ensureInitialized();
124
+
125
+ const log = execFileSync('git', ['log', '--oneline'], {
126
+ cwd: testDir,
127
+ encoding: 'utf-8',
128
+ });
129
+
130
+ expect(log).toContain('Initial commit: migrated existing workspace');
131
+
132
+ // Verify files were committed
133
+ const files = execFileSync('git', ['ls-files'], {
134
+ cwd: testDir,
135
+ encoding: 'utf-8',
136
+ }).trim().split('\n');
137
+
138
+ expect(files).toContain('.gitignore');
139
+ expect(files).toContain('README.md');
140
+ expect(files).toContain('config.json');
141
+ expect(files).toContain('subdir/file.txt');
142
+ });
143
+
144
+ test('initial commit completes within ensureInitialized', async () => {
145
+ // Create some files before initializing git
146
+ for (let i = 0; i < 10; i++) {
147
+ writeFileSync(join(testDir, `file${i}.txt`), 'content');
148
+ }
149
+
150
+ const service = new WorkspaceGitService(testDir);
151
+ await service.ensureInitialized();
152
+
153
+ // Initial commit should already be done - no need to wait
154
+ const log = execFileSync('git', ['log', '--oneline'], {
155
+ cwd: testDir,
156
+ encoding: 'utf-8',
157
+ });
158
+
159
+ expect(log).toContain('Initial commit: migrated existing workspace');
160
+ });
161
+
162
+ test('initial commit does not race with first commitChanges', async () => {
163
+ // Pre-populate workspace with files (simulating a migrated workspace)
164
+ writeFileSync(join(testDir, 'existing.txt'), 'pre-existing content');
165
+
166
+ const service = new WorkspaceGitService(testDir);
167
+
168
+ // Initialize - the initial commit now happens synchronously within
169
+ // ensureInitialized, so it completes before we can write new files.
170
+ await service.ensureInitialized();
171
+
172
+ // Now write a file AFTER init and commit it
173
+ writeFileSync(join(testDir, 'user-edit.txt'), 'user content');
174
+ await service.commitChanges('User turn 1');
175
+
176
+ // The user's commit (HEAD) should contain user-edit.txt
177
+ const userCommitFiles = execFileSync(
178
+ 'git', ['diff', '--name-only', 'HEAD~1', 'HEAD'],
179
+ { cwd: testDir, encoding: 'utf-8' },
180
+ ).trim();
181
+
182
+ expect(userCommitFiles).toContain('user-edit.txt');
183
+ // user-edit.txt should NOT appear in the initial commit
184
+ expect(userCommitFiles).not.toContain('existing.txt');
185
+
186
+ // The initial commit (HEAD~1) should contain existing.txt and .gitignore
187
+ const initialCommitFiles = execFileSync(
188
+ 'git', ['show', '--name-only', '--pretty=format:', 'HEAD~1'],
189
+ { cwd: testDir, encoding: 'utf-8' },
190
+ ).trim();
191
+
192
+ expect(initialCommitFiles).toContain('existing.txt');
193
+ expect(initialCommitFiles).toContain('.gitignore');
194
+ // The initial commit should NOT contain user-edit.txt
195
+ expect(initialCommitFiles).not.toContain('user-edit.txt');
196
+ });
197
+ });
198
+
199
+ describe('commitChanges', () => {
200
+ test('commits changes with message', async () => {
201
+ const service = new WorkspaceGitService(testDir);
202
+ await service.ensureInitialized();
203
+
204
+ writeFileSync(join(testDir, 'test.txt'), 'hello world');
205
+ await service.commitChanges('Add test file');
206
+
207
+ const log = execFileSync('git', ['log', '--oneline', '-n', '1'], {
208
+ cwd: testDir,
209
+ encoding: 'utf-8',
210
+ });
211
+
212
+ expect(log).toContain('Add test file');
213
+ });
214
+
215
+ test('commits with metadata', async () => {
216
+ const service = new WorkspaceGitService(testDir);
217
+ await service.ensureInitialized();
218
+
219
+ writeFileSync(join(testDir, 'test.txt'), 'content');
220
+ await service.commitChanges('Add file', {
221
+ sessionId: 'session-123',
222
+ timestamp: 1234567890,
223
+ author: 'user@example.com',
224
+ });
225
+
226
+ const message = execFileSync('git', ['log', '-1', '--pretty=%B'], {
227
+ cwd: testDir,
228
+ encoding: 'utf-8',
229
+ });
230
+
231
+ expect(message).toContain('Add file');
232
+ expect(message).toContain('sessionId: "session-123"');
233
+ expect(message).toContain('timestamp: 1234567890');
234
+ expect(message).toContain('author: "user@example.com"');
235
+ });
236
+
237
+ test('commits multiple files at once', async () => {
238
+ const service = new WorkspaceGitService(testDir);
239
+ await service.ensureInitialized();
240
+
241
+ writeFileSync(join(testDir, 'file1.txt'), 'content1');
242
+ writeFileSync(join(testDir, 'file2.txt'), 'content2');
243
+ writeFileSync(join(testDir, 'file3.txt'), 'content3');
244
+
245
+ await service.commitChanges('Add multiple files');
246
+
247
+ const files = execFileSync('git', ['diff', '--name-only', 'HEAD~1', 'HEAD'], {
248
+ cwd: testDir,
249
+ encoding: 'utf-8',
250
+ }).trim().split('\n');
251
+
252
+ expect(files).toContain('file1.txt');
253
+ expect(files).toContain('file2.txt');
254
+ expect(files).toContain('file3.txt');
255
+ });
256
+
257
+ test('allows empty commits', async () => {
258
+ const service = new WorkspaceGitService(testDir);
259
+ await service.ensureInitialized();
260
+
261
+ // Commit without any changes
262
+ await service.commitChanges('Empty commit for checkpoint');
263
+
264
+ const log = execFileSync('git', ['log', '--oneline', '-n', '1'], {
265
+ cwd: testDir,
266
+ encoding: 'utf-8',
267
+ });
268
+
269
+ expect(log).toContain('Empty commit for checkpoint');
270
+ });
271
+ });
272
+
273
+ describe('getStatus', () => {
274
+ test('returns clean status for new workspace', async () => {
275
+ const service = new WorkspaceGitService(testDir);
276
+ await service.ensureInitialized();
277
+
278
+ const status = await service.getStatus();
279
+
280
+ expect(status.clean).toBe(true);
281
+ expect(status.staged).toEqual([]);
282
+ expect(status.modified).toEqual([]);
283
+ expect(status.untracked).toEqual([]);
284
+ });
285
+
286
+ test('detects untracked files', async () => {
287
+ const service = new WorkspaceGitService(testDir);
288
+ await service.ensureInitialized();
289
+
290
+ writeFileSync(join(testDir, 'new-file.txt'), 'content');
291
+
292
+ const status = await service.getStatus();
293
+
294
+ expect(status.clean).toBe(false);
295
+ expect(status.untracked).toContain('new-file.txt');
296
+ });
297
+
298
+ test('detects modified files', async () => {
299
+ const service = new WorkspaceGitService(testDir);
300
+ await service.ensureInitialized();
301
+
302
+ writeFileSync(join(testDir, 'file.txt'), 'original');
303
+ await service.commitChanges('Add file');
304
+
305
+ writeFileSync(join(testDir, 'file.txt'), 'modified');
306
+
307
+ const status = await service.getStatus();
308
+
309
+ expect(status.clean).toBe(false);
310
+ expect(status.modified).toContain('file.txt');
311
+ });
312
+
313
+ test('detects staged files', async () => {
314
+ const service = new WorkspaceGitService(testDir);
315
+ await service.ensureInitialized();
316
+
317
+ writeFileSync(join(testDir, 'file.txt'), 'content');
318
+
319
+ // Manually stage the file
320
+ execFileSync('git', ['add', 'file.txt'], { cwd: testDir });
321
+
322
+ const status = await service.getStatus();
323
+
324
+ expect(status.clean).toBe(false);
325
+ expect(status.staged).toContain('file.txt');
326
+ });
327
+ });
328
+
329
+ describe('mutex locking', () => {
330
+ test('serializes concurrent commit operations', async () => {
331
+ const service = new WorkspaceGitService(testDir);
332
+ await service.ensureInitialized();
333
+
334
+ // Start multiple concurrent commits
335
+ const commits = [];
336
+ for (let i = 0; i < 10; i++) {
337
+ commits.push(
338
+ (async () => {
339
+ writeFileSync(join(testDir, `file${i}.txt`), `content ${i}`);
340
+ await service.commitChanges(`Add file ${i}`);
341
+ })(),
342
+ );
343
+ }
344
+
345
+ await Promise.all(commits);
346
+
347
+ // All commits should have succeeded
348
+ const log = execFileSync('git', ['log', '--oneline'], {
349
+ cwd: testDir,
350
+ encoding: 'utf-8',
351
+ });
352
+
353
+ for (let i = 0; i < 10; i++) {
354
+ expect(log).toContain(`Add file ${i}`);
355
+ }
356
+
357
+ // Count commits (excluding initial commit)
358
+ const commitCount = log.trim().split('\n').length;
359
+ expect(commitCount).toBe(11); // 10 + 1 initial
360
+ });
361
+
362
+ test('serializes concurrent status checks', async () => {
363
+ const service = new WorkspaceGitService(testDir);
364
+ await service.ensureInitialized();
365
+
366
+ // Start multiple concurrent status checks
367
+ const checks = [];
368
+ for (let i = 0; i < 20; i++) {
369
+ checks.push(service.getStatus());
370
+ }
371
+
372
+ const results = await Promise.all(checks);
373
+
374
+ // All should succeed and return consistent results
375
+ for (const status of results) {
376
+ expect(status).toBeDefined();
377
+ expect(status.clean).toBe(true);
378
+ }
379
+ });
380
+ });
381
+
382
+ describe('getWorkspaceGitService singleton', () => {
383
+ test('returns same instance for same workspace', () => {
384
+ const service1 = getWorkspaceGitService(testDir);
385
+ const service2 = getWorkspaceGitService(testDir);
386
+
387
+ expect(service1).toBe(service2);
388
+ });
389
+
390
+ test('returns different instances for different workspaces', () => {
391
+ const testDir2 = join(tmpdir(), `vellum-test-${Date.now()}-other`);
392
+ mkdirSync(testDir2, { recursive: true });
393
+
394
+ try {
395
+ const service1 = getWorkspaceGitService(testDir);
396
+ const service2 = getWorkspaceGitService(testDir2);
397
+
398
+ expect(service1).not.toBe(service2);
399
+ expect(service1.getWorkspaceDir()).toBe(testDir);
400
+ expect(service2.getWorkspaceDir()).toBe(testDir2);
401
+ } finally {
402
+ rmSync(testDir2, { recursive: true, force: true });
403
+ }
404
+ });
405
+ });
406
+
407
+ describe('error handling', () => {
408
+ test('handles invalid workspace directory', async () => {
409
+ const invalidDir = '/nonexistent/path/that/does/not/exist';
410
+ const service = new WorkspaceGitService(invalidDir);
411
+
412
+ await expect(service.ensureInitialized()).rejects.toThrow();
413
+ });
414
+
415
+ test('failed initialization can be retried', async () => {
416
+ // Create a service pointing to a directory that doesn't exist yet
417
+ const retryDir = join(tmpdir(), `vellum-retry-${Date.now()}-${Math.random().toString(36).slice(2)}`);
418
+ const service = new WorkspaceGitService(retryDir);
419
+
420
+ // First attempt: directory doesn't exist, should fail
421
+ await expect(service.ensureInitialized()).rejects.toThrow();
422
+
423
+ // Create the directory so the retry can succeed
424
+ mkdirSync(retryDir, { recursive: true });
425
+
426
+ try {
427
+ // Second attempt: directory now exists, should succeed because
428
+ // the .catch handler cleared initPromise after the first failure
429
+ await service.ensureInitialized();
430
+ expect(service.isInitialized()).toBe(true);
431
+
432
+ // Verify the repo was actually initialized
433
+ const log = execFileSync('git', ['log', '--oneline'], {
434
+ cwd: retryDir,
435
+ encoding: 'utf-8',
436
+ });
437
+ expect(log).toContain('Initial commit');
438
+ } finally {
439
+ rmSync(retryDir, { recursive: true, force: true });
440
+ }
441
+ });
442
+
443
+ test('continues to work after failed operation', async () => {
444
+ const service = new WorkspaceGitService(testDir);
445
+ await service.ensureInitialized();
446
+
447
+ // Try to commit without any changes and without allow-empty
448
+ // (This should succeed with --allow-empty, but let's test recovery)
449
+ writeFileSync(join(testDir, 'test.txt'), 'content');
450
+ await service.commitChanges('Valid commit');
451
+
452
+ // Service should still work
453
+ const status = await service.getStatus();
454
+ expect(status).toBeDefined();
455
+ });
456
+ });
457
+
458
+ describe('existing repo normalization', () => {
459
+ test('existing repo on feature branch auto-switches to main on init', async () => {
460
+ // Set up a pre-existing git repo on a feature branch
461
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
462
+ execFileSync('git', ['config', 'user.name', 'Test'], { cwd: testDir });
463
+ execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: testDir });
464
+ writeFileSync(join(testDir, 'file.txt'), 'content');
465
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
466
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
467
+ execFileSync('git', ['checkout', '-b', 'feature-branch'], { cwd: testDir });
468
+
469
+ // Verify we're on feature-branch
470
+ const branchBefore = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
471
+ cwd: testDir,
472
+ encoding: 'utf-8',
473
+ }).trim();
474
+ expect(branchBefore).toBe('feature-branch');
475
+
476
+ // Initialize the service — should auto-switch to main
477
+ const service = new WorkspaceGitService(testDir);
478
+ await service.ensureInitialized();
479
+
480
+ const branchAfter = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
481
+ cwd: testDir,
482
+ encoding: 'utf-8',
483
+ }).trim();
484
+ expect(branchAfter).toBe('main');
485
+ });
486
+
487
+ test('detached HEAD recovers to main on init', async () => {
488
+ // Set up a pre-existing git repo then detach HEAD
489
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
490
+ execFileSync('git', ['config', 'user.name', 'Test'], { cwd: testDir });
491
+ execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: testDir });
492
+ writeFileSync(join(testDir, 'file.txt'), 'content');
493
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
494
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
495
+ // Detach HEAD by checking out the commit hash
496
+ const commitHash = execFileSync('git', ['rev-parse', 'HEAD'], {
497
+ cwd: testDir,
498
+ encoding: 'utf-8',
499
+ }).trim();
500
+ execFileSync('git', ['checkout', commitHash], { cwd: testDir });
501
+
502
+ // Verify we're in detached HEAD
503
+ let isDetached = false;
504
+ try {
505
+ execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], { cwd: testDir });
506
+ } catch {
507
+ isDetached = true;
508
+ }
509
+ expect(isDetached).toBe(true);
510
+
511
+ // Initialize the service — should recover to main
512
+ const service = new WorkspaceGitService(testDir);
513
+ await service.ensureInitialized();
514
+
515
+ const branchAfter = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
516
+ cwd: testDir,
517
+ encoding: 'utf-8',
518
+ }).trim();
519
+ expect(branchAfter).toBe('main');
520
+ });
521
+
522
+ test('existing repo on feature branch with dirty working tree switches to main', async () => {
523
+ // Set up a pre-existing git repo on a feature branch with uncommitted changes.
524
+ // This exercises the --discard-changes fallback in ensureOnMainLocked().
525
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
526
+ execFileSync('git', ['config', 'user.name', 'Test'], { cwd: testDir });
527
+ execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: testDir });
528
+ writeFileSync(join(testDir, 'file.txt'), 'original content');
529
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
530
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
531
+ execFileSync('git', ['checkout', '-b', 'feature-branch'], { cwd: testDir });
532
+
533
+ // Create uncommitted changes that would block a normal `git switch main`
534
+ writeFileSync(join(testDir, 'file.txt'), 'modified on feature branch');
535
+
536
+ // Verify we're on feature-branch with dirty working tree
537
+ const branchBefore = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
538
+ cwd: testDir,
539
+ encoding: 'utf-8',
540
+ }).trim();
541
+ expect(branchBefore).toBe('feature-branch');
542
+ const statusBefore = execFileSync('git', ['status', '--porcelain'], {
543
+ cwd: testDir,
544
+ encoding: 'utf-8',
545
+ }).trim();
546
+ expect(statusBefore).toContain('file.txt');
547
+
548
+ // Initialize the service — should auto-switch to main despite dirty tree
549
+ const service = new WorkspaceGitService(testDir);
550
+ await service.ensureInitialized();
551
+
552
+ const branchAfter = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
553
+ cwd: testDir,
554
+ encoding: 'utf-8',
555
+ }).trim();
556
+ expect(branchAfter).toBe('main');
557
+ });
558
+
559
+ test('existing repo gets .gitignore rules appended on init', async () => {
560
+ // Set up a pre-existing git repo without our gitignore rules
561
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
562
+ execFileSync('git', ['config', 'user.name', 'Test'], { cwd: testDir });
563
+ execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: testDir });
564
+ writeFileSync(join(testDir, '.gitignore'), 'node_modules/\n');
565
+ writeFileSync(join(testDir, 'file.txt'), 'content');
566
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
567
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
568
+
569
+ // Verify .gitignore does NOT have our rules yet
570
+ const contentBefore = readFileSync(join(testDir, '.gitignore'), 'utf-8');
571
+ expect(contentBefore).not.toContain('data/db/');
572
+ expect(contentBefore).not.toContain('vellum.sock');
573
+
574
+ // Initialize the service — should append rules
575
+ const service = new WorkspaceGitService(testDir);
576
+ await service.ensureInitialized();
577
+
578
+ const contentAfter = readFileSync(join(testDir, '.gitignore'), 'utf-8');
579
+ expect(contentAfter).toContain('node_modules/'); // original rule preserved
580
+ expect(contentAfter).toContain('data/db/');
581
+ expect(contentAfter).toContain('data/qdrant/');
582
+ expect(contentAfter).toContain('data/ipc-blobs/');
583
+ expect(contentAfter).toContain('*.log');
584
+ expect(contentAfter).toContain('vellum.sock');
585
+ expect(contentAfter).toContain('session-token');
586
+ });
587
+
588
+ test('existing repo with old data/ rule gets it replaced with selective rules', async () => {
589
+ // Set up a pre-existing git repo with the OLD broad data/ rule
590
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
591
+ execFileSync('git', ['config', 'user.name', 'Test'], { cwd: testDir });
592
+ execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: testDir });
593
+ const oldGitignore = '# Runtime state - excluded from git tracking\ndata/\nlogs/\n*.log\n*.sock\n*.pid\n*.sqlite\n*.sqlite-journal\n*.sqlite-wal\n*.sqlite-shm\n*.db\n*.db-journal\n*.db-wal\n*.db-shm\nvellum.sock\nvellum.pid\nsession-token\nhttp-token\n';
594
+ writeFileSync(join(testDir, '.gitignore'), oldGitignore);
595
+ writeFileSync(join(testDir, 'file.txt'), 'content');
596
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
597
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
598
+
599
+ // Verify the old broad rule is present
600
+ const contentBefore = readFileSync(join(testDir, '.gitignore'), 'utf-8');
601
+ expect(contentBefore).toContain('data/\n');
602
+
603
+ // Initialize the service — should migrate the gitignore
604
+ const service = new WorkspaceGitService(testDir);
605
+ await service.ensureInitialized();
606
+
607
+ const contentAfter = readFileSync(join(testDir, '.gitignore'), 'utf-8');
608
+
609
+ // Old broad rule should be removed
610
+ expect(contentAfter).not.toMatch(/^data\/$/m);
611
+
612
+ // New selective rules should be present
613
+ expect(contentAfter).toContain('data/db/');
614
+ expect(contentAfter).toContain('data/qdrant/');
615
+ expect(contentAfter).toContain('data/ipc-blobs/');
616
+
617
+ // Other existing rules should be preserved
618
+ expect(contentAfter).toContain('logs/');
619
+ expect(contentAfter).toContain('*.log');
620
+ expect(contentAfter).toContain('vellum.sock');
621
+ });
622
+
623
+ test('existing repo gets local identity set on init', async () => {
624
+ // Set up a pre-existing git repo with a different identity
625
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
626
+ execFileSync('git', ['config', 'user.name', 'Old Name'], { cwd: testDir });
627
+ execFileSync('git', ['config', 'user.email', 'old@example.com'], { cwd: testDir });
628
+ writeFileSync(join(testDir, 'file.txt'), 'content');
629
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
630
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
631
+
632
+ // Initialize the service — should set identity
633
+ const service = new WorkspaceGitService(testDir);
634
+ await service.ensureInitialized();
635
+
636
+ const userName = execFileSync('git', ['config', 'user.name'], {
637
+ cwd: testDir,
638
+ encoding: 'utf-8',
639
+ }).trim();
640
+ const userEmail = execFileSync('git', ['config', 'user.email'], {
641
+ cwd: testDir,
642
+ encoding: 'utf-8',
643
+ }).trim();
644
+
645
+ expect(userName).toBe('Vellum Assistant');
646
+ expect(userEmail).toBe('assistant@vellum.ai');
647
+ });
648
+
649
+ test('existing repo with correct config is idempotent', async () => {
650
+ // Set up a repo that already has everything configured correctly
651
+ execFileSync('git', ['init', '-b', 'main'], { cwd: testDir });
652
+ execFileSync('git', ['config', 'user.name', 'Vellum Assistant'], { cwd: testDir });
653
+ execFileSync('git', ['config', 'user.email', 'assistant@vellum.ai'], { cwd: testDir });
654
+ const gitignoreContent = '# Runtime state - excluded from git tracking\ndata/db/\ndata/qdrant/\ndata/ipc-blobs/\nlogs/\n*.log\n*.sock\n*.pid\n*.sqlite\n*.sqlite-journal\n*.sqlite-wal\n*.sqlite-shm\n*.db\n*.db-journal\n*.db-wal\n*.db-shm\nvellum.sock\nvellum.pid\nsession-token\nhttp-token\n';
655
+ writeFileSync(join(testDir, '.gitignore'), gitignoreContent);
656
+ writeFileSync(join(testDir, 'file.txt'), 'content');
657
+ execFileSync('git', ['add', '-A'], { cwd: testDir });
658
+ execFileSync('git', ['commit', '-m', 'init'], { cwd: testDir });
659
+
660
+ const gitignoreBefore = readFileSync(join(testDir, '.gitignore'), 'utf-8');
661
+
662
+ // Initialize the service — should be a no-op
663
+ const service = new WorkspaceGitService(testDir);
664
+ await service.ensureInitialized();
665
+
666
+ // Verify nothing changed
667
+ const gitignoreAfter = readFileSync(join(testDir, '.gitignore'), 'utf-8');
668
+ expect(gitignoreAfter).toBe(gitignoreBefore);
669
+
670
+ const userName = execFileSync('git', ['config', 'user.name'], {
671
+ cwd: testDir,
672
+ encoding: 'utf-8',
673
+ }).trim();
674
+ expect(userName).toBe('Vellum Assistant');
675
+
676
+ const branch = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
677
+ cwd: testDir,
678
+ encoding: 'utf-8',
679
+ }).trim();
680
+ expect(branch).toBe('main');
681
+
682
+ // No errors, no duplicate rules
683
+ const ruleCount = (gitignoreAfter.match(/data\/db\//g) || []).length;
684
+ expect(ruleCount).toBe(1);
685
+ });
686
+ });
687
+
688
+ describe('gitignore behavior', () => {
689
+ test('ignores data/db/ but tracks other data/ subdirectories', async () => {
690
+ const service = new WorkspaceGitService(testDir);
691
+ await service.ensureInitialized();
692
+
693
+ // Create files in ignored data subdirectories
694
+ mkdirSync(join(testDir, 'data', 'db'), { recursive: true });
695
+ writeFileSync(join(testDir, 'data', 'db', 'conversations.sqlite'), 'db content');
696
+ mkdirSync(join(testDir, 'data', 'qdrant'), { recursive: true });
697
+ writeFileSync(join(testDir, 'data', 'qdrant', 'index.bin'), 'qdrant content');
698
+ mkdirSync(join(testDir, 'data', 'ipc-blobs'), { recursive: true });
699
+ writeFileSync(join(testDir, 'data', 'ipc-blobs', 'blob1'), 'ipc content');
700
+
701
+ // Create files in tracked data subdirectories
702
+ mkdirSync(join(testDir, 'data', 'memory'), { recursive: true });
703
+ writeFileSync(join(testDir, 'data', 'memory', 'index.json'), '{}');
704
+ mkdirSync(join(testDir, 'data', 'apps'), { recursive: true });
705
+ writeFileSync(join(testDir, 'data', 'apps', 'state.json'), '{}');
706
+
707
+ // Commit all changes, then verify what was included
708
+ await service.commitChanges('test commit');
709
+
710
+ const committedFiles = execFileSync(
711
+ 'git', ['diff', '--name-only', 'HEAD~1', 'HEAD'],
712
+ { cwd: testDir, encoding: 'utf-8' },
713
+ ).trim();
714
+
715
+ // Ignored subdirectories should NOT be in the commit
716
+ expect(committedFiles).not.toContain('data/db/');
717
+ expect(committedFiles).not.toContain('data/qdrant/');
718
+ expect(committedFiles).not.toContain('data/ipc-blobs/');
719
+
720
+ // Tracked subdirectories SHOULD be in the commit
721
+ expect(committedFiles).toContain('data/memory/index.json');
722
+ expect(committedFiles).toContain('data/apps/state.json');
723
+ });
724
+
725
+ test('respects .gitignore for log files', async () => {
726
+ const service = new WorkspaceGitService(testDir);
727
+ await service.ensureInitialized();
728
+
729
+ writeFileSync(join(testDir, 'test.log'), 'log content');
730
+
731
+ const status = await service.getStatus();
732
+
733
+ // .log files should be ignored
734
+ expect(status.untracked).not.toContain('test.log');
735
+ });
736
+
737
+ test('tracks non-ignored files', async () => {
738
+ const service = new WorkspaceGitService(testDir);
739
+ await service.ensureInitialized();
740
+
741
+ writeFileSync(join(testDir, 'config.json'), '{}');
742
+ writeFileSync(join(testDir, 'README.md'), '# Test');
743
+
744
+ const status = await service.getStatus();
745
+
746
+ expect(status.untracked).toContain('config.json');
747
+ expect(status.untracked).toContain('README.md');
748
+ });
749
+ });
750
+ });