@vellumai/assistant 0.3.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.
- package/.dockerignore +27 -0
- package/.env.example +22 -0
- package/Dockerfile +99 -0
- package/Dockerfile.sandbox +5 -0
- package/README.md +248 -0
- package/bun.lock +1723 -0
- package/bunfig.toml +2 -0
- package/docs/skills.md +158 -0
- package/drizzle/0000_dizzy_maggott.sql +301 -0
- package/drizzle/meta/0000_snapshot.json +1999 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/eslint.config.mjs +17 -0
- package/hook-templates/debug-prompt-logger/hook.json +7 -0
- package/hook-templates/debug-prompt-logger/run.sh +68 -0
- package/knip.json +9 -0
- package/package.json +70 -0
- package/scripts/capture-x-graphql.ts +545 -0
- package/scripts/ipc/check-contract-inventory.ts +104 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +166 -0
- package/scripts/ipc/generate-swift.ts +492 -0
- package/scripts/test-filesystem-tools.sh +48 -0
- package/scripts/test.sh +127 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2485 -0
- package/src/__tests__/account-registry.test.ts +245 -0
- package/src/__tests__/active-skill-tools.test.ts +378 -0
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/agent-loop-thinking.test.ts +81 -0
- package/src/__tests__/agent-loop.test.ts +1135 -0
- package/src/__tests__/anthropic-provider.test.ts +778 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +290 -0
- package/src/__tests__/app-bundler.test.ts +292 -0
- package/src/__tests__/app-executors.test.ts +613 -0
- package/src/__tests__/app-git-history.test.ts +176 -0
- package/src/__tests__/app-git-service.test.ts +169 -0
- package/src/__tests__/app-open-proxy.test.ts +62 -0
- package/src/__tests__/asset-materialize-tool.test.ts +452 -0
- package/src/__tests__/asset-search-tool.test.ts +477 -0
- package/src/__tests__/assistant-attachment-directive.test.ts +401 -0
- package/src/__tests__/assistant-attachments.test.ts +437 -0
- package/src/__tests__/assistant-event-hub.test.ts +226 -0
- package/src/__tests__/assistant-event.test.ts +123 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +315 -0
- package/src/__tests__/attachments-store.test.ts +476 -0
- package/src/__tests__/attachments.test.ts +134 -0
- package/src/__tests__/audit-log-rotation.test.ts +154 -0
- package/src/__tests__/browser-fill-credential.test.ts +309 -0
- package/src/__tests__/browser-manager.test.ts +203 -0
- package/src/__tests__/browser-runtime-check.test.ts +55 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +68 -0
- package/src/__tests__/browser-skill-endstate.test.ts +195 -0
- package/src/__tests__/bundle-scanner.test.ts +313 -0
- package/src/__tests__/call-bridge.test.ts +517 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-domain.test.ts +163 -0
- package/src/__tests__/call-orchestrator.test.ts +625 -0
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +699 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-state.test.ts +174 -0
- package/src/__tests__/call-store.test.ts +691 -0
- package/src/__tests__/channel-approval-routes.test.ts +2356 -0
- package/src/__tests__/channel-approval.test.ts +299 -0
- package/src/__tests__/channel-approvals.test.ts +521 -0
- package/src/__tests__/channel-delivery-store.test.ts +447 -0
- package/src/__tests__/channel-guardian.test.ts +1005 -0
- package/src/__tests__/checker.test.ts +3519 -0
- package/src/__tests__/clarification-resolver.test.ts +159 -0
- package/src/__tests__/classifier.test.ts +67 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +127 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +88 -0
- package/src/__tests__/cli-discover.test.ts +85 -0
- package/src/__tests__/cli.test.ts +26 -0
- package/src/__tests__/clipboard.test.ts +80 -0
- package/src/__tests__/commit-guarantee.test.ts +335 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +550 -0
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-session-compaction.test.ts +132 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +293 -0
- package/src/__tests__/computer-use-session-working-dir.test.ts +117 -0
- package/src/__tests__/computer-use-skill-baseline.test.ts +74 -0
- package/src/__tests__/computer-use-skill-endstate.test.ts +89 -0
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +217 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +107 -0
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +54 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +1462 -0
- package/src/__tests__/conflict-intent-tokenization.test.ts +141 -0
- package/src/__tests__/conflict-policy.test.ts +121 -0
- package/src/__tests__/conflict-store.test.ts +332 -0
- package/src/__tests__/connection-policy.test.ts +102 -0
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/context-memory-e2e.test.ts +434 -0
- package/src/__tests__/context-token-estimator.test.ts +135 -0
- package/src/__tests__/context-window-manager.test.ts +376 -0
- package/src/__tests__/contradiction-checker.test.ts +314 -0
- package/src/__tests__/conversation-store.test.ts +612 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +517 -0
- package/src/__tests__/credential-broker-server-use.test.ts +554 -0
- package/src/__tests__/credential-broker.test.ts +167 -0
- package/src/__tests__/credential-host-pattern-match.test.ts +104 -0
- package/src/__tests__/credential-metadata-store.test.ts +779 -0
- package/src/__tests__/credential-policy-validate.test.ts +121 -0
- package/src/__tests__/credential-resolve.test.ts +328 -0
- package/src/__tests__/credential-security-e2e.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +583 -0
- package/src/__tests__/credential-selection.test.ts +354 -0
- package/src/__tests__/credential-vault-unit.test.ts +780 -0
- package/src/__tests__/credential-vault.test.ts +852 -0
- package/src/__tests__/daemon-assistant-events.test.ts +164 -0
- package/src/__tests__/daemon-server-session-init.test.ts +522 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +97 -0
- package/src/__tests__/diff.test.ts +121 -0
- package/src/__tests__/domain-normalize.test.ts +112 -0
- package/src/__tests__/domain-policy.test.ts +124 -0
- package/src/__tests__/doordash-client.test.ts +186 -0
- package/src/__tests__/doordash-session.test.ts +152 -0
- package/src/__tests__/dynamic-page-surface.test.ts +91 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +132 -0
- package/src/__tests__/edit-engine.test.ts +180 -0
- package/src/__tests__/elevenlabs-client.test.ts +271 -0
- package/src/__tests__/email-cli.test.ts +283 -0
- package/src/__tests__/encrypted-store.test.ts +332 -0
- package/src/__tests__/entity-extractor.test.ts +190 -0
- package/src/__tests__/ephemeral-permissions.test.ts +362 -0
- package/src/__tests__/evaluate-typescript-tool.test.ts +286 -0
- package/src/__tests__/event-bus.test.ts +222 -0
- package/src/__tests__/file-edit-tool.test.ts +122 -0
- package/src/__tests__/file-ops-service.test.ts +330 -0
- package/src/__tests__/file-read-tool.test.ts +75 -0
- package/src/__tests__/file-write-tool.test.ts +113 -0
- package/src/__tests__/filesystem-tools.test.ts +579 -0
- package/src/__tests__/fixtures/credential-security-fixtures.ts +181 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +126 -0
- package/src/__tests__/fixtures/mock-signup-server.ts +387 -0
- package/src/__tests__/fixtures/proxy-fixtures.ts +147 -0
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/forbidden-legacy-symbols.test.ts +71 -0
- package/src/__tests__/fuzzy-match-property.test.ts +216 -0
- package/src/__tests__/fuzzy-match.test.ts +138 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +631 -0
- package/src/__tests__/gemini-image-service.test.ts +261 -0
- package/src/__tests__/gemini-provider.test.ts +651 -0
- package/src/__tests__/get-weather.test.ts +318 -0
- package/src/__tests__/gmail-integration.test.ts +73 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +202 -0
- package/src/__tests__/handlers-cu-observation-blob.test.ts +352 -0
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +191 -0
- package/src/__tests__/handlers-slack-config.test.ts +200 -0
- package/src/__tests__/handlers-task-submit-slash.test.ts +38 -0
- package/src/__tests__/handlers-telegram-config.test.ts +968 -0
- package/src/__tests__/handlers-twilio-config.test.ts +659 -0
- package/src/__tests__/handlers-twitter-config.test.ts +858 -0
- package/src/__tests__/headless-browser-interactions.test.ts +536 -0
- package/src/__tests__/headless-browser-navigate.test.ts +211 -0
- package/src/__tests__/headless-browser-read-tools.test.ts +261 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +185 -0
- package/src/__tests__/history-repair-observability.test.ts +56 -0
- package/src/__tests__/history-repair.test.ts +510 -0
- package/src/__tests__/home-base-bootstrap.test.ts +82 -0
- package/src/__tests__/hooks-blocking.test.ts +128 -0
- package/src/__tests__/hooks-cli.test.ts +144 -0
- package/src/__tests__/hooks-config.test.ts +93 -0
- package/src/__tests__/hooks-discovery.test.ts +199 -0
- package/src/__tests__/hooks-integration.test.ts +189 -0
- package/src/__tests__/hooks-manager.test.ts +187 -0
- package/src/__tests__/hooks-runner.test.ts +182 -0
- package/src/__tests__/hooks-settings.test.ts +154 -0
- package/src/__tests__/hooks-templates.test.ts +137 -0
- package/src/__tests__/hooks-ts-runner.test.ts +125 -0
- package/src/__tests__/hooks-watch.test.ts +100 -0
- package/src/__tests__/host-file-edit-tool.test.ts +228 -0
- package/src/__tests__/host-file-read-tool.test.ts +123 -0
- package/src/__tests__/host-file-write-tool.test.ts +136 -0
- package/src/__tests__/host-shell-tool.test.ts +562 -0
- package/src/__tests__/ingress-reconcile.test.ts +581 -0
- package/src/__tests__/ingress-url-consistency.test.ts +214 -0
- package/src/__tests__/intent-routing.test.ts +259 -0
- package/src/__tests__/ipc-blob-store.test.ts +315 -0
- package/src/__tests__/ipc-contract-inventory.test.ts +54 -0
- package/src/__tests__/ipc-contract.test.ts +74 -0
- package/src/__tests__/ipc-protocol.test.ts +113 -0
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +1769 -0
- package/src/__tests__/ipc-validate.test.ts +407 -0
- package/src/__tests__/key-migration.test.ts +206 -0
- package/src/__tests__/keychain.test.ts +258 -0
- package/src/__tests__/llm-usage-store.test.ts +221 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +257 -0
- package/src/__tests__/managed-store.test.ts +608 -0
- package/src/__tests__/media-generate-image.test.ts +238 -0
- package/src/__tests__/media-reuse-story.e2e.test.ts +676 -0
- package/src/__tests__/media-visibility-policy.test.ts +141 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +235 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +481 -0
- package/src/__tests__/memory-query-builder.test.ts +59 -0
- package/src/__tests__/memory-recall-quality.test.ts +846 -0
- package/src/__tests__/memory-regressions.experimental.test.ts +538 -0
- package/src/__tests__/memory-regressions.test.ts +4435 -0
- package/src/__tests__/memory-retrieval-budget.test.ts +49 -0
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/migration-cli-flows.test.ts +169 -0
- package/src/__tests__/migration-ordering.test.ts +249 -0
- package/src/__tests__/mock-signup-server.test.ts +528 -0
- package/src/__tests__/oauth-callback-registry.test.ts +92 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +285 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +176 -0
- package/src/__tests__/onboarding-template-contract.test.ts +58 -0
- package/src/__tests__/openai-provider.test.ts +753 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/parser.test.ts +472 -0
- package/src/__tests__/path-classifier.test.ts +73 -0
- package/src/__tests__/path-policy.test.ts +435 -0
- package/src/__tests__/platform-move-helper.test.ts +99 -0
- package/src/__tests__/platform-socket-path.test.ts +52 -0
- package/src/__tests__/platform-workspace-migration.test.ts +1000 -0
- package/src/__tests__/platform.test.ts +131 -0
- package/src/__tests__/playbook-execution.test.ts +502 -0
- package/src/__tests__/playbook-tools.test.ts +340 -0
- package/src/__tests__/prebuilt-home-base-seed.test.ts +75 -0
- package/src/__tests__/pricing.test.ts +256 -0
- package/src/__tests__/profile-compiler.test.ts +374 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +342 -0
- package/src/__tests__/provider-registry-ollama.test.ts +16 -0
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/proxy-approval-callback.test.ts +601 -0
- package/src/__tests__/public-ingress-urls.test.ts +256 -0
- package/src/__tests__/qdrant-manager.test.ts +267 -0
- package/src/__tests__/ratelimit.test.ts +297 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +175 -0
- package/src/__tests__/recurrence-engine.test.ts +78 -0
- package/src/__tests__/recurrence-types.test.ts +79 -0
- package/src/__tests__/registry.test.ts +494 -0
- package/src/__tests__/relay-server.test.ts +688 -0
- package/src/__tests__/reminder-store.test.ts +223 -0
- package/src/__tests__/reminder.test.ts +229 -0
- package/src/__tests__/request-file-tool.test.ts +158 -0
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +227 -0
- package/src/__tests__/run-orchestrator.test.ts +425 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +189 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +343 -0
- package/src/__tests__/runtime-events-sse.test.ts +162 -0
- package/src/__tests__/runtime-runs-http.test.ts +438 -0
- package/src/__tests__/runtime-runs.test.ts +260 -0
- package/src/__tests__/sandbox-diagnostics.test.ts +408 -0
- package/src/__tests__/sandbox-host-parity.test.ts +950 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +253 -0
- package/src/__tests__/schedule-store.test.ts +484 -0
- package/src/__tests__/schedule-tools.test.ts +783 -0
- package/src/__tests__/scheduler-recurrence.test.ts +430 -0
- package/src/__tests__/script-proxy-certs.test.ts +90 -0
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +177 -0
- package/src/__tests__/script-proxy-decision-trace.test.ts +156 -0
- package/src/__tests__/script-proxy-http-forwarder.test.ts +281 -0
- package/src/__tests__/script-proxy-injection-runtime.test.ts +401 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +407 -0
- package/src/__tests__/script-proxy-policy-runtime.test.ts +287 -0
- package/src/__tests__/script-proxy-policy.test.ts +310 -0
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +135 -0
- package/src/__tests__/script-proxy-router.test.ts +180 -0
- package/src/__tests__/script-proxy-session-manager.test.ts +382 -0
- package/src/__tests__/script-proxy-session-runtime.test.ts +113 -0
- package/src/__tests__/secret-allowlist.test.ts +230 -0
- package/src/__tests__/secret-ingress-handler.test.ts +110 -0
- package/src/__tests__/secret-onetime-send.test.ts +130 -0
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +106 -0
- package/src/__tests__/secret-response-routing.test.ts +93 -0
- package/src/__tests__/secret-scanner-executor.test.ts +348 -0
- package/src/__tests__/secret-scanner.test.ts +900 -0
- package/src/__tests__/secure-keys.test.ts +323 -0
- package/src/__tests__/server-history-render.test.ts +431 -0
- package/src/__tests__/session-abort-tool-results.test.ts +240 -0
- package/src/__tests__/session-conflict-gate.test.ts +1136 -0
- package/src/__tests__/session-error.test.ts +369 -0
- package/src/__tests__/session-evictor.test.ts +188 -0
- package/src/__tests__/session-init.benchmark.test.ts +465 -0
- package/src/__tests__/session-load-history-repair.test.ts +222 -0
- package/src/__tests__/session-pre-run-repair.test.ts +213 -0
- package/src/__tests__/session-process-bridge.test.ts +242 -0
- package/src/__tests__/session-profile-injection.test.ts +444 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +306 -0
- package/src/__tests__/session-queue.test.ts +1535 -0
- package/src/__tests__/session-runtime-assembly.test.ts +476 -0
- package/src/__tests__/session-runtime-workspace.test.ts +183 -0
- package/src/__tests__/session-skill-tools.test.ts +2431 -0
- package/src/__tests__/session-slash-known.test.ts +368 -0
- package/src/__tests__/session-slash-queue.test.ts +288 -0
- package/src/__tests__/session-slash-unknown.test.ts +271 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +473 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +140 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +140 -0
- package/src/__tests__/session-undo.test.ts +75 -0
- package/src/__tests__/session-workspace-cache-state.test.ts +246 -0
- package/src/__tests__/session-workspace-injection.test.ts +327 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +240 -0
- package/src/__tests__/shared-filesystem-errors.test.ts +78 -0
- package/src/__tests__/shell-credential-ref.test.ts +187 -0
- package/src/__tests__/shell-identity.test.ts +256 -0
- package/src/__tests__/shell-parser-fuzz.test.ts +544 -0
- package/src/__tests__/shell-parser-property.test.ts +433 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +272 -0
- package/src/__tests__/signup-e2e.test.ts +353 -0
- package/src/__tests__/size-guard.test.ts +117 -0
- package/src/__tests__/skill-include-graph.test.ts +303 -0
- package/src/__tests__/skill-load-tool.test.ts +409 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +338 -0
- package/src/__tests__/skill-script-runner-host.test.ts +489 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +349 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/skill-tool-factory.test.ts +252 -0
- package/src/__tests__/skill-tool-manifest.test.ts +658 -0
- package/src/__tests__/skill-version-hash.test.ts +182 -0
- package/src/__tests__/skills.test.ts +680 -0
- package/src/__tests__/slash-commands-catalog.test.ts +86 -0
- package/src/__tests__/slash-commands-parser.test.ts +119 -0
- package/src/__tests__/slash-commands-resolver.test.ts +193 -0
- package/src/__tests__/slash-commands-rewrite.test.ts +39 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/starter-bundle.test.ts +136 -0
- package/src/__tests__/starter-task-flow.test.ts +143 -0
- package/src/__tests__/subagent-manager-notify.test.ts +404 -0
- package/src/__tests__/subagent-tools.test.ts +801 -0
- package/src/__tests__/subagent-types.test.ts +78 -0
- package/src/__tests__/swarm-orchestrator.test.ts +428 -0
- package/src/__tests__/swarm-plan-validator.test.ts +330 -0
- package/src/__tests__/swarm-recursion.test.ts +165 -0
- package/src/__tests__/swarm-router-planner.test.ts +208 -0
- package/src/__tests__/swarm-session-integration.test.ts +274 -0
- package/src/__tests__/swarm-tool.test.ts +145 -0
- package/src/__tests__/swarm-worker-backend.test.ts +129 -0
- package/src/__tests__/swarm-worker-runner.test.ts +272 -0
- package/src/__tests__/system-prompt.test.ts +439 -0
- package/src/__tests__/task-compiler.test.ts +284 -0
- package/src/__tests__/task-management-tools.test.ts +936 -0
- package/src/__tests__/task-runner.test.ts +216 -0
- package/src/__tests__/task-scheduler.test.ts +217 -0
- package/src/__tests__/task-tools.test.ts +595 -0
- package/src/__tests__/terminal-sandbox-docker.test.ts +1064 -0
- package/src/__tests__/terminal-sandbox.integration.test.ts +178 -0
- package/src/__tests__/terminal-sandbox.test.ts +202 -0
- package/src/__tests__/terminal-tools.test.ts +840 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +90 -0
- package/src/__tests__/test-support/computer-use-skill-harness.ts +45 -0
- package/src/__tests__/tool-audit-listener.test.ts +113 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +253 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +516 -0
- package/src/__tests__/tool-executor-redaction.test.ts +289 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +301 -0
- package/src/__tests__/tool-executor.test.ts +1989 -0
- package/src/__tests__/tool-metrics-listener.test.ts +225 -0
- package/src/__tests__/tool-notification-listener.test.ts +49 -0
- package/src/__tests__/tool-permission-simulate-handler.test.ts +336 -0
- package/src/__tests__/tool-policy.test.ts +54 -0
- package/src/__tests__/tool-profiling-listener.test.ts +268 -0
- package/src/__tests__/tool-result-truncation.test.ts +217 -0
- package/src/__tests__/tool-trace-listener.test.ts +226 -0
- package/src/__tests__/top-level-renderer.test.ts +121 -0
- package/src/__tests__/top-level-scanner.test.ts +141 -0
- package/src/__tests__/trace-emitter.test.ts +173 -0
- package/src/__tests__/trust-store.test.ts +1605 -0
- package/src/__tests__/turn-commit.test.ts +554 -0
- package/src/__tests__/twilio-provider.test.ts +329 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +375 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +127 -0
- package/src/__tests__/twilio-routes.test.ts +577 -0
- package/src/__tests__/twitter-auth-handler.test.ts +667 -0
- package/src/__tests__/twitter-cli-error-shaping.test.ts +208 -0
- package/src/__tests__/twitter-cli-routing.test.ts +252 -0
- package/src/__tests__/twitter-oauth-client.test.ts +209 -0
- package/src/__tests__/url-safety.test.ts +418 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/weather-skill-regression.test.ts +225 -0
- package/src/__tests__/web-fetch.test.ts +869 -0
- package/src/__tests__/web-search.test.ts +584 -0
- package/src/__tests__/workspace-git-service.test.ts +1153 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +486 -0
- package/src/__tests__/workspace-lifecycle.test.ts +292 -0
- package/src/__tests__/workspace-policy.test.ts +213 -0
- package/src/agent/attachments.ts +35 -0
- package/src/agent/loop.ts +500 -0
- package/src/agent/message-types.ts +17 -0
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/autonomy/autonomy-resolver.ts +60 -0
- package/src/autonomy/autonomy-store.ts +122 -0
- package/src/autonomy/disposition-mapper.ts +31 -0
- package/src/autonomy/index.ts +11 -0
- package/src/autonomy/types.ts +39 -0
- package/src/bundler/app-bundler.ts +295 -0
- package/src/bundler/bundle-scanner.ts +535 -0
- package/src/bundler/bundle-signer.ts +124 -0
- package/src/bundler/manifest.ts +21 -0
- package/src/bundler/signature-verifier.ts +184 -0
- package/src/calls/call-bridge.ts +168 -0
- package/src/calls/call-constants.ts +48 -0
- package/src/calls/call-domain.ts +430 -0
- package/src/calls/call-orchestrator.ts +498 -0
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-state.ts +87 -0
- package/src/calls/call-store.ts +422 -0
- package/src/calls/elevenlabs-client.ts +97 -0
- package/src/calls/elevenlabs-config.ts +31 -0
- package/src/calls/relay-server.ts +390 -0
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-config.ts +45 -0
- package/src/calls/twilio-provider.ts +263 -0
- package/src/calls/twilio-rest.ts +156 -0
- package/src/calls/twilio-routes.ts +311 -0
- package/src/calls/types.ts +39 -0
- package/src/calls/voice-provider.ts +14 -0
- package/src/calls/voice-quality.ts +114 -0
- package/src/cli/autonomy.ts +188 -0
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/contacts.ts +149 -0
- package/src/cli/core-commands.ts +784 -0
- package/src/cli/doordash.ts +1055 -0
- package/src/cli/email-guardrails.ts +200 -0
- package/src/cli/email.ts +405 -0
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/main-screen.tsx +53 -0
- package/src/cli/map.ts +270 -0
- package/src/cli/twitter.ts +754 -0
- package/src/cli.ts +918 -0
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/.gitkeep +0 -0
- package/src/config/bundled-skills/agentmail/SKILL.md +128 -0
- package/src/config/bundled-skills/agentmail/icon.svg +21 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1404 -0
- package/src/config/bundled-skills/app-builder/TOOLS.json +279 -0
- package/src/config/bundled-skills/app-builder/icon.svg +9 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +15 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +11 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +18 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +11 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-query.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +20 -0
- package/src/config/bundled-skills/browser/SKILL.md +28 -0
- package/src/config/bundled-skills/browser/TOOLS.json +234 -0
- package/src/config/bundled-skills/browser/tools/browser-click.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-close.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-type.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +9 -0
- package/src/config/bundled-skills/claude-code/SKILL.md +50 -0
- package/src/config/bundled-skills/claude-code/TOOLS.json +40 -0
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +9 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +17 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +326 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-done.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-double-click.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-drag.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-key.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-open-app.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-respond.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-right-click.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-run-applescript.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-scroll.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-type-text.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-wait.ts +9 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +57 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +60 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +66 -0
- package/src/config/bundled-skills/document/SKILL.md +26 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +163 -0
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/icon.svg +24 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/google-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/google-calendar/TOOLS.json +108 -0
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +165 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +21 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +42 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +13 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +30 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +41 -0
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +18 -0
- package/src/config/bundled-skills/google-calendar/types.ts +97 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +32 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +42 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +115 -0
- package/src/config/bundled-skills/macos-automation/SKILL.md +66 -0
- package/src/config/bundled-skills/messaging/SKILL.md +153 -0
- package/src/config/bundled-skills/messaging/TOOLS.json +357 -0
- package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +23 -0
- package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +23 -0
- package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +26 -0
- package/src/config/bundled-skills/messaging/tools/gmail-label.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/gmail-trash.ts +23 -0
- package/src/config/bundled-skills/messaging/tools/gmail-unsubscribe.ts +84 -0
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +18 -0
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +125 -0
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +16 -0
- package/src/config/bundled-skills/messaging/tools/messaging-draft.ts +49 -0
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +21 -0
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +28 -0
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +22 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +31 -0
- package/src/config/bundled-skills/messaging/tools/shared.ts +76 -0
- package/src/config/bundled-skills/messaging/tools/slack-add-reaction.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/slack-leave-channel.ts +23 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +533 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +98 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +54 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +76 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +113 -0
- package/src/config/bundled-skills/public-ingress/SKILL.md +200 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/self-upgrade/SKILL.md +68 -0
- package/src/config/bundled-skills/start-the-day/SKILL.md +70 -0
- package/src/config/bundled-skills/start-the-day/icon.svg +13 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +281 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +25 -0
- package/src/config/bundled-skills/transcribe/TOOLS.json +32 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +370 -0
- package/src/config/bundled-skills/twitter/SKILL.md +220 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/bundled-skills/weather/SKILL.md +37 -0
- package/src/config/bundled-skills/weather/TOOLS.json +32 -0
- package/src/config/bundled-skills/weather/icon.svg +24 -0
- package/src/config/bundled-skills/weather/tools/get-weather.ts +9 -0
- package/src/config/computer-use-prompt.ts +97 -0
- package/src/config/defaults.ts +263 -0
- package/src/config/loader.ts +339 -0
- package/src/config/schema.ts +1436 -0
- package/src/config/skill-state.ts +95 -0
- package/src/config/skills.ts +972 -0
- package/src/config/system-prompt.ts +675 -0
- package/src/config/templates/BOOTSTRAP.md +70 -0
- package/src/config/templates/IDENTITY.md +25 -0
- package/src/config/templates/LOOKS.md +25 -0
- package/src/config/templates/SOUL.md +37 -0
- package/src/config/templates/USER.md +19 -0
- package/src/config/types.ts +42 -0
- package/src/config/vellum-skills/chatgpt-import/SKILL.md +24 -0
- package/src/config/vellum-skills/chatgpt-import/TOOLS.json +23 -0
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +284 -0
- package/src/config/vellum-skills/deploy-fullstack-vercel/SKILL.md +179 -0
- package/src/config/vellum-skills/document-writer/SKILL.md +195 -0
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +199 -0
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +153 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +143 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +213 -0
- package/src/contacts/contact-store.ts +410 -0
- package/src/contacts/index.ts +11 -0
- package/src/contacts/types.ts +28 -0
- package/src/context/token-estimator.ts +108 -0
- package/src/context/tool-result-truncation.ts +128 -0
- package/src/context/window-manager.ts +531 -0
- package/src/daemon/assistant-attachments.ts +691 -0
- package/src/daemon/classifier.ts +110 -0
- package/src/daemon/computer-use-session.ts +903 -0
- package/src/daemon/connection-policy.ts +41 -0
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +530 -0
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +187 -0
- package/src/daemon/handlers/config.ts +1517 -0
- package/src/daemon/handlers/diagnostics.ts +338 -0
- package/src/daemon/handlers/documents.ts +173 -0
- package/src/daemon/handlers/home-base.ts +78 -0
- package/src/daemon/handlers/identity.ts +127 -0
- package/src/daemon/handlers/index.ts +129 -0
- package/src/daemon/handlers/misc.ts +331 -0
- package/src/daemon/handlers/open-bundle-handler.ts +80 -0
- package/src/daemon/handlers/publish.ts +187 -0
- package/src/daemon/handlers/sessions.ts +555 -0
- package/src/daemon/handlers/shared.ts +570 -0
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +486 -0
- package/src/daemon/handlers/subagents.ts +210 -0
- package/src/daemon/handlers/twitter-auth.ts +198 -0
- package/src/daemon/handlers/work-items.ts +632 -0
- package/src/daemon/handlers/workspace-files.ts +75 -0
- package/src/daemon/handlers.ts +17 -0
- package/src/daemon/history-repair.ts +214 -0
- package/src/daemon/ipc-blob-store.ts +231 -0
- package/src/daemon/ipc-contract-inventory.json +495 -0
- package/src/daemon/ipc-contract-inventory.ts +126 -0
- package/src/daemon/ipc-contract.ts +2551 -0
- package/src/daemon/ipc-protocol.ts +75 -0
- package/src/daemon/ipc-validate.ts +188 -0
- package/src/daemon/lifecycle.ts +582 -0
- package/src/daemon/main.ts +21 -0
- package/src/daemon/media-visibility-policy.ts +57 -0
- package/src/daemon/ride-shotgun-handler.ts +309 -0
- package/src/daemon/server.ts +1215 -0
- package/src/daemon/session-agent-loop.ts +922 -0
- package/src/daemon/session-attachments.ts +196 -0
- package/src/daemon/session-conflict-gate.ts +184 -0
- package/src/daemon/session-dynamic-profile.ts +63 -0
- package/src/daemon/session-error.ts +290 -0
- package/src/daemon/session-evictor.ts +196 -0
- package/src/daemon/session-history.ts +437 -0
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-memory.ts +212 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +193 -0
- package/src/daemon/session-process.ts +323 -0
- package/src/daemon/session-queue-manager.ts +82 -0
- package/src/daemon/session-runtime-assembly.ts +447 -0
- package/src/daemon/session-skill-tools.ts +356 -0
- package/src/daemon/session-slash.ts +305 -0
- package/src/daemon/session-surfaces.ts +702 -0
- package/src/daemon/session-tool-setup.ts +523 -0
- package/src/daemon/session-usage.ts +72 -0
- package/src/daemon/session-workspace.ts +19 -0
- package/src/daemon/session.ts +400 -0
- package/src/daemon/tls-certs.ts +189 -0
- package/src/daemon/trace-emitter.ts +82 -0
- package/src/daemon/video-thumbnail.ts +62 -0
- package/src/daemon/watch-handler.ts +274 -0
- package/src/doordash/client.ts +999 -0
- package/src/doordash/queries.ts +1311 -0
- package/src/doordash/query-extractor.ts +93 -0
- package/src/doordash/session.ts +82 -0
- package/src/email/provider.ts +117 -0
- package/src/email/providers/agentmail.ts +317 -0
- package/src/email/providers/index.ts +58 -0
- package/src/email/service.ts +303 -0
- package/src/email/types.ts +126 -0
- package/src/events/bus.ts +157 -0
- package/src/events/domain-events.ts +83 -0
- package/src/events/index.ts +18 -0
- package/src/events/tool-audit-listener.ts +80 -0
- package/src/events/tool-domain-event-publisher.ts +111 -0
- package/src/events/tool-metrics-listener.ts +159 -0
- package/src/events/tool-notification-listener.ts +17 -0
- package/src/events/tool-profiling-listener.ts +158 -0
- package/src/events/tool-trace-listener.ts +75 -0
- package/src/export/formatter.ts +98 -0
- package/src/followups/followup-store.ts +168 -0
- package/src/followups/index.ts +10 -0
- package/src/followups/types.ts +29 -0
- package/src/gallery/default-gallery.ts +795 -0
- package/src/gallery/gallery-manifest.ts +24 -0
- package/src/home-base/app-link-store.ts +82 -0
- package/src/home-base/bootstrap.ts +68 -0
- package/src/home-base/prebuilt/index.html +662 -0
- package/src/home-base/prebuilt/seed-metadata.json +21 -0
- package/src/home-base/prebuilt/seed.ts +112 -0
- package/src/home-base/prebuilt-home-base-updater.ts +30 -0
- package/src/hooks/cli.ts +163 -0
- package/src/hooks/config.ts +88 -0
- package/src/hooks/discovery.ts +110 -0
- package/src/hooks/manager.ts +124 -0
- package/src/hooks/runner.ts +123 -0
- package/src/hooks/templates.ts +52 -0
- package/src/hooks/types.ts +72 -0
- package/src/inbound/public-ingress-urls.ts +123 -0
- package/src/index.ts +81 -0
- package/src/instrument.ts +60 -0
- package/src/logfire.ts +99 -0
- package/src/media/gemini-image-service.ts +136 -0
- package/src/memory/account-store.ts +108 -0
- package/src/memory/admin.ts +211 -0
- package/src/memory/app-git-service.ts +295 -0
- package/src/memory/app-store.ts +577 -0
- package/src/memory/attachments-store.ts +397 -0
- package/src/memory/channel-delivery-store.ts +353 -0
- package/src/memory/channel-guardian-store.ts +669 -0
- package/src/memory/checkpoints.ts +52 -0
- package/src/memory/clarification-resolver.ts +298 -0
- package/src/memory/conflict-intent.ts +157 -0
- package/src/memory/conflict-policy.ts +73 -0
- package/src/memory/conflict-store.ts +350 -0
- package/src/memory/contradiction-checker.ts +358 -0
- package/src/memory/conversation-key-store.ts +122 -0
- package/src/memory/conversation-store.ts +470 -0
- package/src/memory/db.ts +1991 -0
- package/src/memory/embedding-backend.ts +229 -0
- package/src/memory/embedding-gemini.ts +52 -0
- package/src/memory/embedding-local.ts +65 -0
- package/src/memory/embedding-ollama.ts +55 -0
- package/src/memory/embedding-openai.ts +25 -0
- package/src/memory/entity-extractor.ts +474 -0
- package/src/memory/external-conversation-store.ts +234 -0
- package/src/memory/fingerprint.ts +20 -0
- package/src/memory/indexer.ts +156 -0
- package/src/memory/items-extractor.ts +461 -0
- package/src/memory/job-handlers/backfill.ts +139 -0
- package/src/memory/job-handlers/cleanup.ts +58 -0
- package/src/memory/job-handlers/conflict.ts +141 -0
- package/src/memory/job-handlers/embedding.ts +61 -0
- package/src/memory/job-handlers/extraction.ts +123 -0
- package/src/memory/job-handlers/index-maintenance.ts +54 -0
- package/src/memory/job-handlers/summarization.ts +286 -0
- package/src/memory/job-utils.ts +170 -0
- package/src/memory/jobs-store.ts +401 -0
- package/src/memory/jobs-worker.ts +313 -0
- package/src/memory/llm-request-log-store.ts +45 -0
- package/src/memory/llm-usage-store.ts +60 -0
- package/src/memory/message-content.ts +54 -0
- package/src/memory/profile-compiler.ts +160 -0
- package/src/memory/published-pages-store.ts +137 -0
- package/src/memory/qdrant-client.ts +366 -0
- package/src/memory/qdrant-manager.ts +242 -0
- package/src/memory/query-builder.ts +45 -0
- package/src/memory/retrieval-budget.ts +30 -0
- package/src/memory/retriever.ts +653 -0
- package/src/memory/runs-store.ts +305 -0
- package/src/memory/schema.ts +677 -0
- package/src/memory/search/entity.ts +298 -0
- package/src/memory/search/formatting.ts +207 -0
- package/src/memory/search/lexical.ts +227 -0
- package/src/memory/search/ranking.ts +401 -0
- package/src/memory/search/semantic.ts +121 -0
- package/src/memory/search/types.ts +137 -0
- package/src/memory/segmenter.ts +68 -0
- package/src/memory/shared-app-links-store.ts +138 -0
- package/src/memory/tool-usage-store.ts +62 -0
- package/src/messaging/activity-analyzer.ts +76 -0
- package/src/messaging/draft-store.ts +88 -0
- package/src/messaging/index.ts +3 -0
- package/src/messaging/provider-types.ts +80 -0
- package/src/messaging/provider.ts +52 -0
- package/src/messaging/providers/gmail/adapter.ts +193 -0
- package/src/messaging/providers/gmail/client.ts +204 -0
- package/src/messaging/providers/gmail/types.ts +90 -0
- package/src/messaging/providers/slack/adapter.ts +202 -0
- package/src/messaging/providers/slack/client.ts +198 -0
- package/src/messaging/providers/slack/types.ts +119 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +162 -0
- package/src/messaging/providers/telegram-bot/client.ts +104 -0
- package/src/messaging/providers/telegram-bot/types.ts +15 -0
- package/src/messaging/registry.ts +35 -0
- package/src/messaging/style-analyzer.ts +159 -0
- package/src/messaging/thread-summarizer.ts +306 -0
- package/src/messaging/triage-engine.ts +323 -0
- package/src/messaging/types.ts +55 -0
- package/src/permissions/checker.ts +640 -0
- package/src/permissions/defaults.ts +254 -0
- package/src/permissions/prompter.ts +98 -0
- package/src/permissions/secret-prompter.ts +114 -0
- package/src/permissions/shell-identity.ts +227 -0
- package/src/permissions/trust-store.ts +607 -0
- package/src/permissions/types.ts +43 -0
- package/src/permissions/workspace-policy.ts +114 -0
- package/src/playbooks/index.ts +2 -0
- package/src/playbooks/playbook-compiler.ts +90 -0
- package/src/playbooks/types.ts +55 -0
- package/src/providers/anthropic/client.ts +751 -0
- package/src/providers/failover.ts +129 -0
- package/src/providers/fireworks/client.ts +20 -0
- package/src/providers/gemini/client.ts +285 -0
- package/src/providers/ollama/client.ts +30 -0
- package/src/providers/openai/client.ts +337 -0
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/ratelimit.ts +93 -0
- package/src/providers/registry.ts +146 -0
- package/src/providers/retry.ts +81 -0
- package/src/providers/stream-timeout.ts +38 -0
- package/src/providers/types.ts +109 -0
- package/src/runtime/assistant-event-hub.ts +157 -0
- package/src/runtime/assistant-event.ts +82 -0
- package/src/runtime/channel-approval-parser.ts +60 -0
- package/src/runtime/channel-approval-types.ts +73 -0
- package/src/runtime/channel-approvals.ts +206 -0
- package/src/runtime/channel-guardian-service.ts +212 -0
- package/src/runtime/gateway-client.ts +58 -0
- package/src/runtime/http-server.ts +1076 -0
- package/src/runtime/http-types.ts +66 -0
- package/src/runtime/routes/app-routes.ts +174 -0
- package/src/runtime/routes/attachment-routes.ts +133 -0
- package/src/runtime/routes/call-routes.ts +190 -0
- package/src/runtime/routes/channel-routes.ts +1404 -0
- package/src/runtime/routes/conversation-routes.ts +352 -0
- package/src/runtime/routes/events-routes.ts +148 -0
- package/src/runtime/routes/run-routes.ts +257 -0
- package/src/runtime/routes/secret-routes.ts +76 -0
- package/src/runtime/run-orchestrator.ts +330 -0
- package/src/schedule/recurrence-engine.ts +162 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +506 -0
- package/src/schedule/scheduler.ts +171 -0
- package/src/security/encrypted-store.ts +238 -0
- package/src/security/keychain.ts +252 -0
- package/src/security/oauth-callback-registry.ts +66 -0
- package/src/security/oauth2.ts +274 -0
- package/src/security/redaction.ts +89 -0
- package/src/security/secret-allowlist.ts +164 -0
- package/src/security/secret-ingress.ts +57 -0
- package/src/security/secret-scanner.ts +550 -0
- package/src/security/secure-keys.ts +180 -0
- package/src/security/token-manager.ts +141 -0
- package/src/services/published-app-updater.ts +69 -0
- package/src/services/vercel-deploy.ts +73 -0
- package/src/skills/active-skill-tools.ts +81 -0
- package/src/skills/clawhub.ts +414 -0
- package/src/skills/include-graph.ts +146 -0
- package/src/skills/managed-store.ts +233 -0
- package/src/skills/path-classifier.ts +128 -0
- package/src/skills/slash-commands.ts +174 -0
- package/src/skills/tool-manifest.ts +165 -0
- package/src/skills/version-hash.ts +110 -0
- package/src/slack/slack-webhook.ts +61 -0
- package/src/subagent/index.ts +19 -0
- package/src/subagent/manager.ts +511 -0
- package/src/subagent/types.ts +69 -0
- package/src/swarm/backend-claude-code.ts +145 -0
- package/src/swarm/index.ts +44 -0
- package/src/swarm/limits.ts +37 -0
- package/src/swarm/orchestrator.ts +279 -0
- package/src/swarm/plan-validator.ts +151 -0
- package/src/swarm/router-planner.ts +100 -0
- package/src/swarm/router-prompts.ts +36 -0
- package/src/swarm/synthesizer.ts +62 -0
- package/src/swarm/types.ts +62 -0
- package/src/swarm/worker-backend.ts +121 -0
- package/src/swarm/worker-prompts.ts +79 -0
- package/src/swarm/worker-runner.ts +164 -0
- package/src/tasks/SPEC.md +139 -0
- package/src/tasks/candidate-store.ts +86 -0
- package/src/tasks/ephemeral-permissions.ts +48 -0
- package/src/tasks/task-compiler.ts +199 -0
- package/src/tasks/task-runner.ts +90 -0
- package/src/tasks/task-scheduler.ts +21 -0
- package/src/tasks/task-store.ts +127 -0
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/apps/definitions.ts +59 -0
- package/src/tools/apps/executors.ts +313 -0
- package/src/tools/apps/open-proxy.ts +43 -0
- package/src/tools/apps/registry.ts +16 -0
- package/src/tools/assets/materialize.ts +218 -0
- package/src/tools/assets/search.ts +361 -0
- package/src/tools/browser/__tests__/auth-cache.test.ts +219 -0
- package/src/tools/browser/__tests__/auth-detector.test.ts +362 -0
- package/src/tools/browser/__tests__/jit-auth.test.ts +189 -0
- package/src/tools/browser/api-map.ts +293 -0
- package/src/tools/browser/auth-cache.ts +149 -0
- package/src/tools/browser/auth-detector.ts +347 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +980 -0
- package/src/tools/browser/browser-handoff.ts +79 -0
- package/src/tools/browser/browser-manager.ts +715 -0
- package/src/tools/browser/browser-screencast.ts +217 -0
- package/src/tools/browser/headless-browser.ts +450 -0
- package/src/tools/browser/jit-auth.ts +51 -0
- package/src/tools/browser/network-recorder.ts +349 -0
- package/src/tools/browser/network-recording-types.ts +49 -0
- package/src/tools/browser/recording-store.ts +49 -0
- package/src/tools/browser/runtime-check.ts +43 -0
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +67 -0
- package/src/tools/calls/call-start.ts +81 -0
- package/src/tools/calls/call-status.ts +81 -0
- package/src/tools/claude-code/claude-code.ts +428 -0
- package/src/tools/computer-use/definitions.ts +443 -0
- package/src/tools/computer-use/registry.ts +22 -0
- package/src/tools/computer-use/request-computer-control.ts +53 -0
- package/src/tools/computer-use/skill-proxy-bridge.ts +28 -0
- package/src/tools/credentials/account-registry.ts +127 -0
- package/src/tools/credentials/broker-types.ts +107 -0
- package/src/tools/credentials/broker.ts +372 -0
- package/src/tools/credentials/domain-policy.ts +51 -0
- package/src/tools/credentials/host-pattern-match.ts +60 -0
- package/src/tools/credentials/metadata-store.ts +335 -0
- package/src/tools/credentials/policy-types.ts +52 -0
- package/src/tools/credentials/policy-validate.ts +80 -0
- package/src/tools/credentials/resolve.ts +122 -0
- package/src/tools/credentials/selection.ts +159 -0
- package/src/tools/credentials/tool-policy.ts +25 -0
- package/src/tools/credentials/vault.ts +657 -0
- package/src/tools/document/document-tool.ts +92 -0
- package/src/tools/document/editor-template.ts +237 -0
- package/src/tools/execution-target.ts +21 -0
- package/src/tools/execution-timeout.ts +49 -0
- package/src/tools/executor.ts +815 -0
- package/src/tools/filesystem/edit.ts +127 -0
- package/src/tools/filesystem/fuzzy-match.ts +202 -0
- package/src/tools/filesystem/read.ts +71 -0
- package/src/tools/filesystem/view-image.ts +199 -0
- package/src/tools/filesystem/write.ts +79 -0
- package/src/tools/followups/followup_create.ts +76 -0
- package/src/tools/followups/followup_list.ts +60 -0
- package/src/tools/followups/followup_resolve.ts +56 -0
- package/src/tools/host-filesystem/edit.ts +125 -0
- package/src/tools/host-filesystem/read.ts +80 -0
- package/src/tools/host-filesystem/write.ts +76 -0
- package/src/tools/host-terminal/cli-discover.ts +180 -0
- package/src/tools/host-terminal/host-shell.ts +191 -0
- package/src/tools/memory/definitions.ts +69 -0
- package/src/tools/memory/handlers.ts +246 -0
- package/src/tools/memory/register.ts +66 -0
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/domain-normalize.ts +85 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/script-proxy/certs.ts +237 -0
- package/src/tools/network/script-proxy/connect-tunnel.ts +82 -0
- package/src/tools/network/script-proxy/http-forwarder.ts +151 -0
- package/src/tools/network/script-proxy/index.ts +28 -0
- package/src/tools/network/script-proxy/logging.ts +196 -0
- package/src/tools/network/script-proxy/mitm-handler.ts +269 -0
- package/src/tools/network/script-proxy/policy.ts +152 -0
- package/src/tools/network/script-proxy/router.ts +60 -0
- package/src/tools/network/script-proxy/server.ts +136 -0
- package/src/tools/network/script-proxy/session-manager.ts +534 -0
- package/src/tools/network/script-proxy/types.ts +125 -0
- package/src/tools/network/url-safety.ts +227 -0
- package/src/tools/network/web-fetch.ts +713 -0
- package/src/tools/network/web-search.ts +296 -0
- package/src/tools/policy-context.ts +29 -0
- package/src/tools/registry.ts +295 -0
- package/src/tools/reminder/reminder-store.ts +148 -0
- package/src/tools/reminder/reminder.ts +80 -0
- package/src/tools/schedule/create.ts +81 -0
- package/src/tools/schedule/delete.ts +28 -0
- package/src/tools/schedule/list.ts +69 -0
- package/src/tools/schedule/update.ts +97 -0
- package/src/tools/shared/filesystem/edit-engine.ts +56 -0
- package/src/tools/shared/filesystem/errors.ts +85 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +215 -0
- package/src/tools/shared/filesystem/format-diff.ts +35 -0
- package/src/tools/shared/filesystem/path-policy.ts +125 -0
- package/src/tools/shared/filesystem/size-guard.ts +41 -0
- package/src/tools/shared/filesystem/types.ts +80 -0
- package/src/tools/shared/shell-output.ts +52 -0
- package/src/tools/skills/delete-managed.ts +60 -0
- package/src/tools/skills/load.ts +139 -0
- package/src/tools/skills/sandbox-runner.ts +279 -0
- package/src/tools/skills/scaffold-managed.ts +150 -0
- package/src/tools/skills/script-contract.ts +6 -0
- package/src/tools/skills/skill-script-runner.ts +86 -0
- package/src/tools/skills/skill-tool-factory.ts +64 -0
- package/src/tools/skills/vellum-catalog.ts +217 -0
- package/src/tools/subagent/abort.ts +33 -0
- package/src/tools/subagent/message.ts +39 -0
- package/src/tools/subagent/read.ts +67 -0
- package/src/tools/subagent/spawn.ts +46 -0
- package/src/tools/subagent/status.ts +45 -0
- package/src/tools/swarm/delegate.ts +183 -0
- package/src/tools/system/request-permission.ts +98 -0
- package/src/tools/system/version.ts +43 -0
- package/src/tools/tasks/index.ts +27 -0
- package/src/tools/tasks/task-delete.ts +82 -0
- package/src/tools/tasks/task-list.ts +44 -0
- package/src/tools/tasks/task-run.ts +97 -0
- package/src/tools/tasks/task-save.ts +47 -0
- package/src/tools/tasks/work-item-enqueue.ts +234 -0
- package/src/tools/tasks/work-item-list.ts +55 -0
- package/src/tools/tasks/work-item-remove.ts +60 -0
- package/src/tools/tasks/work-item-run.ts +78 -0
- package/src/tools/tasks/work-item-update.ts +114 -0
- package/src/tools/terminal/backends/docker.ts +372 -0
- package/src/tools/terminal/backends/native.ts +190 -0
- package/src/tools/terminal/backends/types.ts +26 -0
- package/src/tools/terminal/evaluate-typescript.ts +275 -0
- package/src/tools/terminal/parser.ts +413 -0
- package/src/tools/terminal/safe-env.ts +37 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +149 -0
- package/src/tools/terminal/sandbox.ts +44 -0
- package/src/tools/terminal/shell.ts +257 -0
- package/src/tools/tool-manifest.ts +198 -0
- package/src/tools/types.ts +176 -0
- package/src/tools/ui-surface/definitions.ts +244 -0
- package/src/tools/ui-surface/registry.ts +14 -0
- package/src/tools/watch/screen-watch.ts +130 -0
- package/src/tools/watch/watch-state.ts +119 -0
- package/src/tools/watcher/create.ts +64 -0
- package/src/tools/watcher/delete.ts +27 -0
- package/src/tools/watcher/digest.ts +50 -0
- package/src/tools/watcher/list.ts +60 -0
- package/src/tools/watcher/update.ts +56 -0
- package/src/tools/weather/service.ts +551 -0
- package/src/twitter/client.ts +690 -0
- package/src/twitter/oauth-client.ts +102 -0
- package/src/twitter/router.ts +101 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/actors.ts +24 -0
- package/src/usage/types.ts +37 -0
- package/src/util/clipboard.ts +33 -0
- package/src/util/content-id.ts +16 -0
- package/src/util/debounce.ts +88 -0
- package/src/util/diff.ts +181 -0
- package/src/util/errors.ts +129 -0
- package/src/util/logger.ts +243 -0
- package/src/util/network-info.ts +47 -0
- package/src/util/platform.ts +632 -0
- package/src/util/pricing.ts +150 -0
- package/src/util/promise-guard.ts +37 -0
- package/src/util/retry.ts +98 -0
- package/src/util/spinner.ts +51 -0
- package/src/util/time.ts +16 -0
- package/src/util/truncate.ts +6 -0
- package/src/util/xml.ts +4 -0
- package/src/version.ts +3 -0
- package/src/watcher/constants.ts +11 -0
- package/src/watcher/engine.ts +199 -0
- package/src/watcher/provider-registry.ts +15 -0
- package/src/watcher/provider-types.ts +48 -0
- package/src/watcher/providers/gmail.ts +198 -0
- package/src/watcher/providers/google-calendar.ts +228 -0
- package/src/watcher/providers/slack.ts +129 -0
- package/src/watcher/watcher-store.ts +419 -0
- package/src/work-items/work-item-runner.ts +171 -0
- package/src/work-items/work-item-store.ts +325 -0
- package/src/workspace/commit-message-enrichment-service.ts +284 -0
- package/src/workspace/commit-message-provider.ts +95 -0
- package/src/workspace/git-service.ts +857 -0
- package/src/workspace/heartbeat-service.ts +345 -0
- package/src/workspace/provider-commit-message-generator.ts +285 -0
- package/src/workspace/top-level-renderer.ts +19 -0
- package/src/workspace/top-level-scanner.ts +41 -0
- package/src/workspace/turn-commit.ts +175 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { getLogger } from '../util/logger.js';
|
|
6
|
+
import { getConfig } from '../config/loader.js';
|
|
7
|
+
import { PromiseGuard } from '../util/promise-guard.js';
|
|
8
|
+
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const log = getLogger('workspace-git');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build a clean env for git subprocesses.
|
|
14
|
+
*
|
|
15
|
+
* Strips all GIT_* env vars (e.g. GIT_DIR, GIT_WORK_TREE) that CI runners
|
|
16
|
+
* or parent processes may set, then adds GIT_CEILING_DIRECTORIES to prevent
|
|
17
|
+
* walking up to a parent repo.
|
|
18
|
+
*/
|
|
19
|
+
function cleanGitEnv(workspaceDir: string): Record<string, string> {
|
|
20
|
+
const env: Record<string, string> = {};
|
|
21
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
22
|
+
if (value !== undefined && !key.startsWith('GIT_')) {
|
|
23
|
+
env[key] = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
env.GIT_CEILING_DIRECTORIES = workspaceDir;
|
|
27
|
+
return env;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Patterns excluded from workspace git tracking.
|
|
32
|
+
* These are written to .gitignore on init and appended to existing .gitignore files.
|
|
33
|
+
*/
|
|
34
|
+
const WORKSPACE_GITIGNORE_RULES = [
|
|
35
|
+
'data/db/',
|
|
36
|
+
'data/qdrant/',
|
|
37
|
+
'data/ipc-blobs/',
|
|
38
|
+
'logs/',
|
|
39
|
+
'*.log',
|
|
40
|
+
'*.sock',
|
|
41
|
+
'*.pid',
|
|
42
|
+
'*.sqlite',
|
|
43
|
+
'*.sqlite-journal',
|
|
44
|
+
'*.sqlite-wal',
|
|
45
|
+
'*.sqlite-shm',
|
|
46
|
+
'*.db',
|
|
47
|
+
'*.db-journal',
|
|
48
|
+
'*.db-wal',
|
|
49
|
+
'*.db-shm',
|
|
50
|
+
'vellum.sock',
|
|
51
|
+
'vellum.pid',
|
|
52
|
+
'session-token',
|
|
53
|
+
'http-token',
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/** Properties added by Node's child_process errors. */
|
|
57
|
+
interface ExecError extends Error {
|
|
58
|
+
killed?: boolean;
|
|
59
|
+
signal?: string;
|
|
60
|
+
code?: string | number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Simple mutex implementation for per-workspace git operation serialization.
|
|
65
|
+
* Prevents concurrent git operations from corrupting the repository state.
|
|
66
|
+
*/
|
|
67
|
+
class Mutex {
|
|
68
|
+
private locked = false;
|
|
69
|
+
private waitQueue: Array<() => void> = [];
|
|
70
|
+
|
|
71
|
+
async acquire(): Promise<void> {
|
|
72
|
+
if (!this.locked) {
|
|
73
|
+
this.locked = true;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Wait for the lock to be released
|
|
77
|
+
await new Promise<void>((resolve) => {
|
|
78
|
+
this.waitQueue.push(resolve);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
release(): void {
|
|
83
|
+
const next = this.waitQueue.shift();
|
|
84
|
+
if (next) {
|
|
85
|
+
next();
|
|
86
|
+
} else {
|
|
87
|
+
this.locked = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Execute a function while holding the lock.
|
|
93
|
+
* Automatically releases the lock when done, even if the function throws.
|
|
94
|
+
*/
|
|
95
|
+
async withLock<T>(fn: () => Promise<T>): Promise<T> {
|
|
96
|
+
await this.acquire();
|
|
97
|
+
try {
|
|
98
|
+
return await fn();
|
|
99
|
+
} finally {
|
|
100
|
+
this.release();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
interface GitCommitMetadata {
|
|
106
|
+
/** Optional metadata to include in the commit message or as git notes */
|
|
107
|
+
[key: string]: unknown;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface GitStatus {
|
|
111
|
+
/** Files staged for commit */
|
|
112
|
+
staged: string[];
|
|
113
|
+
/** Files modified but not staged */
|
|
114
|
+
modified: string[];
|
|
115
|
+
/** Untracked files */
|
|
116
|
+
untracked: string[];
|
|
117
|
+
/** True if the working directory is clean */
|
|
118
|
+
clean: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Git service for workspace change management.
|
|
123
|
+
*
|
|
124
|
+
* Provides git-backed tracking of workspace state with lazy initialization.
|
|
125
|
+
* Each workspace gets its own git repository initialized on first write.
|
|
126
|
+
*
|
|
127
|
+
* Key features:
|
|
128
|
+
* - Lazy initialization: git repo created only when needed
|
|
129
|
+
* - Mutex-protected operations: prevents concurrent git command conflicts
|
|
130
|
+
* - Handles both new and existing workspaces transparently
|
|
131
|
+
* - Synchronous initial commit within mutex to prevent races
|
|
132
|
+
*/
|
|
133
|
+
export class WorkspaceGitService {
|
|
134
|
+
private readonly workspaceDir: string;
|
|
135
|
+
private readonly mutex: Mutex;
|
|
136
|
+
private initialized = false;
|
|
137
|
+
private readonly initGuard = new PromiseGuard<void>();
|
|
138
|
+
private consecutiveFailures = 0;
|
|
139
|
+
private nextAllowedAttemptMs = 0;
|
|
140
|
+
private initConsecutiveFailures = 0;
|
|
141
|
+
private initNextAllowedAttemptMs = 0;
|
|
142
|
+
|
|
143
|
+
constructor(workspaceDir: string) {
|
|
144
|
+
this.workspaceDir = workspaceDir;
|
|
145
|
+
this.mutex = new Mutex();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Check if the circuit breaker is open (too many recent failures).
|
|
150
|
+
* When open, commit attempts are skipped until the backoff window expires.
|
|
151
|
+
*/
|
|
152
|
+
private isBreakerOpen(): boolean {
|
|
153
|
+
if (this.consecutiveFailures === 0) return false;
|
|
154
|
+
return Date.now() < this.nextAllowedAttemptMs;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private recordSuccess(): void {
|
|
158
|
+
if (this.consecutiveFailures > 0) {
|
|
159
|
+
log.info(
|
|
160
|
+
{ workspaceDir: this.workspaceDir, previousFailures: this.consecutiveFailures },
|
|
161
|
+
'Circuit breaker closed: commit succeeded after failures',
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
this.consecutiveFailures = 0;
|
|
165
|
+
this.nextAllowedAttemptMs = 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private recordFailure(): void {
|
|
169
|
+
const config = getConfig();
|
|
170
|
+
const failureBackoffBaseMs = config.workspaceGit?.failureBackoffBaseMs ?? 2000;
|
|
171
|
+
const failureBackoffMaxMs = config.workspaceGit?.failureBackoffMaxMs ?? 60000;
|
|
172
|
+
this.consecutiveFailures++;
|
|
173
|
+
const delay = Math.min(
|
|
174
|
+
failureBackoffBaseMs * Math.pow(2, this.consecutiveFailures - 1),
|
|
175
|
+
failureBackoffMaxMs,
|
|
176
|
+
);
|
|
177
|
+
this.nextAllowedAttemptMs = Date.now() + delay;
|
|
178
|
+
log.warn(
|
|
179
|
+
{ workspaceDir: this.workspaceDir, consecutiveFailures: this.consecutiveFailures, backoffMs: delay },
|
|
180
|
+
'Circuit breaker opened: commit failed, backing off',
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if the init circuit breaker is open (too many recent init failures).
|
|
186
|
+
* When open, init attempts are skipped until the backoff window expires.
|
|
187
|
+
*/
|
|
188
|
+
private isInitBreakerOpen(): boolean {
|
|
189
|
+
if (this.initConsecutiveFailures < 2) return false;
|
|
190
|
+
return Date.now() < this.initNextAllowedAttemptMs;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private recordInitSuccess(): void {
|
|
194
|
+
if (this.initConsecutiveFailures > 0) {
|
|
195
|
+
log.info(
|
|
196
|
+
{ workspaceDir: this.workspaceDir, previousFailures: this.initConsecutiveFailures },
|
|
197
|
+
'Init circuit breaker closed: initialization succeeded after failures',
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
this.initConsecutiveFailures = 0;
|
|
201
|
+
this.initNextAllowedAttemptMs = 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private recordInitFailure(): void {
|
|
205
|
+
const config = getConfig();
|
|
206
|
+
const failureBackoffBaseMs = config.workspaceGit?.failureBackoffBaseMs ?? 2000;
|
|
207
|
+
const failureBackoffMaxMs = config.workspaceGit?.failureBackoffMaxMs ?? 60000;
|
|
208
|
+
this.initConsecutiveFailures++;
|
|
209
|
+
const delay = Math.min(
|
|
210
|
+
failureBackoffBaseMs * Math.pow(2, this.initConsecutiveFailures - 1),
|
|
211
|
+
failureBackoffMaxMs,
|
|
212
|
+
);
|
|
213
|
+
this.initNextAllowedAttemptMs = Date.now() + delay;
|
|
214
|
+
log.warn(
|
|
215
|
+
{ workspaceDir: this.workspaceDir, consecutiveFailures: this.initConsecutiveFailures, backoffMs: delay },
|
|
216
|
+
'Init circuit breaker opened: initialization failed, backing off',
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Ensure the git repository is initialized.
|
|
222
|
+
* Idempotent: safe to call multiple times.
|
|
223
|
+
*
|
|
224
|
+
* If .git doesn't exist:
|
|
225
|
+
* 1. Run git init -b main
|
|
226
|
+
* 2. Create .gitignore
|
|
227
|
+
* 3. Set git identity
|
|
228
|
+
* 4. Stage all files and create initial commit
|
|
229
|
+
*
|
|
230
|
+
* The initial commit is created synchronously within the mutex lock
|
|
231
|
+
* to prevent races with the first commitChanges() call.
|
|
232
|
+
*/
|
|
233
|
+
async ensureInitialized(): Promise<void> {
|
|
234
|
+
// Fast path: already initialized
|
|
235
|
+
if (this.initialized) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// If initialization is in progress, wait for it
|
|
240
|
+
if (this.initGuard.active) {
|
|
241
|
+
return this.initGuard.run(() => { throw new Error('unreachable'); });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Circuit breaker: skip if multiple recent init attempts have been failing.
|
|
245
|
+
// Checked AFTER initGuard.active so callers waiting on in-progress init aren't
|
|
246
|
+
// blocked, and only activates after 2+ consecutive failures so that a
|
|
247
|
+
// single transient failure allows immediate retry.
|
|
248
|
+
if (this.isInitBreakerOpen()) {
|
|
249
|
+
throw new Error('Init circuit breaker open: backing off after repeated failures');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return this.initGuard.run(
|
|
253
|
+
() => this.mutex.withLock(async () => {
|
|
254
|
+
// Double-check after acquiring lock
|
|
255
|
+
if (this.initialized) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const gitDir = join(this.workspaceDir, '.git');
|
|
260
|
+
|
|
261
|
+
if (existsSync(gitDir)) {
|
|
262
|
+
// Validate existing repo is not corrupted before marking as ready.
|
|
263
|
+
// A corrupted .git directory (e.g. missing HEAD) would cause all
|
|
264
|
+
// subsequent git operations to fail with confusing errors.
|
|
265
|
+
try {
|
|
266
|
+
await this.execGit(['rev-parse', '--git-dir']);
|
|
267
|
+
} catch (err: unknown) {
|
|
268
|
+
// Distinguish transient failures from genuine corruption.
|
|
269
|
+
// Transient errors (timeouts, permissions, missing git binary)
|
|
270
|
+
// should NOT destroy .git — they will resolve on retry via
|
|
271
|
+
// the guard clearing logic.
|
|
272
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
273
|
+
const execErr = err as ExecError;
|
|
274
|
+
const isTimeout = execErr.killed === true
|
|
275
|
+
|| execErr.signal === 'SIGTERM'
|
|
276
|
+
|| errMsg.includes('SIGTERM')
|
|
277
|
+
|| errMsg.includes('timed out');
|
|
278
|
+
const isPermission = execErr.code === 'EACCES'
|
|
279
|
+
|| errMsg.includes('EACCES')
|
|
280
|
+
|| errMsg.toLowerCase().includes('permission denied');
|
|
281
|
+
const isMissingBinary = execErr.code === 'ENOENT'
|
|
282
|
+
|| errMsg.includes('ENOENT');
|
|
283
|
+
|
|
284
|
+
if (isTimeout || isPermission || isMissingBinary) {
|
|
285
|
+
// Re-throw so initialization fails gracefully without
|
|
286
|
+
// destroying valid git history.
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Genuine corruption (e.g. missing HEAD, broken refs) —
|
|
291
|
+
// remove corrupted .git and fall through to full init below.
|
|
292
|
+
log.warn(
|
|
293
|
+
{ workspaceDir: this.workspaceDir, err: errMsg },
|
|
294
|
+
'Corrupted .git directory detected; reinitializing',
|
|
295
|
+
);
|
|
296
|
+
const { rmSync } = await import('node:fs');
|
|
297
|
+
rmSync(gitDir, { recursive: true, force: true });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (existsSync(gitDir)) {
|
|
301
|
+
// .git exists and passed the corruption check, but we still
|
|
302
|
+
// need to verify that at least one commit exists. A partial
|
|
303
|
+
// init (e.g. git init succeeded but the initial commit failed)
|
|
304
|
+
// leaves .git present with an undefined HEAD. In that case,
|
|
305
|
+
// fall through to the initial commit logic below.
|
|
306
|
+
let headExists = false;
|
|
307
|
+
try {
|
|
308
|
+
await this.execGit(['rev-parse', 'HEAD']);
|
|
309
|
+
headExists = true;
|
|
310
|
+
} catch (err: unknown) {
|
|
311
|
+
// Distinguish transient failures from genuine "no commits".
|
|
312
|
+
// Transient errors (timeouts, permissions, missing git binary)
|
|
313
|
+
// should NOT fall through to re-initialization — they will
|
|
314
|
+
// resolve on retry via the guard clearing logic.
|
|
315
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
316
|
+
const execErr = err as ExecError;
|
|
317
|
+
const isTimeout = execErr.killed === true
|
|
318
|
+
|| execErr.signal === 'SIGTERM'
|
|
319
|
+
|| errMsg.includes('SIGTERM')
|
|
320
|
+
|| errMsg.includes('timed out');
|
|
321
|
+
const isPermission = execErr.code === 'EACCES'
|
|
322
|
+
|| errMsg.includes('EACCES')
|
|
323
|
+
|| errMsg.toLowerCase().includes('permission denied');
|
|
324
|
+
const isMissingBinary = execErr.code === 'ENOENT'
|
|
325
|
+
|| errMsg.includes('ENOENT');
|
|
326
|
+
|
|
327
|
+
if (isTimeout || isPermission || isMissingBinary) {
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
330
|
+
// Genuine "no commits" (unborn HEAD) — fall through to
|
|
331
|
+
// create the initial commit.
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (headExists) {
|
|
335
|
+
// HEAD resolves — repo is fully initialized.
|
|
336
|
+
// Run normalization for existing repos that may have been
|
|
337
|
+
// created before these helpers existed, or by external tools.
|
|
338
|
+
// These calls are OUTSIDE the rev-parse try/catch so that
|
|
339
|
+
// normalization errors are not misclassified as "no commits".
|
|
340
|
+
this.ensureGitignoreRulesLocked();
|
|
341
|
+
await this.ensureCommitIdentityLocked();
|
|
342
|
+
await this.ensureOnMainLocked();
|
|
343
|
+
this.initialized = true;
|
|
344
|
+
this.recordInitSuccess();
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Otherwise fall through to reinitialize / create initial commit
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Initialize new git repository
|
|
352
|
+
await this.execGit(['init', '-b', 'main']);
|
|
353
|
+
|
|
354
|
+
// Run normalization (gitignore + identity + branch enforcement).
|
|
355
|
+
// For fresh `git init -b main` the branch is already main, but
|
|
356
|
+
// in the corruption-recovery path we fall through here after
|
|
357
|
+
// removing .git, so branch enforcement is still useful.
|
|
358
|
+
this.ensureGitignoreRulesLocked();
|
|
359
|
+
await this.ensureCommitIdentityLocked();
|
|
360
|
+
await this.ensureOnMainLocked();
|
|
361
|
+
|
|
362
|
+
// Create initial commit synchronously within the lock to prevent
|
|
363
|
+
// races with the first commitChanges() call. Without this, the
|
|
364
|
+
// initial commit could run concurrently and consume edits meant
|
|
365
|
+
// for the first user-requested commit.
|
|
366
|
+
const status = await this.getStatusInternal();
|
|
367
|
+
const hasExistingFiles = status.untracked.length > 1 || // More than just .gitignore
|
|
368
|
+
status.untracked.some(f => f !== '.gitignore');
|
|
369
|
+
|
|
370
|
+
await this.execGit(['add', '-A']);
|
|
371
|
+
|
|
372
|
+
const message = hasExistingFiles
|
|
373
|
+
? 'Initial commit: migrated existing workspace'
|
|
374
|
+
: 'Initial commit: new workspace';
|
|
375
|
+
|
|
376
|
+
await this.execGit(['commit', '-m', message, '--allow-empty']);
|
|
377
|
+
|
|
378
|
+
this.initialized = true;
|
|
379
|
+
this.recordInitSuccess();
|
|
380
|
+
}),
|
|
381
|
+
() => this.recordInitFailure(),
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Commit all changes in the workspace.
|
|
387
|
+
*
|
|
388
|
+
* @param message - Commit message describing the changes
|
|
389
|
+
* @param metadata - Optional metadata (currently stored in commit message)
|
|
390
|
+
*/
|
|
391
|
+
async commitChanges(message: string, metadata?: GitCommitMetadata): Promise<void> {
|
|
392
|
+
await this.ensureInitialized();
|
|
393
|
+
|
|
394
|
+
await this.mutex.withLock(async () => {
|
|
395
|
+
// Stage all changes
|
|
396
|
+
await this.execGit(['add', '-A']);
|
|
397
|
+
|
|
398
|
+
// Build commit message with metadata if provided
|
|
399
|
+
let fullMessage = message;
|
|
400
|
+
if (metadata && Object.keys(metadata).length > 0) {
|
|
401
|
+
fullMessage += '\n\n' + Object.entries(metadata)
|
|
402
|
+
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
403
|
+
.join('\n');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Commit (will succeed even if no changes)
|
|
407
|
+
await this.execGit(['commit', '-m', fullMessage, '--allow-empty']);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Atomically check for uncommitted changes and commit if the caller decides to.
|
|
413
|
+
*
|
|
414
|
+
* The status check, staging, and commit all happen within a single mutex lock,
|
|
415
|
+
* eliminating the TOCTOU race that exists when calling getStatus() and
|
|
416
|
+
* commitChanges() separately.
|
|
417
|
+
*
|
|
418
|
+
* @param decide - Called with the current status. Return an object with `message`
|
|
419
|
+
* (and optional `metadata`) to commit, or `null` to skip.
|
|
420
|
+
* @param options.bypassBreaker - Skip circuit breaker checks (used for shutdown commits).
|
|
421
|
+
* @param options.deadlineMs - Absolute timestamp (Date.now()) after which the commit
|
|
422
|
+
* should be skipped. Checked before lock acquisition, after lock acquisition, and
|
|
423
|
+
* before git add/commit to prevent stale queued attempts from doing expensive work.
|
|
424
|
+
* @returns Whether a commit was created and the status at check time.
|
|
425
|
+
*/
|
|
426
|
+
async commitIfDirty(
|
|
427
|
+
decide: (status: GitStatus) => { message: string; metadata?: GitCommitMetadata } | null,
|
|
428
|
+
options?: { bypassBreaker?: boolean; deadlineMs?: number },
|
|
429
|
+
): Promise<{ committed: boolean; status: GitStatus }> {
|
|
430
|
+
const emptyStatus: GitStatus = { staged: [], modified: [], untracked: [], clean: false };
|
|
431
|
+
|
|
432
|
+
// Circuit breaker: skip expensive git work if recent attempts have been failing.
|
|
433
|
+
// Shutdown commits bypass the breaker because the process is about to exit and
|
|
434
|
+
// this is the last chance to persist workspace state.
|
|
435
|
+
if (!options?.bypassBreaker && this.isBreakerOpen()) {
|
|
436
|
+
log.debug(
|
|
437
|
+
{ workspaceDir: this.workspaceDir, consecutiveFailures: this.consecutiveFailures },
|
|
438
|
+
'Circuit breaker open, skipping commit attempt',
|
|
439
|
+
);
|
|
440
|
+
return { committed: false, status: emptyStatus };
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Deadline fast-path: bail before acquiring the lock if already past deadline.
|
|
444
|
+
if (isDeadlineExpired(options?.deadlineMs)) {
|
|
445
|
+
log.debug(
|
|
446
|
+
{ workspaceDir: this.workspaceDir },
|
|
447
|
+
'Deadline expired before lock acquisition, skipping commit',
|
|
448
|
+
);
|
|
449
|
+
return { committed: false, status: emptyStatus };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
await this.ensureInitialized();
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
const result = await this.mutex.withLock(async () => {
|
|
456
|
+
// Re-check breaker under lock: a queued call that started before the
|
|
457
|
+
// breaker opened should not proceed with expensive git work now that
|
|
458
|
+
// the breaker is open.
|
|
459
|
+
if (!options?.bypassBreaker && this.isBreakerOpen()) {
|
|
460
|
+
log.debug(
|
|
461
|
+
{ workspaceDir: this.workspaceDir, consecutiveFailures: this.consecutiveFailures },
|
|
462
|
+
'Circuit breaker open after lock acquisition, skipping commit',
|
|
463
|
+
);
|
|
464
|
+
return { committed: false, status: emptyStatus, didRunGit: false as const };
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Re-check deadline after lock acquisition: the call may have waited
|
|
468
|
+
// in the mutex queue past its deadline.
|
|
469
|
+
if (isDeadlineExpired(options?.deadlineMs)) {
|
|
470
|
+
log.debug(
|
|
471
|
+
{ workspaceDir: this.workspaceDir },
|
|
472
|
+
'Deadline expired after lock acquisition, skipping commit',
|
|
473
|
+
);
|
|
474
|
+
return { committed: false, status: emptyStatus, didRunGit: false as const };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const status = await this.getStatusInternal();
|
|
478
|
+
if (status.clean) {
|
|
479
|
+
return { committed: false, status, didRunGit: true as const };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const decision = decide(status);
|
|
483
|
+
if (!decision) {
|
|
484
|
+
return { committed: false, status, didRunGit: true as const };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Check deadline before expensive git add/commit operations.
|
|
488
|
+
if (isDeadlineExpired(options?.deadlineMs)) {
|
|
489
|
+
log.debug(
|
|
490
|
+
{ workspaceDir: this.workspaceDir },
|
|
491
|
+
'Deadline expired before git add/commit, skipping commit',
|
|
492
|
+
);
|
|
493
|
+
return { committed: false, status, didRunGit: true as const };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
await this.execGit(['add', '-A']);
|
|
497
|
+
|
|
498
|
+
// Verify something was actually staged. Another service instance
|
|
499
|
+
// (or external process) could have committed between our status
|
|
500
|
+
// check and the add, leaving the index clean.
|
|
501
|
+
try {
|
|
502
|
+
await this.execGit(['diff', '--cached', '--quiet']);
|
|
503
|
+
// Exit code 0 means nothing staged — nothing to commit
|
|
504
|
+
return { committed: false, status, didRunGit: true as const };
|
|
505
|
+
} catch (err) {
|
|
506
|
+
// git diff --cached --quiet exits with code 1 when there are staged changes.
|
|
507
|
+
// Any other error (timeout, permission, etc.) should be treated as a failure.
|
|
508
|
+
const execErr = err as ExecError;
|
|
509
|
+
if (execErr.code !== 1) {
|
|
510
|
+
throw err;
|
|
511
|
+
}
|
|
512
|
+
// Exit code 1 = staged changes exist — proceed with commit
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let fullMessage = decision.message;
|
|
516
|
+
if (decision.metadata && Object.keys(decision.metadata).length > 0) {
|
|
517
|
+
fullMessage += '\n\n' + Object.entries(decision.metadata)
|
|
518
|
+
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
|
519
|
+
.join('\n');
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
await this.execGit(['commit', '-m', fullMessage]);
|
|
523
|
+
return { committed: true, status, didRunGit: true as const };
|
|
524
|
+
});
|
|
525
|
+
if (result.didRunGit) {
|
|
526
|
+
this.recordSuccess();
|
|
527
|
+
}
|
|
528
|
+
return { committed: result.committed, status: result.status };
|
|
529
|
+
} catch (err) {
|
|
530
|
+
this.recordFailure();
|
|
531
|
+
throw err;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Get the current git status of the workspace.
|
|
537
|
+
*
|
|
538
|
+
* @returns Status information about staged, modified, and untracked files
|
|
539
|
+
*/
|
|
540
|
+
async getStatus(): Promise<GitStatus> {
|
|
541
|
+
await this.ensureInitialized();
|
|
542
|
+
return this.mutex.withLock(() => this.getStatusInternal());
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Internal status implementation (must be called with lock held).
|
|
547
|
+
*/
|
|
548
|
+
private async getStatusInternal(): Promise<GitStatus> {
|
|
549
|
+
const { stdout } = await this.execGit(['status', '--porcelain']);
|
|
550
|
+
|
|
551
|
+
const staged: string[] = [];
|
|
552
|
+
const modified: string[] = [];
|
|
553
|
+
const untracked: string[] = [];
|
|
554
|
+
|
|
555
|
+
for (const line of stdout.split('\n')) {
|
|
556
|
+
if (!line) continue;
|
|
557
|
+
|
|
558
|
+
const status = line.substring(0, 2);
|
|
559
|
+
const file = line.substring(3);
|
|
560
|
+
|
|
561
|
+
// First character is staged status, second is working tree status
|
|
562
|
+
const stagedStatus = status[0];
|
|
563
|
+
const workingStatus = status[1];
|
|
564
|
+
|
|
565
|
+
if (stagedStatus !== ' ' && stagedStatus !== '?') {
|
|
566
|
+
staged.push(file);
|
|
567
|
+
}
|
|
568
|
+
if (workingStatus === 'M' || workingStatus === 'D') {
|
|
569
|
+
modified.push(file);
|
|
570
|
+
}
|
|
571
|
+
if (status === '??') {
|
|
572
|
+
untracked.push(file);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
staged,
|
|
578
|
+
modified,
|
|
579
|
+
untracked,
|
|
580
|
+
clean: staged.length === 0 && modified.length === 0 && untracked.length === 0,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Ensure .gitignore contains all required workspace exclusion rules.
|
|
586
|
+
* Idempotent: checks for missing rules and only appends what's needed.
|
|
587
|
+
* Must be called with the mutex lock held.
|
|
588
|
+
*/
|
|
589
|
+
private ensureGitignoreRulesLocked(): void {
|
|
590
|
+
const gitignorePath = join(this.workspaceDir, '.gitignore');
|
|
591
|
+
if (existsSync(gitignorePath)) {
|
|
592
|
+
let content = readFileSync(gitignorePath, 'utf-8');
|
|
593
|
+
|
|
594
|
+
// Migrate legacy broad ignore rule to selective data subdirectory rules.
|
|
595
|
+
// This keeps user-tracked files under data/ visible to git.
|
|
596
|
+
const lines = content.split('\n');
|
|
597
|
+
const hadLegacyDataRule = lines.some(line => line.trim() === 'data/');
|
|
598
|
+
if (hadLegacyDataRule) {
|
|
599
|
+
content = lines
|
|
600
|
+
.filter(line => line.trim() !== 'data/')
|
|
601
|
+
.join('\n');
|
|
602
|
+
if (!content.endsWith('\n')) {
|
|
603
|
+
content += '\n';
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const missingRules = WORKSPACE_GITIGNORE_RULES.filter(rule => !content.includes(rule));
|
|
608
|
+
if (hadLegacyDataRule || missingRules.length > 0) {
|
|
609
|
+
let updated = content;
|
|
610
|
+
if (missingRules.length > 0) {
|
|
611
|
+
if (!updated.endsWith('\n')) {
|
|
612
|
+
updated += '\n';
|
|
613
|
+
}
|
|
614
|
+
updated += '# Vellum runtime state (auto-added)\n' + missingRules.join('\n') + '\n';
|
|
615
|
+
}
|
|
616
|
+
writeFileSync(gitignorePath, updated, 'utf-8');
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
const gitignore = '# Runtime state - excluded from git tracking\n' + WORKSPACE_GITIGNORE_RULES.join('\n') + '\n';
|
|
620
|
+
writeFileSync(gitignorePath, gitignore, 'utf-8');
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Ensure local git identity is configured for automated commits.
|
|
626
|
+
* Idempotent: git config set is a no-op if the value is already correct.
|
|
627
|
+
* Must be called with the mutex lock held.
|
|
628
|
+
*/
|
|
629
|
+
private async ensureCommitIdentityLocked(): Promise<void> {
|
|
630
|
+
await this.execGit(['config', 'user.name', 'Vellum Assistant']);
|
|
631
|
+
await this.execGit(['config', 'user.email', 'assistant@vellum.ai']);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Ensure the workspace repo is on the `main` branch.
|
|
636
|
+
* If on a different branch or in detached HEAD state, switches to main
|
|
637
|
+
* (creating it if it doesn't exist).
|
|
638
|
+
* Must be called with the mutex lock held.
|
|
639
|
+
*/
|
|
640
|
+
private async ensureOnMainLocked(): Promise<void> {
|
|
641
|
+
let currentBranch: string | null = null;
|
|
642
|
+
try {
|
|
643
|
+
const { stdout } = await this.execGit(['symbolic-ref', '--short', 'HEAD']);
|
|
644
|
+
currentBranch = stdout.trim();
|
|
645
|
+
} catch {
|
|
646
|
+
// symbolic-ref fails in detached HEAD state
|
|
647
|
+
currentBranch = null;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (currentBranch === 'main') {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const state = currentBranch === null ? 'detached HEAD' : `branch '${currentBranch}'`;
|
|
655
|
+
log.warn(
|
|
656
|
+
{ workspaceDir: this.workspaceDir, currentBranch },
|
|
657
|
+
`Workspace repo is on ${state}; auto-switching to main`,
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
// Try switching to existing main branch first.
|
|
661
|
+
// If the switch fails, distinguish "main doesn't exist" from
|
|
662
|
+
// "local changes would be overwritten" to pick the right recovery.
|
|
663
|
+
try {
|
|
664
|
+
await this.execGit(['switch', 'main']);
|
|
665
|
+
} catch {
|
|
666
|
+
// Check whether `main` already exists as a branch.
|
|
667
|
+
let mainExists = false;
|
|
668
|
+
try {
|
|
669
|
+
await this.execGit(['rev-parse', '--verify', 'refs/heads/main']);
|
|
670
|
+
mainExists = true;
|
|
671
|
+
} catch {
|
|
672
|
+
// main branch does not exist
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (mainExists) {
|
|
676
|
+
// `main` exists but switch failed — likely due to uncommitted
|
|
677
|
+
// local changes that would be overwritten. Discard them so we
|
|
678
|
+
// can land on main.
|
|
679
|
+
await this.execGit(['switch', 'main', '--discard-changes']);
|
|
680
|
+
} else {
|
|
681
|
+
// `main` doesn't exist yet — create it.
|
|
682
|
+
await this.execGit(['switch', '-c', 'main']);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Execute a git command in the workspace directory.
|
|
689
|
+
* Uses the configurable interactiveGitTimeoutMs (default 10 000 ms) to
|
|
690
|
+
* prevent hung operations (e.g. stale git lock files). The timeout is
|
|
691
|
+
* intentionally short for interactive workspace operations — background
|
|
692
|
+
* enrichment jobs use their own dedicated timeout.
|
|
693
|
+
*/
|
|
694
|
+
private async execGit(args: string[], options?: { signal?: AbortSignal }): Promise<{ stdout: string; stderr: string }> {
|
|
695
|
+
const config = getConfig();
|
|
696
|
+
const timeoutMs = config.workspaceGit?.interactiveGitTimeoutMs ?? 10_000;
|
|
697
|
+
try {
|
|
698
|
+
const { stdout, stderr } = await execFileAsync('git', args, {
|
|
699
|
+
cwd: this.workspaceDir,
|
|
700
|
+
encoding: 'utf-8',
|
|
701
|
+
timeout: timeoutMs,
|
|
702
|
+
env: cleanGitEnv(this.workspaceDir),
|
|
703
|
+
signal: options?.signal,
|
|
704
|
+
});
|
|
705
|
+
return { stdout, stderr };
|
|
706
|
+
} catch (err) {
|
|
707
|
+
// Enhance error with git command details, preserving properties
|
|
708
|
+
// needed to distinguish transient failures from corruption.
|
|
709
|
+
const gitErr = err as Error & {
|
|
710
|
+
stdout?: string; stderr?: string;
|
|
711
|
+
code?: string; killed?: boolean; signal?: string;
|
|
712
|
+
};
|
|
713
|
+
const isPermissionError = gitErr.code === 'EACCES' || gitErr.stderr?.includes('Permission denied');
|
|
714
|
+
const prefix = isPermissionError ? 'Git permission error' : 'Git command failed';
|
|
715
|
+
const enhanced = new Error(
|
|
716
|
+
`${prefix}: git ${args.join(' ')}\n` +
|
|
717
|
+
`Error: ${gitErr.message}\n` +
|
|
718
|
+
`Stderr: ${gitErr.stderr || ''}`,
|
|
719
|
+
);
|
|
720
|
+
// Preserve properties so callers can detect timeouts, permission
|
|
721
|
+
// errors, and missing-binary failures without parsing the message.
|
|
722
|
+
(enhanced as ExecError).killed = gitErr.killed;
|
|
723
|
+
(enhanced as ExecError).signal = gitErr.signal;
|
|
724
|
+
(enhanced as ExecError).code = gitErr.code;
|
|
725
|
+
throw enhanced;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Run an arbitrary read-only git command in the workspace directory.
|
|
731
|
+
* Uses the same clean env and timeout as other git operations.
|
|
732
|
+
* Does NOT acquire the mutex — callers must ensure they are not
|
|
733
|
+
* writing to the repo concurrently (or accept eventual-consistency).
|
|
734
|
+
*/
|
|
735
|
+
async runReadOnlyGit(args: string[]): Promise<{ stdout: string; stderr: string }> {
|
|
736
|
+
await this.ensureInitialized();
|
|
737
|
+
return this.execGit(args);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Run a sequence of git commands atomically under the workspace mutex.
|
|
742
|
+
* Use this for write operations that need serialization with other
|
|
743
|
+
* git mutations (e.g. checkout + commit).
|
|
744
|
+
*/
|
|
745
|
+
async runWithMutex(fn: (exec: (args: string[]) => Promise<{ stdout: string; stderr: string }>) => Promise<void>): Promise<void> {
|
|
746
|
+
await this.ensureInitialized();
|
|
747
|
+
await this.mutex.withLock(async () => {
|
|
748
|
+
await fn((args) => this.execGit(args));
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Get the commit hash of the current HEAD.
|
|
754
|
+
* This is a lightweight read-only operation that does not require the mutex.
|
|
755
|
+
*/
|
|
756
|
+
async getHeadHash(): Promise<string> {
|
|
757
|
+
const { stdout } = await this.execGit(['rev-parse', 'HEAD']);
|
|
758
|
+
return stdout.trim();
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Write a git note to a specific commit.
|
|
763
|
+
* Uses the 'vellum' notes ref to avoid conflicts with default notes.
|
|
764
|
+
*/
|
|
765
|
+
async writeNote(commitHash: string, noteContent: string, signal?: AbortSignal): Promise<void> {
|
|
766
|
+
await this.execGit(['notes', '--ref=vellum', 'add', '-f', '-m', noteContent, commitHash], { signal });
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Check if the workspace has a git repository initialized.
|
|
771
|
+
* This is a non-blocking check that doesn't trigger initialization.
|
|
772
|
+
*/
|
|
773
|
+
isInitialized(): boolean {
|
|
774
|
+
return existsSync(join(this.workspaceDir, '.git'));
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Get the workspace directory path.
|
|
779
|
+
*/
|
|
780
|
+
getWorkspaceDir(): string {
|
|
781
|
+
return this.workspaceDir;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Check whether a deadline has expired.
|
|
787
|
+
* Returns true when `deadlineMs` is provided and `Date.now()` has reached or passed it.
|
|
788
|
+
*/
|
|
789
|
+
export function isDeadlineExpired(deadlineMs?: number): boolean {
|
|
790
|
+
return deadlineMs !== undefined && Date.now() >= deadlineMs;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Singleton registry for workspace git services.
|
|
795
|
+
* Ensures one service instance per workspace directory.
|
|
796
|
+
*/
|
|
797
|
+
const serviceRegistry = new Map<string, WorkspaceGitService>();
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Get or create a git service for the specified workspace directory.
|
|
801
|
+
*
|
|
802
|
+
* @param workspaceDir - Absolute path to workspace directory
|
|
803
|
+
* @returns WorkspaceGitService instance for the workspace
|
|
804
|
+
*/
|
|
805
|
+
export function getWorkspaceGitService(workspaceDir: string): WorkspaceGitService {
|
|
806
|
+
let service = serviceRegistry.get(workspaceDir);
|
|
807
|
+
if (!service) {
|
|
808
|
+
service = new WorkspaceGitService(workspaceDir);
|
|
809
|
+
serviceRegistry.set(workspaceDir, service);
|
|
810
|
+
}
|
|
811
|
+
return service;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Returns all currently registered WorkspaceGitService instances.
|
|
816
|
+
* Used by the heartbeat service to check all tracked workspaces for uncommitted changes.
|
|
817
|
+
*/
|
|
818
|
+
export function getAllWorkspaceGitServices(): ReadonlyMap<string, WorkspaceGitService> {
|
|
819
|
+
return serviceRegistry;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* @internal Test-only: clear the service registry
|
|
824
|
+
*/
|
|
825
|
+
export function _resetGitServiceRegistry(): void {
|
|
826
|
+
serviceRegistry.clear();
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* @internal Test-only: reset circuit breaker state for a service instance
|
|
831
|
+
*/
|
|
832
|
+
export function _resetBreaker(service: WorkspaceGitService): void {
|
|
833
|
+
(service as unknown as { consecutiveFailures: number }).consecutiveFailures = 0;
|
|
834
|
+
(service as unknown as { nextAllowedAttemptMs: number }).nextAllowedAttemptMs = 0;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* @internal Test-only: get consecutive failure count
|
|
839
|
+
*/
|
|
840
|
+
export function _getConsecutiveFailures(service: WorkspaceGitService): number {
|
|
841
|
+
return (service as unknown as { consecutiveFailures: number }).consecutiveFailures;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* @internal Test-only: reset init circuit breaker state for a service instance
|
|
846
|
+
*/
|
|
847
|
+
export function _resetInitBreaker(service: WorkspaceGitService): void {
|
|
848
|
+
(service as unknown as { initConsecutiveFailures: number }).initConsecutiveFailures = 0;
|
|
849
|
+
(service as unknown as { initNextAllowedAttemptMs: number }).initNextAllowedAttemptMs = 0;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* @internal Test-only: get init consecutive failure count
|
|
854
|
+
*/
|
|
855
|
+
export function _getInitConsecutiveFailures(service: WorkspaceGitService): number {
|
|
856
|
+
return (service as unknown as { initConsecutiveFailures: number }).initConsecutiveFailures;
|
|
857
|
+
}
|