@vellumai/assistant 0.3.5 → 0.3.6
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/README.md +51 -0
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +18 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-orchestrator.test.ts +752 -271
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +5 -0
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1260 -85
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +4 -65
- package/src/__tests__/channel-guardian.test.ts +556 -0
- package/src/__tests__/channel-readiness-service.test.ts +74 -7
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +12 -7
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +6 -2
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +126 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +228 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
- package/src/__tests__/run-orchestrator.test.ts +20 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +237 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +2 -1
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +141 -21
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +45 -29
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +106 -5
- package/src/calls/call-orchestrator.ts +252 -54
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +7 -5
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +40 -15
- package/src/calls/twilio-routes.ts +60 -45
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
- package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
- package/src/config/bundled-skills/messaging/SKILL.md +12 -2
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +26 -2
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +156 -1137
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +107 -56
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +0 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -2463
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +4 -6
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/index.ts +9 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +54 -5
- package/src/daemon/handlers/shared.ts +3 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +2 -2
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +60 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +226 -2527
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -379
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +67 -2
- package/src/daemon/server.ts +60 -44
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +113 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +149 -15
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +28 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +24 -1
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +3 -1
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +200 -1
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +121 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +11 -42
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +6 -9
- package/src/memory/jobs-worker.ts +51 -36
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +160 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +130 -7
- package/src/memory/search/entity.ts +10 -19
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +21 -22
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/providers/sms/adapter.ts +3 -6
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +119 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +115 -5
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +0 -21
- package/src/runtime/channel-guardian-service.ts +48 -7
- package/src/runtime/channel-readiness-service.ts +160 -34
- package/src/runtime/channel-readiness-types.ts +10 -4
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -743
- package/src/runtime/http-types.ts +56 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +49 -6
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1634
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +52 -34
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +7 -3
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +72 -365
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
- package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
- package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
- package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
- package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
- package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
- package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
- package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
- package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
- package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { getRootDir, getWorkspaceDir } from '../util/platform.js';
|
|
4
|
+
import { migrationLog } from './log.js';
|
|
5
|
+
import { mergeSkippedConfigKeys } from './config-merge.js';
|
|
6
|
+
import { mergeLegacyHooks } from './hooks-merge.js';
|
|
7
|
+
import { mergeLegacySkills } from './skills-merge.js';
|
|
8
|
+
import { mergeLegacyDataEntries } from './data-merge.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Idempotent move: relocates source to destination for migration.
|
|
12
|
+
* - No-op if source is missing (already migrated or never existed).
|
|
13
|
+
* - No-op if destination already exists (avoids clobbering).
|
|
14
|
+
* - Creates destination parent directories as needed.
|
|
15
|
+
* - Logs warning on failure instead of throwing.
|
|
16
|
+
*
|
|
17
|
+
* Exported for testing; not intended for general use outside migrations.
|
|
18
|
+
*/
|
|
19
|
+
export function migratePath(source: string, destination: string): void {
|
|
20
|
+
if (!existsSync(source)) return;
|
|
21
|
+
if (existsSync(destination)) {
|
|
22
|
+
migrationLog('debug', 'Migration skipped: destination already exists', { source, destination });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const destDir = dirname(destination);
|
|
27
|
+
if (!existsSync(destDir)) {
|
|
28
|
+
mkdirSync(destDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
renameSync(source, destination);
|
|
31
|
+
migrationLog('info', 'Migrated path', { from: source, to: destination });
|
|
32
|
+
} catch (err) {
|
|
33
|
+
migrationLog('warn', 'Failed to migrate path', { err: String(err), from: source, to: destination });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Migrate from the flat ~/.vellum layout to the workspace-based layout.
|
|
39
|
+
*
|
|
40
|
+
* Step (a) is special: if the workspace dir doesn't exist yet but the old
|
|
41
|
+
* sandbox working dir (data/sandbox/fs) does, its contents are "extracted"
|
|
42
|
+
* to become the new workspace root via rename. All subsequent moves then
|
|
43
|
+
* land inside that workspace directory.
|
|
44
|
+
*
|
|
45
|
+
* Idempotent: safe to call on every startup — already-migrated items are
|
|
46
|
+
* skipped, and a second run is a no-op.
|
|
47
|
+
*/
|
|
48
|
+
export function migrateToWorkspaceLayout(): void {
|
|
49
|
+
const root = getRootDir();
|
|
50
|
+
if (!existsSync(root)) return;
|
|
51
|
+
|
|
52
|
+
const ws = getWorkspaceDir();
|
|
53
|
+
|
|
54
|
+
// (a) Extract data/sandbox/fs -> workspace (only when workspace doesn't exist yet)
|
|
55
|
+
if (!existsSync(ws)) {
|
|
56
|
+
const sandboxFs = join(root, 'data', 'sandbox', 'fs');
|
|
57
|
+
if (existsSync(sandboxFs)) {
|
|
58
|
+
try {
|
|
59
|
+
renameSync(sandboxFs, ws);
|
|
60
|
+
migrationLog('info', 'Extracted sandbox/fs as workspace root', { from: sandboxFs, to: ws });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
migrationLog('warn', 'Failed to extract sandbox/fs', { err: String(err), from: sandboxFs, to: ws });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// (b)-(h) Move legacy root-level items into workspace
|
|
68
|
+
migratePath(join(root, 'config.json'), join(ws, 'config.json'));
|
|
69
|
+
mergeSkippedConfigKeys(join(root, 'config.json'), join(ws, 'config.json'));
|
|
70
|
+
migratePath(join(root, 'data'), join(ws, 'data'));
|
|
71
|
+
mergeLegacyDataEntries(join(root, 'data'), join(ws, 'data'));
|
|
72
|
+
migratePath(join(root, 'hooks'), join(ws, 'hooks'));
|
|
73
|
+
mergeLegacyHooks(join(root, 'hooks'), join(ws, 'hooks'));
|
|
74
|
+
migratePath(join(root, 'IDENTITY.md'), join(ws, 'IDENTITY.md'));
|
|
75
|
+
migratePath(join(root, 'skills'), join(ws, 'skills'));
|
|
76
|
+
mergeLegacySkills(join(root, 'skills'), join(ws, 'skills'));
|
|
77
|
+
migratePath(join(root, 'SOUL.md'), join(ws, 'SOUL.md'));
|
|
78
|
+
migratePath(join(root, 'USER.md'), join(ws, 'USER.md'));
|
|
79
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { RiskLevel, type PermissionCheckResult, type AllowlistOption, type ScopeOption, type PolicyContext } from './types.js';
|
|
2
|
-
import { findHighestPriorityRule } from './trust-store.js';
|
|
3
|
-
import { parse } from '../tools/terminal/parser.js';
|
|
3
|
+
import { findHighestPriorityRule, onRulesChanged } from './trust-store.js';
|
|
4
4
|
import { resolveSkillSelector } from '../config/skills.js';
|
|
5
5
|
import { computeSkillVersionHash } from '../skills/version-hash.js';
|
|
6
6
|
import { getTool } from '../tools/registry.js';
|
|
@@ -11,9 +11,33 @@ import { homedir } from 'node:os';
|
|
|
11
11
|
import { looksLikeHostPortShorthand, looksLikePathOnlyInput } from '../tools/network/url-safety.js';
|
|
12
12
|
import { normalizeFilePath, isSkillSourcePath } from '../skills/path-classifier.js';
|
|
13
13
|
import { isWorkspaceScopedInvocation } from './workspace-policy.js';
|
|
14
|
-
import { buildShellCommandCandidates, buildShellAllowlistOptions, type ParsedCommand } from './shell-identity.js';
|
|
14
|
+
import { buildShellCommandCandidates, buildShellAllowlistOptions, cachedParse, type ParsedCommand } from './shell-identity.js';
|
|
15
15
|
import type { ManifestOverride } from '../tools/execution-target.js';
|
|
16
16
|
|
|
17
|
+
// ── Risk classification cache ────────────────────────────────────────────────
|
|
18
|
+
// classifyRisk() is called on every permission check and can invoke WASM
|
|
19
|
+
// parsing for shell commands. Cache results keyed on (toolName, inputHash).
|
|
20
|
+
// Invalidated when trust rules change since risk classification for file tools
|
|
21
|
+
// depends on skill source path checks which reference config, but the core
|
|
22
|
+
// risk logic is input-deterministic.
|
|
23
|
+
const RISK_CACHE_MAX = 256;
|
|
24
|
+
const riskCache = new Map<string, RiskLevel>();
|
|
25
|
+
|
|
26
|
+
function riskCacheKey(toolName: string, input: Record<string, unknown>): string {
|
|
27
|
+
const inputJson = JSON.stringify(input);
|
|
28
|
+
const hash = createHash('sha256').update(inputJson).digest('hex');
|
|
29
|
+
return `${toolName}\0${hash}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Clear the risk classification cache. Called when trust rules change. */
|
|
33
|
+
export function clearRiskCache(): void {
|
|
34
|
+
riskCache.clear();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Invalidate risk cache whenever trust rules change so that risk decisions
|
|
38
|
+
// referencing config-dependent checks (e.g. skill source paths) stay fresh.
|
|
39
|
+
onRulesChanged(clearRiskCache);
|
|
40
|
+
|
|
17
41
|
// Ensures the legacy mode deprecation warning fires at most once per process.
|
|
18
42
|
let _legacyDeprecationWarned = false;
|
|
19
43
|
|
|
@@ -33,8 +57,8 @@ const LOW_RISK_PROGRAMS = new Set([
|
|
|
33
57
|
'man', 'help', 'info',
|
|
34
58
|
'env', 'printenv', 'set',
|
|
35
59
|
'diff', 'sort', 'uniq', 'cut', 'tr', 'tee', 'xargs',
|
|
36
|
-
'jq', 'yq',
|
|
37
|
-
'
|
|
60
|
+
'jq', 'yq',
|
|
61
|
+
'http', 'dig', 'nslookup', 'ping',
|
|
38
62
|
'tree', 'du', 'df',
|
|
39
63
|
]);
|
|
40
64
|
|
|
@@ -56,6 +80,41 @@ const LOW_RISK_GIT_SUBCOMMANDS = new Set([
|
|
|
56
80
|
'cat-file', 'reflog',
|
|
57
81
|
]);
|
|
58
82
|
|
|
83
|
+
// Commands that wrap another program — the real program appears as the first
|
|
84
|
+
// non-flag argument. When one of these is the segment program we look through
|
|
85
|
+
// its args to find the effective program (e.g. `env curl …` → curl).
|
|
86
|
+
const WRAPPER_PROGRAMS = new Set([
|
|
87
|
+
'env', 'nice', 'nohup', 'time', 'command', 'exec',
|
|
88
|
+
'strace', 'ltrace', 'ionice', 'taskset', 'timeout',
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
// `env` flags that consume the next positional argument as their value.
|
|
92
|
+
// Without this, `env -u curl echo` would incorrectly identify `curl` (the
|
|
93
|
+
// value of -u) as the wrapped program instead of `echo`.
|
|
94
|
+
const ENV_VALUE_FLAGS = new Set(['-u', '--unset', '-C', '--chdir']);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Given a segment whose program is a known wrapper, return the first
|
|
98
|
+
* non-flag argument (i.e. the wrapped program name). Returns `undefined`
|
|
99
|
+
* when no suitable argument is found.
|
|
100
|
+
*
|
|
101
|
+
* Handles `env` specially: skips `VAR=value` pairs and value-taking flags
|
|
102
|
+
* like `-u NAME` and `-C DIR`.
|
|
103
|
+
*/
|
|
104
|
+
function getWrappedProgram(seg: { program: string; args: string[] }): string | undefined {
|
|
105
|
+
const isEnv = seg.program === 'env';
|
|
106
|
+
for (let i = 0; i < seg.args.length; i++) {
|
|
107
|
+
const arg = seg.args[i];
|
|
108
|
+
if (arg.startsWith('-')) {
|
|
109
|
+
if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++; // skip the value argument
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (isEnv && arg.includes('=')) continue; // skip env VAR=value pairs
|
|
113
|
+
return arg;
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
59
118
|
function isHighRiskRm(args: string[]): boolean {
|
|
60
119
|
// rm with -r, -rf, -fr, or targeting root/home
|
|
61
120
|
for (const arg of args) {
|
|
@@ -245,7 +304,36 @@ async function buildCommandCandidates(toolName: string, input: Record<string, un
|
|
|
245
304
|
return [...new Set(candidates)];
|
|
246
305
|
}
|
|
247
306
|
|
|
248
|
-
export async function classifyRisk(toolName: string, input: Record<string, unknown>, workingDir?: string, preParsed?: ParsedCommand, manifestOverride?: ManifestOverride): Promise<RiskLevel> {
|
|
307
|
+
export async function classifyRisk(toolName: string, input: Record<string, unknown>, workingDir?: string, preParsed?: ParsedCommand, manifestOverride?: ManifestOverride, signal?: AbortSignal): Promise<RiskLevel> {
|
|
308
|
+
if (signal?.aborted) throw new Error('Cancelled');
|
|
309
|
+
|
|
310
|
+
// Check cache first (skip when preParsed is provided since caller already
|
|
311
|
+
// parsed and we'd just be duplicating the key computation cost).
|
|
312
|
+
const cacheKey = preParsed ? null : riskCacheKey(toolName, input);
|
|
313
|
+
if (cacheKey) {
|
|
314
|
+
const cached = riskCache.get(cacheKey);
|
|
315
|
+
if (cached !== undefined) {
|
|
316
|
+
// LRU refresh
|
|
317
|
+
riskCache.delete(cacheKey);
|
|
318
|
+
riskCache.set(cacheKey, cached);
|
|
319
|
+
return cached;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const result = await classifyRiskUncached(toolName, input, workingDir, preParsed, manifestOverride);
|
|
324
|
+
|
|
325
|
+
if (cacheKey) {
|
|
326
|
+
if (riskCache.size >= RISK_CACHE_MAX) {
|
|
327
|
+
const oldest = riskCache.keys().next().value;
|
|
328
|
+
if (oldest !== undefined) riskCache.delete(oldest);
|
|
329
|
+
}
|
|
330
|
+
riskCache.set(cacheKey, result);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function classifyRiskUncached(toolName: string, input: Record<string, unknown>, workingDir?: string, preParsed?: ParsedCommand, manifestOverride?: ManifestOverride): Promise<RiskLevel> {
|
|
249
337
|
if (toolName === 'file_read') return RiskLevel.Low;
|
|
250
338
|
if (toolName === 'file_write' || toolName === 'file_edit') {
|
|
251
339
|
const filePath = getStringField(input, 'path', 'file_path');
|
|
@@ -285,7 +373,7 @@ export async function classifyRisk(toolName: string, input: Record<string, unkno
|
|
|
285
373
|
const command = (input.command as string) ?? '';
|
|
286
374
|
if (!command.trim()) return RiskLevel.Low;
|
|
287
375
|
|
|
288
|
-
const parsed = preParsed ?? await
|
|
376
|
+
const parsed = preParsed ?? await cachedParse(command);
|
|
289
377
|
|
|
290
378
|
// Dangerous patterns → High
|
|
291
379
|
if (parsed.dangerousPatterns.length > 0) return RiskLevel.High;
|
|
@@ -307,11 +395,27 @@ export async function classifyRisk(toolName: string, input: Record<string, unkno
|
|
|
307
395
|
continue;
|
|
308
396
|
}
|
|
309
397
|
|
|
310
|
-
if (prog === 'chmod' || prog === 'chown' || prog === 'chgrp'
|
|
398
|
+
if (prog === 'chmod' || prog === 'chown' || prog === 'chgrp'
|
|
399
|
+
|| prog === 'sed' || prog === 'awk') {
|
|
400
|
+
maxRisk = RiskLevel.Medium;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// curl/wget can download and execute arbitrary code from the internet.
|
|
405
|
+
// Also catch wrapped invocations like `env curl …` or `nice wget …`.
|
|
406
|
+
if (prog === 'curl' || prog === 'wget') {
|
|
311
407
|
maxRisk = RiskLevel.Medium;
|
|
312
408
|
continue;
|
|
313
409
|
}
|
|
314
410
|
|
|
411
|
+
if (WRAPPER_PROGRAMS.has(prog)) {
|
|
412
|
+
const wrapped = getWrappedProgram(seg);
|
|
413
|
+
if (wrapped === 'curl' || wrapped === 'wget') {
|
|
414
|
+
maxRisk = RiskLevel.Medium;
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
315
419
|
if (prog === 'git') {
|
|
316
420
|
const subcommand = seg.args[0];
|
|
317
421
|
if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
|
|
@@ -360,17 +464,20 @@ export async function check(
|
|
|
360
464
|
workingDir: string,
|
|
361
465
|
policyContext?: PolicyContext,
|
|
362
466
|
manifestOverride?: ManifestOverride,
|
|
467
|
+
signal?: AbortSignal,
|
|
363
468
|
): Promise<PermissionCheckResult> {
|
|
469
|
+
if (signal?.aborted) throw new Error('Cancelled');
|
|
470
|
+
|
|
364
471
|
// For shell tools, parse once and share the result to avoid duplicate tree-sitter work.
|
|
365
472
|
let shellParsed: ParsedCommand | undefined;
|
|
366
473
|
if (toolName === 'bash' || toolName === 'host_bash') {
|
|
367
474
|
const command = ((input.command as string) ?? '').trim();
|
|
368
475
|
if (command) {
|
|
369
|
-
shellParsed = await
|
|
476
|
+
shellParsed = await cachedParse(command);
|
|
370
477
|
}
|
|
371
478
|
}
|
|
372
479
|
|
|
373
|
-
const risk = await classifyRisk(toolName, input, workingDir, shellParsed, manifestOverride);
|
|
480
|
+
const risk = await classifyRisk(toolName, input, workingDir, shellParsed, manifestOverride, signal);
|
|
374
481
|
|
|
375
482
|
// Build command string candidates for rule matching
|
|
376
483
|
const commandCandidates = await buildCommandCandidates(toolName, input, workingDir, shellParsed);
|
|
@@ -500,7 +607,8 @@ function friendlyHostname(url: URL): string {
|
|
|
500
607
|
return url.hostname.replace(/^www\./, '');
|
|
501
608
|
}
|
|
502
609
|
|
|
503
|
-
export async function generateAllowlistOptions(toolName: string, input: Record<string, unknown
|
|
610
|
+
export async function generateAllowlistOptions(toolName: string, input: Record<string, unknown>, signal?: AbortSignal): Promise<AllowlistOption[]> {
|
|
611
|
+
if (signal?.aborted) throw new Error('Cancelled');
|
|
504
612
|
if (toolName === 'bash' || toolName === 'host_bash') {
|
|
505
613
|
const command = ((input.command as string) ?? '').trim();
|
|
506
614
|
return buildShellAllowlistOptions(command);
|
|
@@ -43,12 +43,15 @@ export class PermissionPrompter {
|
|
|
43
43
|
sessionId?: string,
|
|
44
44
|
executionTarget?: ExecutionTarget,
|
|
45
45
|
persistentDecisionsAllowed?: boolean,
|
|
46
|
+
signal?: AbortSignal,
|
|
46
47
|
): Promise<{
|
|
47
48
|
decision: UserDecision;
|
|
48
49
|
selectedPattern?: string;
|
|
49
50
|
selectedScope?: string;
|
|
50
51
|
decisionContext?: string;
|
|
51
52
|
}> {
|
|
53
|
+
if (signal?.aborted) return { decision: 'deny' };
|
|
54
|
+
|
|
52
55
|
const requestId = uuid();
|
|
53
56
|
|
|
54
57
|
return new Promise((resolve, reject) => {
|
|
@@ -61,6 +64,17 @@ export class PermissionPrompter {
|
|
|
61
64
|
|
|
62
65
|
this.pending.set(requestId, { resolve, reject, timer });
|
|
63
66
|
|
|
67
|
+
if (signal) {
|
|
68
|
+
const onAbort = () => {
|
|
69
|
+
if (this.pending.has(requestId)) {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
this.pending.delete(requestId);
|
|
72
|
+
resolve({ decision: 'deny' });
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
76
|
+
}
|
|
77
|
+
|
|
64
78
|
this.sendToClient({
|
|
65
79
|
type: 'confirmation_request',
|
|
66
80
|
requestId,
|
|
@@ -3,6 +3,36 @@ import type { AllowlistOption } from './types.js';
|
|
|
3
3
|
|
|
4
4
|
export type { ParsedCommand };
|
|
5
5
|
|
|
6
|
+
// ── Shell parse result cache ─────────────────────────────────────────────────
|
|
7
|
+
// Shell parsing via web-tree-sitter WASM is deterministic — the same command
|
|
8
|
+
// string always produces the same ParsedCommand. Cache results to avoid
|
|
9
|
+
// redundant WASM invocations on repeated permission checks.
|
|
10
|
+
const PARSE_CACHE_MAX = 256;
|
|
11
|
+
const parseCache = new Map<string, ParsedCommand>();
|
|
12
|
+
|
|
13
|
+
export async function cachedParse(command: string): Promise<ParsedCommand> {
|
|
14
|
+
const cached = parseCache.get(command);
|
|
15
|
+
if (cached !== undefined) {
|
|
16
|
+
// LRU refresh: move to end of insertion order
|
|
17
|
+
parseCache.delete(command);
|
|
18
|
+
parseCache.set(command, cached);
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
21
|
+
const result = await parse(command);
|
|
22
|
+
// Evict oldest entry if at capacity
|
|
23
|
+
if (parseCache.size >= PARSE_CACHE_MAX) {
|
|
24
|
+
const oldest = parseCache.keys().next().value;
|
|
25
|
+
if (oldest !== undefined) parseCache.delete(oldest);
|
|
26
|
+
}
|
|
27
|
+
parseCache.set(command, result);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Clear the shell parse cache. Exposed for testing. */
|
|
32
|
+
export function clearShellParseCache(): void {
|
|
33
|
+
parseCache.clear();
|
|
34
|
+
}
|
|
35
|
+
|
|
6
36
|
export interface ShellActionKey {
|
|
7
37
|
/** e.g. "action:gh", "action:gh pr", "action:gh pr view" */
|
|
8
38
|
key: string;
|
|
@@ -40,7 +70,7 @@ const MAX_ACTION_KEY_DEPTH = 3;
|
|
|
40
70
|
* identity information for permission decisions.
|
|
41
71
|
*/
|
|
42
72
|
export async function analyzeShellCommand(command: string, preParsed?: ParsedCommand): Promise<ShellIdentityAnalysis> {
|
|
43
|
-
const parsed = preParsed ?? await
|
|
73
|
+
const parsed = preParsed ?? await cachedParse(command);
|
|
44
74
|
|
|
45
75
|
const operators: string[] = [];
|
|
46
76
|
for (const seg of parsed.segments) {
|
|
@@ -21,6 +21,21 @@ interface TrustFile {
|
|
|
21
21
|
let cachedRules: TrustRule[] | null = null;
|
|
22
22
|
let cachedStarterBundleAccepted: boolean | null = null;
|
|
23
23
|
|
|
24
|
+
// Callbacks invoked when trust rules change (add/update/remove/clear).
|
|
25
|
+
// Used by the permission checker to invalidate dependent caches.
|
|
26
|
+
const rulesChangedListeners: Array<() => void> = [];
|
|
27
|
+
|
|
28
|
+
/** Register a callback to be invoked whenever trust rules change. */
|
|
29
|
+
export function onRulesChanged(listener: () => void): void {
|
|
30
|
+
rulesChangedListeners.push(listener);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function notifyRulesChanged(): void {
|
|
34
|
+
for (const listener of rulesChangedListeners) {
|
|
35
|
+
listener();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
/**
|
|
25
40
|
* Cache of pre-compiled Minimatch objects keyed by pattern string.
|
|
26
41
|
* Rebuilt whenever cachedRules changes. Avoids re-parsing glob patterns
|
|
@@ -326,7 +341,7 @@ function saveToDisk(rules: TrustRule[]): void {
|
|
|
326
341
|
}
|
|
327
342
|
|
|
328
343
|
function getRules(): TrustRule[] {
|
|
329
|
-
if (cachedRules
|
|
344
|
+
if (cachedRules == null) {
|
|
330
345
|
cachedRules = loadFromDisk();
|
|
331
346
|
rebuildPatternCache(cachedRules);
|
|
332
347
|
}
|
|
@@ -368,6 +383,7 @@ export function addRule(
|
|
|
368
383
|
cachedRules = rules;
|
|
369
384
|
rebuildPatternCache(rules);
|
|
370
385
|
saveToDisk(rules);
|
|
386
|
+
notifyRulesChanged();
|
|
371
387
|
log.info({ rule }, 'Added trust rule');
|
|
372
388
|
return rule;
|
|
373
389
|
}
|
|
@@ -395,6 +411,7 @@ export function updateRule(
|
|
|
395
411
|
cachedRules = rules;
|
|
396
412
|
rebuildPatternCache(rules);
|
|
397
413
|
saveToDisk(rules);
|
|
414
|
+
notifyRulesChanged();
|
|
398
415
|
log.info({ rule }, 'Updated trust rule');
|
|
399
416
|
return rule;
|
|
400
417
|
}
|
|
@@ -412,6 +429,7 @@ export function removeRule(id: string): boolean {
|
|
|
412
429
|
cachedRules = rules;
|
|
413
430
|
rebuildPatternCache(rules);
|
|
414
431
|
saveToDisk(rules);
|
|
432
|
+
notifyRulesChanged();
|
|
415
433
|
log.info({ id }, 'Removed trust rule');
|
|
416
434
|
return true;
|
|
417
435
|
}
|
|
@@ -508,6 +526,7 @@ export function clearAllRules(): void {
|
|
|
508
526
|
cachedRules = rules;
|
|
509
527
|
rebuildPatternCache(rules);
|
|
510
528
|
saveToDisk(rules);
|
|
529
|
+
notifyRulesChanged();
|
|
511
530
|
log.info('Cleared all user trust rules (default rules preserved)');
|
|
512
531
|
}
|
|
513
532
|
|
|
@@ -608,6 +627,7 @@ export function acceptStarterBundle(): AcceptStarterBundleResult {
|
|
|
608
627
|
cachedRules = rules;
|
|
609
628
|
rebuildPatternCache(rules);
|
|
610
629
|
saveToDisk(rules);
|
|
630
|
+
notifyRulesChanged();
|
|
611
631
|
log.info({ rulesAdded: added }, 'Starter approval bundle accepted');
|
|
612
632
|
|
|
613
633
|
return { accepted: true, rulesAdded: added, alreadyAccepted: false };
|
|
@@ -58,12 +58,12 @@ export const PLACEHOLDER_BLOCKS_OMITTED = '\x00__PLACEHOLDER__[internal blocks o
|
|
|
58
58
|
|
|
59
59
|
/** Type-guard for tool_use blocks in Anthropic-formatted content. */
|
|
60
60
|
function isToolUseBlock(block: unknown): block is Anthropic.ToolUseBlockParam {
|
|
61
|
-
return typeof block === 'object' && block
|
|
61
|
+
return typeof block === 'object' && block != null && (block as { type: string }).type === 'tool_use';
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/** Type-guard for tool_result blocks in Anthropic-formatted content. */
|
|
65
65
|
function isToolResultBlock(block: unknown): block is Anthropic.ToolResultBlockParam {
|
|
66
|
-
return typeof block === 'object' && block
|
|
66
|
+
return typeof block === 'object' && block != null && (block as { type: string }).type === 'tool_result';
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -379,12 +379,12 @@ export class AnthropicProvider implements Provider {
|
|
|
379
379
|
const content = m.content
|
|
380
380
|
.map((block) => {
|
|
381
381
|
const result = this.toAnthropicBlockSafe(block);
|
|
382
|
-
if (result
|
|
382
|
+
if (result == null) {
|
|
383
383
|
droppedUnknownBlock = true;
|
|
384
384
|
}
|
|
385
385
|
return result;
|
|
386
386
|
})
|
|
387
|
-
.filter((block): block is Anthropic.ContentBlockParam => block
|
|
387
|
+
.filter((block): block is Anthropic.ContentBlockParam => block != null)
|
|
388
388
|
.filter((block) => !(block.type === 'text' && !(block as { text?: string }).text?.trim()));
|
|
389
389
|
|
|
390
390
|
// Preserve assistant turns that would otherwise become empty after filtering
|
|
@@ -77,7 +77,7 @@ export class FailoverProvider implements Provider {
|
|
|
77
77
|
const now = Date.now();
|
|
78
78
|
|
|
79
79
|
// Skip providers that are still in cooldown
|
|
80
|
-
if (health.unhealthySince
|
|
80
|
+
if (health.unhealthySince != null) {
|
|
81
81
|
const elapsed = now - health.unhealthySince;
|
|
82
82
|
if (elapsed < this.cooldownMs) {
|
|
83
83
|
log.debug(
|
|
@@ -93,7 +93,7 @@ export class FailoverProvider implements Provider {
|
|
|
93
93
|
try {
|
|
94
94
|
const response = await provider.sendMessage(messages, tools, systemPrompt, options);
|
|
95
95
|
// Success — mark healthy
|
|
96
|
-
if (health.unhealthySince
|
|
96
|
+
if (health.unhealthySince != null) {
|
|
97
97
|
log.info({ provider: provider.name }, 'Provider recovered, marking healthy');
|
|
98
98
|
health.unhealthySince = null;
|
|
99
99
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ModelIntent } from './types.js';
|
|
2
|
+
|
|
3
|
+
const PROVIDER_DEFAULT_MODELS = {
|
|
4
|
+
anthropic: 'claude-opus-4-6',
|
|
5
|
+
openai: 'gpt-5.2',
|
|
6
|
+
gemini: 'gemini-3-flash',
|
|
7
|
+
ollama: 'llama3.2',
|
|
8
|
+
fireworks: 'accounts/fireworks/models/kimi-k2p5',
|
|
9
|
+
openrouter: 'x-ai/grok-4',
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
type KnownProviderName = keyof typeof PROVIDER_DEFAULT_MODELS;
|
|
13
|
+
|
|
14
|
+
const PROVIDER_MODEL_INTENTS: Record<KnownProviderName, Record<ModelIntent, string>> = {
|
|
15
|
+
anthropic: {
|
|
16
|
+
'latency-optimized': 'claude-haiku-4-5-20251001',
|
|
17
|
+
'quality-optimized': 'claude-opus-4-6',
|
|
18
|
+
'vision-optimized': 'claude-sonnet-4-6',
|
|
19
|
+
},
|
|
20
|
+
openai: {
|
|
21
|
+
'latency-optimized': 'gpt-4o-mini',
|
|
22
|
+
'quality-optimized': 'gpt-5.2',
|
|
23
|
+
'vision-optimized': 'gpt-4o',
|
|
24
|
+
},
|
|
25
|
+
gemini: {
|
|
26
|
+
'latency-optimized': 'gemini-3-flash',
|
|
27
|
+
'quality-optimized': 'gemini-3-flash',
|
|
28
|
+
'vision-optimized': 'gemini-3-flash',
|
|
29
|
+
},
|
|
30
|
+
ollama: {
|
|
31
|
+
'latency-optimized': 'llama3.2',
|
|
32
|
+
'quality-optimized': 'llama3.2',
|
|
33
|
+
'vision-optimized': 'llama3.2',
|
|
34
|
+
},
|
|
35
|
+
fireworks: {
|
|
36
|
+
'latency-optimized': 'accounts/fireworks/models/kimi-k2p5',
|
|
37
|
+
'quality-optimized': 'accounts/fireworks/models/kimi-k2p5',
|
|
38
|
+
'vision-optimized': 'accounts/fireworks/models/kimi-k2p5',
|
|
39
|
+
},
|
|
40
|
+
openrouter: {
|
|
41
|
+
'latency-optimized': 'x-ai/grok-4',
|
|
42
|
+
'quality-optimized': 'x-ai/grok-4',
|
|
43
|
+
'vision-optimized': 'x-ai/grok-4',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const MODEL_INTENTS = new Set<ModelIntent>([
|
|
48
|
+
'latency-optimized',
|
|
49
|
+
'quality-optimized',
|
|
50
|
+
'vision-optimized',
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
export function isModelIntent(value: unknown): value is ModelIntent {
|
|
54
|
+
return typeof value === 'string' && MODEL_INTENTS.has(value as ModelIntent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getProviderDefaultModel(providerName: string): string {
|
|
58
|
+
const knownProvider = providerName as KnownProviderName;
|
|
59
|
+
return PROVIDER_DEFAULT_MODELS[knownProvider] ?? PROVIDER_DEFAULT_MODELS.anthropic;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function resolveModelIntent(providerName: string, intent: ModelIntent): string {
|
|
63
|
+
const knownProvider = providerName as KnownProviderName;
|
|
64
|
+
const providerIntentModels = PROVIDER_MODEL_INTENTS[knownProvider];
|
|
65
|
+
if (providerIntentModels) {
|
|
66
|
+
return providerIntentModels[intent];
|
|
67
|
+
}
|
|
68
|
+
return getProviderDefaultModel(providerName);
|
|
69
|
+
}
|
|
70
|
+
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OpenAIProvider } from '../openai/client.js';
|
|
2
|
+
import { getOllamaBaseUrlEnv } from '../../config/env.js';
|
|
2
3
|
|
|
3
4
|
export interface OllamaProviderOptions {
|
|
4
5
|
apiKey?: string;
|
|
@@ -20,7 +21,7 @@ export class OllamaProvider extends OpenAIProvider {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function resolveBaseUrl(optionBaseUrl?: string): string {
|
|
23
|
-
for (const candidate of [optionBaseUrl,
|
|
24
|
+
for (const candidate of [optionBaseUrl, getOllamaBaseUrlEnv()]) {
|
|
24
25
|
if (typeof candidate === 'string') {
|
|
25
26
|
const trimmed = candidate.trim();
|
|
26
27
|
if (trimmed.length > 0) return trimmed;
|