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,347 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { mkdirSync, rmSync, writeFileSync, existsSync } 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
+ _resetGitServiceRegistry,
9
+ } from '../workspace/git-service.js';
10
+ import {
11
+ HeartbeatService,
12
+ _resetHeartbeatState,
13
+ } from '../workspace/heartbeat-service.js';
14
+
15
+ describe('HeartbeatService', () => {
16
+ let testDir: string;
17
+ let service: WorkspaceGitService;
18
+ let services: Map<string, WorkspaceGitService>;
19
+
20
+ beforeEach(async () => {
21
+ testDir = join(tmpdir(), `vellum-heartbeat-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
22
+ mkdirSync(testDir, { recursive: true });
23
+ _resetGitServiceRegistry();
24
+ _resetHeartbeatState();
25
+
26
+ service = new WorkspaceGitService(testDir);
27
+ await service.ensureInitialized();
28
+
29
+ services = new Map();
30
+ services.set(testDir, service);
31
+ });
32
+
33
+ afterEach(() => {
34
+ if (existsSync(testDir)) {
35
+ rmSync(testDir, { recursive: true, force: true });
36
+ }
37
+ });
38
+
39
+ describe('heartbeat check with age threshold', () => {
40
+ test('does not commit when workspace is clean', async () => {
41
+ const heartbeat = new HeartbeatService({
42
+ ageThresholdMs: 0, // Immediate
43
+ fileThreshold: 1,
44
+ getServices: () => services,
45
+ });
46
+
47
+ const result = await heartbeat.check();
48
+
49
+ expect(result.checked).toBe(1);
50
+ expect(result.committed).toBe(0);
51
+ expect(result.skipped).toBe(1);
52
+ });
53
+
54
+ test('does not commit when changes are below age and file thresholds', async () => {
55
+ writeFileSync(join(testDir, 'file.txt'), 'content');
56
+
57
+ const heartbeat = new HeartbeatService({
58
+ ageThresholdMs: 10 * 60 * 1000, // 10 minutes
59
+ fileThreshold: 100,
60
+ getServices: () => services,
61
+ });
62
+
63
+ const result = await heartbeat.check();
64
+
65
+ expect(result.checked).toBe(1);
66
+ expect(result.committed).toBe(0);
67
+ expect(result.skipped).toBe(1);
68
+ });
69
+
70
+ test('commits when changes exceed age threshold', async () => {
71
+ writeFileSync(join(testDir, 'file.txt'), 'content');
72
+
73
+ let currentTime = 1000000;
74
+ const heartbeat = new HeartbeatService({
75
+ ageThresholdMs: 5 * 60 * 1000, // 5 minutes
76
+ fileThreshold: 100,
77
+ getServices: () => services,
78
+ now: () => currentTime,
79
+ });
80
+
81
+ // First check: records dirty time but doesn't commit (too recent)
82
+ const firstResult = await heartbeat.check();
83
+ expect(firstResult.committed).toBe(0);
84
+
85
+ // Advance time past threshold
86
+ currentTime += 6 * 60 * 1000; // 6 minutes later
87
+
88
+ const secondResult = await heartbeat.check();
89
+ expect(secondResult.committed).toBe(1);
90
+
91
+ // Verify commit message
92
+ const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
93
+ cwd: testDir,
94
+ encoding: 'utf-8',
95
+ });
96
+ expect(commitMsg).toContain('auto-commit');
97
+ expect(commitMsg).toContain('heartbeat');
98
+ expect(commitMsg).toContain('safety net');
99
+ });
100
+
101
+ test('commits when file count exceeds threshold', async () => {
102
+ // Create enough files to exceed the threshold
103
+ for (let i = 0; i < 25; i++) {
104
+ writeFileSync(join(testDir, `file${i}.txt`), `content ${i}`);
105
+ }
106
+
107
+ const heartbeat = new HeartbeatService({
108
+ ageThresholdMs: 10 * 60 * 1000, // 10 minutes (not yet)
109
+ fileThreshold: 20,
110
+ getServices: () => services,
111
+ });
112
+
113
+ const result = await heartbeat.check();
114
+
115
+ expect(result.committed).toBe(1);
116
+
117
+ // Verify commit message mentions file count
118
+ const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
119
+ cwd: testDir,
120
+ encoding: 'utf-8',
121
+ });
122
+ expect(commitMsg).toContain('25 files changed');
123
+ });
124
+ });
125
+
126
+ describe('normal operation does not create spurious commits', () => {
127
+ test('clean workspace produces no heartbeat commits', async () => {
128
+ const heartbeat = new HeartbeatService({
129
+ ageThresholdMs: 0,
130
+ fileThreshold: 1,
131
+ getServices: () => services,
132
+ });
133
+
134
+ // Run check multiple times on clean workspace
135
+ for (let i = 0; i < 5; i++) {
136
+ const result = await heartbeat.check();
137
+ expect(result.committed).toBe(0);
138
+ }
139
+
140
+ // Only the initial commit should exist
141
+ const commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
142
+ cwd: testDir,
143
+ encoding: 'utf-8',
144
+ }).trim();
145
+ expect(parseInt(commitCount, 10)).toBe(1);
146
+ });
147
+
148
+ test('changes below both thresholds do not trigger heartbeat commit', async () => {
149
+ // Create a small number of files (below file threshold)
150
+ writeFileSync(join(testDir, 'small-change.txt'), 'content');
151
+
152
+ const heartbeat = new HeartbeatService({
153
+ ageThresholdMs: 10 * 60 * 1000, // 10 minutes
154
+ fileThreshold: 100, // Very high
155
+ getServices: () => services,
156
+ });
157
+
158
+ const result = await heartbeat.check();
159
+ expect(result.committed).toBe(0);
160
+ expect(result.skipped).toBe(1);
161
+ });
162
+
163
+ test('does not double-commit after turn-boundary commit clears changes', async () => {
164
+ writeFileSync(join(testDir, 'file.txt'), 'content');
165
+
166
+ let currentTime = 1000000;
167
+ const heartbeat = new HeartbeatService({
168
+ ageThresholdMs: 5 * 60 * 1000,
169
+ fileThreshold: 100,
170
+ getServices: () => services,
171
+ now: () => currentTime,
172
+ });
173
+
174
+ // First check: records dirty state
175
+ await heartbeat.check();
176
+
177
+ // Simulate a turn-boundary commit that clears the changes
178
+ await service.commitChanges('Turn-boundary commit');
179
+
180
+ // Advance time past the threshold
181
+ currentTime += 6 * 60 * 1000;
182
+
183
+ // Heartbeat should see clean workspace and skip
184
+ const result = await heartbeat.check();
185
+ expect(result.committed).toBe(0);
186
+ expect(result.skipped).toBe(1);
187
+ });
188
+ });
189
+
190
+ describe('shutdown commits', () => {
191
+ test('commits pending changes on shutdown', async () => {
192
+ writeFileSync(join(testDir, 'unsaved.txt'), 'uncommitted content');
193
+
194
+ const heartbeat = new HeartbeatService({
195
+ getServices: () => services,
196
+ });
197
+
198
+ const result = await heartbeat.commitAllPending();
199
+
200
+ expect(result.checked).toBe(1);
201
+ expect(result.committed).toBe(1);
202
+
203
+ // Verify commit message
204
+ const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
205
+ cwd: testDir,
206
+ encoding: 'utf-8',
207
+ });
208
+ expect(commitMsg).toContain('auto-commit');
209
+ expect(commitMsg).toContain('shutdown');
210
+ expect(commitMsg).toContain('safety net');
211
+ });
212
+
213
+ test('does not commit on shutdown when workspace is clean', async () => {
214
+ const heartbeat = new HeartbeatService({
215
+ getServices: () => services,
216
+ });
217
+
218
+ const result = await heartbeat.commitAllPending();
219
+
220
+ expect(result.checked).toBe(1);
221
+ expect(result.committed).toBe(0);
222
+ expect(result.skipped).toBe(1);
223
+ });
224
+
225
+ test('shutdown commits multiple workspaces', async () => {
226
+ const testDir2 = join(tmpdir(), `vellum-heartbeat-test2-${Date.now()}-${Math.random().toString(36).slice(2)}`);
227
+ mkdirSync(testDir2, { recursive: true });
228
+ const service2 = new WorkspaceGitService(testDir2);
229
+ await service2.ensureInitialized();
230
+ services.set(testDir2, service2);
231
+
232
+ writeFileSync(join(testDir, 'file1.txt'), 'content1');
233
+ writeFileSync(join(testDir2, 'file2.txt'), 'content2');
234
+
235
+ const heartbeat = new HeartbeatService({
236
+ getServices: () => services,
237
+ });
238
+
239
+ const result = await heartbeat.commitAllPending();
240
+
241
+ expect(result.checked).toBe(2);
242
+ expect(result.committed).toBe(2);
243
+
244
+ // Clean up second test dir
245
+ rmSync(testDir2, { recursive: true, force: true });
246
+ });
247
+ });
248
+
249
+ describe('uninitialized workspaces', () => {
250
+ test('skips uninitialized workspaces', async () => {
251
+ const uninitDir = join(tmpdir(), `vellum-uninit-${Date.now()}-${Math.random().toString(36).slice(2)}`);
252
+ mkdirSync(uninitDir, { recursive: true });
253
+
254
+ const uninitService = new WorkspaceGitService(uninitDir);
255
+ const mixedServices = new Map<string, WorkspaceGitService>();
256
+ mixedServices.set(uninitDir, uninitService);
257
+ mixedServices.set(testDir, service);
258
+
259
+ writeFileSync(join(testDir, 'file.txt'), 'content');
260
+
261
+ const heartbeat = new HeartbeatService({
262
+ ageThresholdMs: 0,
263
+ fileThreshold: 1,
264
+ getServices: () => mixedServices,
265
+ now: () => Date.now(),
266
+ });
267
+
268
+ // First check to register dirty state, then immediate second check
269
+ await heartbeat.check();
270
+
271
+ const result = await heartbeat.check();
272
+ // Only initialized workspace should be checked
273
+ // The first check committed the file, so the second check sees it clean
274
+ expect(result.checked).toBe(1);
275
+
276
+ rmSync(uninitDir, { recursive: true, force: true });
277
+ });
278
+ });
279
+
280
+ describe('threshold behavior', () => {
281
+ test('resets dirty tracking after successful commit', async () => {
282
+ let currentTime = 1000000;
283
+ const heartbeat = new HeartbeatService({
284
+ ageThresholdMs: 5 * 60 * 1000,
285
+ fileThreshold: 100,
286
+ getServices: () => services,
287
+ now: () => currentTime,
288
+ });
289
+
290
+ // Create changes and register dirty state
291
+ writeFileSync(join(testDir, 'file1.txt'), 'content');
292
+ await heartbeat.check(); // Records first-seen time
293
+
294
+ // Advance past threshold and commit
295
+ currentTime += 6 * 60 * 1000;
296
+ const firstResult = await heartbeat.check();
297
+ expect(firstResult.committed).toBe(1);
298
+
299
+ // Create new changes after the commit
300
+ writeFileSync(join(testDir, 'file2.txt'), 'more content');
301
+
302
+ // Check again immediately -- should not commit (dirty timer was reset)
303
+ const secondResult = await heartbeat.check();
304
+ expect(secondResult.committed).toBe(0);
305
+
306
+ // Advance past threshold again
307
+ currentTime += 6 * 60 * 1000;
308
+ const thirdResult = await heartbeat.check();
309
+ expect(thirdResult.committed).toBe(1);
310
+ });
311
+
312
+ test('commit message includes trigger metadata', async () => {
313
+ writeFileSync(join(testDir, 'file.txt'), 'content');
314
+
315
+ const heartbeat = new HeartbeatService({
316
+ ageThresholdMs: 0,
317
+ fileThreshold: 1,
318
+ getServices: () => services,
319
+ });
320
+
321
+ // Two checks: first registers, second commits (age 0 means immediate on re-check)
322
+ await heartbeat.check();
323
+ await heartbeat.check();
324
+
325
+ const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
326
+ cwd: testDir,
327
+ encoding: 'utf-8',
328
+ });
329
+ expect(commitMsg).toContain('trigger: "heartbeat"');
330
+ });
331
+ });
332
+
333
+ describe('start and stop', () => {
334
+ test('start and stop are idempotent', async () => {
335
+ const heartbeat = new HeartbeatService({
336
+ intervalMs: 60000,
337
+ getServices: () => services,
338
+ });
339
+
340
+ // Should not throw
341
+ heartbeat.start();
342
+ heartbeat.start(); // Idempotent
343
+ await heartbeat.stop();
344
+ await heartbeat.stop(); // Idempotent
345
+ });
346
+ });
347
+ });
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Integration-style test that exercises the full git workspace lifecycle:
3
+ * lazy init → turn-boundary commits → heartbeat safety net → commit history verification.
4
+ *
5
+ * This test wires together WorkspaceGitService, commitTurnChanges, and HeartbeatService
6
+ * in the same flow a real daemon session would follow.
7
+ */
8
+ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
9
+ import { mkdirSync, rmSync, writeFileSync, existsSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { tmpdir } from 'node:os';
12
+ import { execFileSync } from 'node:child_process';
13
+ import {
14
+ WorkspaceGitService,
15
+ getWorkspaceGitService,
16
+ _resetGitServiceRegistry,
17
+ } from '../workspace/git-service.js';
18
+ import { commitTurnChanges } from '../workspace/turn-commit.js';
19
+ import {
20
+ HeartbeatService,
21
+ _resetHeartbeatState,
22
+ } from '../workspace/heartbeat-service.js';
23
+
24
+ describe('Workspace git lifecycle (integration)', () => {
25
+ let testDir: string;
26
+
27
+ beforeEach(() => {
28
+ testDir = join(
29
+ tmpdir(),
30
+ `vellum-lifecycle-${Date.now()}-${Math.random().toString(36).slice(2)}`,
31
+ );
32
+ mkdirSync(testDir, { recursive: true });
33
+ _resetGitServiceRegistry();
34
+ _resetHeartbeatState();
35
+ });
36
+
37
+ afterEach(() => {
38
+ if (existsSync(testDir)) {
39
+ rmSync(testDir, { recursive: true, force: true });
40
+ }
41
+ });
42
+
43
+ // Build a clean git env: strip all GIT_* env vars that CI runners
44
+ // inject, then set GIT_CEILING_DIRECTORIES to isolate test repos.
45
+ function gitEnv(cwd: string): Record<string, string> {
46
+ const env: Record<string, string> = {};
47
+ for (const [key, value] of Object.entries(process.env)) {
48
+ if (value !== undefined && !key.startsWith('GIT_')) {
49
+ env[key] = value;
50
+ }
51
+ }
52
+ env.GIT_CEILING_DIRECTORIES = cwd;
53
+ return env;
54
+ }
55
+
56
+ // Helper to read git log output
57
+ function gitLog(cwd: string, format = '--oneline'): string {
58
+ return execFileSync('git', ['log', format], { cwd, encoding: 'utf-8', env: gitEnv(cwd) }).trim();
59
+ }
60
+
61
+ function commitCount(cwd: string): number {
62
+ return parseInt(
63
+ execFileSync('git', ['rev-list', '--count', 'HEAD'], { cwd, encoding: 'utf-8', env: gitEnv(cwd) }).trim(),
64
+ 10,
65
+ );
66
+ }
67
+
68
+ function lastCommitMessage(cwd: string): string {
69
+ return execFileSync('git', ['log', '-1', '--pretty=%B'], { cwd, encoding: 'utf-8', env: gitEnv(cwd) }).trim();
70
+ }
71
+
72
+ function lastCommitFiles(cwd: string): string[] {
73
+ return execFileSync('git', ['diff', '--name-only', 'HEAD~1', 'HEAD'], {
74
+ cwd,
75
+ encoding: 'utf-8',
76
+ env: gitEnv(cwd),
77
+ })
78
+ .trim()
79
+ .split('\n')
80
+ .filter(Boolean);
81
+ }
82
+
83
+ test('full lifecycle: init → turns → heartbeat → history', async () => {
84
+ const sessionId = 'sess_lifecycle_test';
85
+
86
+ // ----------------------------------------------------------------
87
+ // Step 1: Lazy initialization — workspace starts without a git repo
88
+ // ----------------------------------------------------------------
89
+ expect(existsSync(join(testDir, '.git'))).toBe(false);
90
+
91
+ // Pre-populate workspace with files (simulates existing workspace)
92
+ writeFileSync(join(testDir, 'README.md'), '# My Project');
93
+ writeFileSync(join(testDir, 'config.json'), '{"version": 1}');
94
+
95
+ // Getting the service via the singleton registry is how session.ts does it
96
+ const service = getWorkspaceGitService(testDir);
97
+ await service.ensureInitialized();
98
+
99
+ // Verify lazy init created the repo and the initial commit
100
+ expect(existsSync(join(testDir, '.git'))).toBe(true);
101
+ expect(commitCount(testDir)).toBe(1);
102
+ expect(lastCommitMessage(testDir)).toContain('Initial commit: migrated existing workspace');
103
+
104
+ // ----------------------------------------------------------------
105
+ // Step 2: Turn 1 — assistant edits files, turn-boundary commit fires
106
+ // ----------------------------------------------------------------
107
+ writeFileSync(join(testDir, 'hello.ts'), 'export const greeting = "hello";');
108
+ writeFileSync(join(testDir, 'config.json'), '{"version": 2}');
109
+
110
+ await commitTurnChanges(testDir, sessionId, 1);
111
+
112
+ expect(commitCount(testDir)).toBe(2);
113
+ const turn1Msg = lastCommitMessage(testDir);
114
+ expect(turn1Msg).toContain('Turn:');
115
+ expect(turn1Msg).toContain('Session: sess_lifecycle_test');
116
+ expect(turn1Msg).toContain('Turn: 1');
117
+ expect(turn1Msg).toContain('Files: 2 changed');
118
+ expect(turn1Msg).toMatch(/Timestamp: \d{4}-\d{2}-\d{2}T/);
119
+
120
+ const turn1Files = lastCommitFiles(testDir);
121
+ expect(turn1Files).toContain('hello.ts');
122
+ expect(turn1Files).toContain('config.json');
123
+
124
+ // ----------------------------------------------------------------
125
+ // Step 3: Turn 2 — more edits
126
+ // ----------------------------------------------------------------
127
+ mkdirSync(join(testDir, 'src'), { recursive: true });
128
+ writeFileSync(join(testDir, 'src', 'index.ts'), 'console.log("hello");');
129
+
130
+ await commitTurnChanges(testDir, sessionId, 2);
131
+
132
+ expect(commitCount(testDir)).toBe(3);
133
+ const turn2Msg = lastCommitMessage(testDir);
134
+ expect(turn2Msg).toContain('Turn: 2');
135
+ expect(turn2Msg).toContain('Files: 1 changed');
136
+
137
+ // ----------------------------------------------------------------
138
+ // Step 4: Turn 3 — no changes (should NOT create a commit)
139
+ // ----------------------------------------------------------------
140
+ await commitTurnChanges(testDir, sessionId, 3);
141
+ expect(commitCount(testDir)).toBe(3); // Still 3
142
+
143
+ // ----------------------------------------------------------------
144
+ // Step 5: Heartbeat safety net — simulate uncommitted changes
145
+ // that linger past the age threshold
146
+ // ----------------------------------------------------------------
147
+ writeFileSync(join(testDir, 'background-output.log.txt'), 'background process output');
148
+
149
+ // Build a services map the way the heartbeat does at runtime
150
+ const services = new Map<string, WorkspaceGitService>();
151
+ services.set(testDir, service);
152
+
153
+ let fakeTime = 2_000_000;
154
+ const heartbeat = new HeartbeatService({
155
+ ageThresholdMs: 5 * 60 * 1000,
156
+ fileThreshold: 100,
157
+ getServices: () => services,
158
+ now: () => fakeTime,
159
+ });
160
+
161
+ // First heartbeat: records the dirty timestamp
162
+ const firstCheck = await heartbeat.check();
163
+ expect(firstCheck.committed).toBe(0);
164
+ expect(firstCheck.skipped).toBe(1); // Below threshold
165
+
166
+ // Advance time past 5-minute threshold
167
+ fakeTime += 6 * 60 * 1000;
168
+
169
+ const secondCheck = await heartbeat.check();
170
+ expect(secondCheck.committed).toBe(1);
171
+ expect(commitCount(testDir)).toBe(4);
172
+
173
+ const heartbeatMsg = lastCommitMessage(testDir);
174
+ expect(heartbeatMsg).toContain('auto-commit');
175
+ expect(heartbeatMsg).toContain('heartbeat');
176
+ expect(heartbeatMsg).toContain('safety net');
177
+
178
+ // ----------------------------------------------------------------
179
+ // Step 6: Verify full commit history has correct ordering/metadata
180
+ // ----------------------------------------------------------------
181
+ const fullLog = gitLog(testDir, '--oneline');
182
+ const lines = fullLog.split('\n');
183
+ expect(lines.length).toBe(4);
184
+
185
+ // Most recent first
186
+ expect(lines[0]).toContain('auto-commit');
187
+ expect(lines[1]).toContain('Turn:');
188
+ expect(lines[2]).toContain('Turn:');
189
+ expect(lines[3]).toContain('Initial commit');
190
+
191
+ // ----------------------------------------------------------------
192
+ // Step 7: Shutdown commit — one more file, then graceful shutdown
193
+ // ----------------------------------------------------------------
194
+ writeFileSync(join(testDir, 'unsaved-work.txt'), 'important unsaved data');
195
+
196
+ const shutdownResult = await heartbeat.commitAllPending();
197
+ expect(shutdownResult.committed).toBe(1);
198
+ expect(commitCount(testDir)).toBe(5);
199
+ expect(lastCommitMessage(testDir)).toContain('shutdown');
200
+ });
201
+
202
+ test('workspace recovers from corrupted .git directory', async () => {
203
+ // Initialize a real repo, then corrupt it by removing HEAD.
204
+ // This is more realistic than an empty .git dir (which behaves
205
+ // differently across git versions and CI environments).
206
+ execFileSync('git', ['init', '-b', 'main'], {
207
+ cwd: testDir,
208
+ encoding: 'utf-8',
209
+ env: gitEnv(testDir),
210
+ });
211
+ rmSync(join(testDir, '.git', 'HEAD'));
212
+
213
+ const service = new WorkspaceGitService(testDir);
214
+
215
+ // The key behavior: ensureInitialized must NOT throw on a corrupted repo.
216
+ await service.ensureInitialized();
217
+
218
+ // Service reports as initialized and the repo is functional.
219
+ // We only assert isInitialized() here because on CI runners the
220
+ // corruption recovery path can't fully isolate from the parent
221
+ // checkout repo (git env vars and configs leak through despite
222
+ // cleanGitEnv), causing git status and log helpers to read from
223
+ // the wrong repo.
224
+ expect(service.isInitialized()).toBe(true);
225
+ });
226
+
227
+ test('concurrent turn commits and heartbeats do not conflict', async () => {
228
+ const service = new WorkspaceGitService(testDir);
229
+ await service.ensureInitialized();
230
+
231
+ // Simulate concurrent turn commits and heartbeat checks
232
+ const services = new Map<string, WorkspaceGitService>();
233
+ services.set(testDir, service);
234
+
235
+ const heartbeat = new HeartbeatService({
236
+ ageThresholdMs: 0,
237
+ fileThreshold: 1,
238
+ getServices: () => services,
239
+ });
240
+
241
+ // Create files and fire turn commits + heartbeat checks concurrently
242
+ const operations: Promise<unknown>[] = [];
243
+ for (let i = 0; i < 5; i++) {
244
+ writeFileSync(join(testDir, `concurrent-${i}.txt`), `content ${i}`);
245
+ operations.push(commitTurnChanges(testDir, 'sess_concurrent', i + 1));
246
+ operations.push(heartbeat.check());
247
+ }
248
+
249
+ // None of these should throw (mutex serialization prevents conflicts)
250
+ await Promise.all(operations);
251
+
252
+ // Verify the repo is in a consistent state
253
+ const status = await service.getStatus();
254
+ expect(status.clean).toBe(true);
255
+ });
256
+
257
+ test('fire-and-forget pattern does not lose commits', async () => {
258
+ const service = new WorkspaceGitService(testDir);
259
+ await service.ensureInitialized();
260
+
261
+ // Simulate the session.ts fire-and-forget pattern:
262
+ // `void commitTurnChanges(...)` -- the void discards the promise
263
+ writeFileSync(join(testDir, 'fire-forget-1.txt'), 'content 1');
264
+ const p1 = commitTurnChanges(testDir, 'sess_ff', 1);
265
+
266
+ writeFileSync(join(testDir, 'fire-forget-2.txt'), 'content 2');
267
+ const p2 = commitTurnChanges(testDir, 'sess_ff', 2);
268
+
269
+ // Even though session.ts doesn't await these, they should eventually complete
270
+ await Promise.all([p1, p2]);
271
+
272
+ // All files should be committed (no data loss).
273
+ // The first commit may absorb both files, so the second sees a clean
274
+ // workspace and correctly skips rather than creating an empty commit.
275
+ const count = commitCount(testDir);
276
+ expect(count).toBeGreaterThanOrEqual(2); // initial + at least 1 turn commit
277
+
278
+ // Verify no file changes are lost
279
+ const status = await service.getStatus();
280
+ expect(status.clean).toBe(true);
281
+ });
282
+
283
+ test('getWorkspaceGitService returns same instance across turn commits and heartbeat', async () => {
284
+ // Verify the singleton registry is coherent across all modules
285
+ const fromTurnCommitPath = getWorkspaceGitService(testDir);
286
+ const fromHeartbeatPath = getWorkspaceGitService(testDir);
287
+ const fromDirectCall = getWorkspaceGitService(testDir);
288
+
289
+ expect(fromTurnCommitPath).toBe(fromHeartbeatPath);
290
+ expect(fromHeartbeatPath).toBe(fromDirectCall);
291
+ });
292
+ });
@@ -0,0 +1,35 @@
1
+ import type { ContentBlock } from "../providers/types.js";
2
+
3
+ export interface MessageAttachmentInput {
4
+ id?: string;
5
+ filename: string;
6
+ mimeType: string;
7
+ data: string;
8
+ extractedText?: string;
9
+ }
10
+
11
+ export function attachmentsToContentBlocks(attachments: MessageAttachmentInput[]): ContentBlock[] {
12
+ return attachments.map((attachment) => {
13
+ if (attachment.mimeType.toLowerCase().startsWith("image/")) {
14
+ return {
15
+ type: "image",
16
+ source: {
17
+ type: "base64",
18
+ media_type: attachment.mimeType,
19
+ data: attachment.data,
20
+ },
21
+ } as ContentBlock;
22
+ }
23
+
24
+ return {
25
+ type: "file",
26
+ source: {
27
+ type: "base64",
28
+ media_type: attachment.mimeType,
29
+ data: attachment.data,
30
+ filename: attachment.filename,
31
+ },
32
+ extracted_text: attachment.extractedText,
33
+ } as ContentBlock;
34
+ });
35
+ }