@vellumai/assistant 0.3.5 → 0.3.7
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 +27 -3
- package/src/config/env-registry.ts +169 -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 +157 -1138
- 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 +254 -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 +74 -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 +321 -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 +62 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +227 -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 +98 -4
- package/src/daemon/session-runtime-assembly.ts +149 -15
- package/src/daemon/session-surfaces.ts +26 -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 +12 -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 +163 -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 +126 -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/assistant-event-hub.ts +3 -1
- 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 +289 -745
- 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 +144 -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 +13 -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 +80 -18
- 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 +25 -18
- 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,40 @@ 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
|
|
20
|
+
// (toolName, inputHash, workingDir, manifestOverride).
|
|
21
|
+
// Invalidated when trust rules change since risk classification for file tools
|
|
22
|
+
// depends on skill source path checks which reference config, but the core
|
|
23
|
+
// risk logic is input-deterministic.
|
|
24
|
+
const RISK_CACHE_MAX = 256;
|
|
25
|
+
const riskCache = new Map<string, RiskLevel>();
|
|
26
|
+
|
|
27
|
+
function riskCacheKey(toolName: string, input: Record<string, unknown>, workingDir?: string, manifestOverride?: ManifestOverride): string {
|
|
28
|
+
const inputJson = JSON.stringify(input);
|
|
29
|
+
const hash = createHash('sha256')
|
|
30
|
+
.update(inputJson)
|
|
31
|
+
.update('\0')
|
|
32
|
+
.update(workingDir ?? '')
|
|
33
|
+
.update('\0')
|
|
34
|
+
.update(manifestOverride ? JSON.stringify(manifestOverride) : '')
|
|
35
|
+
.digest('hex');
|
|
36
|
+
return `${toolName}\0${hash}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Clear the risk classification cache. Called when trust rules change. */
|
|
40
|
+
export function clearRiskCache(): void {
|
|
41
|
+
riskCache.clear();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Invalidate risk cache whenever trust rules change so that risk decisions
|
|
45
|
+
// referencing config-dependent checks (e.g. skill source paths) stay fresh.
|
|
46
|
+
onRulesChanged(clearRiskCache);
|
|
47
|
+
|
|
17
48
|
// Ensures the legacy mode deprecation warning fires at most once per process.
|
|
18
49
|
let _legacyDeprecationWarned = false;
|
|
19
50
|
|
|
@@ -33,8 +64,8 @@ const LOW_RISK_PROGRAMS = new Set([
|
|
|
33
64
|
'man', 'help', 'info',
|
|
34
65
|
'env', 'printenv', 'set',
|
|
35
66
|
'diff', 'sort', 'uniq', 'cut', 'tr', 'tee', 'xargs',
|
|
36
|
-
'jq', 'yq',
|
|
37
|
-
'
|
|
67
|
+
'jq', 'yq',
|
|
68
|
+
'http', 'dig', 'nslookup', 'ping',
|
|
38
69
|
'tree', 'du', 'df',
|
|
39
70
|
]);
|
|
40
71
|
|
|
@@ -56,6 +87,41 @@ const LOW_RISK_GIT_SUBCOMMANDS = new Set([
|
|
|
56
87
|
'cat-file', 'reflog',
|
|
57
88
|
]);
|
|
58
89
|
|
|
90
|
+
// Commands that wrap another program — the real program appears as the first
|
|
91
|
+
// non-flag argument. When one of these is the segment program we look through
|
|
92
|
+
// its args to find the effective program (e.g. `env curl …` → curl).
|
|
93
|
+
const WRAPPER_PROGRAMS = new Set([
|
|
94
|
+
'env', 'nice', 'nohup', 'time', 'command', 'exec',
|
|
95
|
+
'strace', 'ltrace', 'ionice', 'taskset', 'timeout',
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
// `env` flags that consume the next positional argument as their value.
|
|
99
|
+
// Without this, `env -u curl echo` would incorrectly identify `curl` (the
|
|
100
|
+
// value of -u) as the wrapped program instead of `echo`.
|
|
101
|
+
const ENV_VALUE_FLAGS = new Set(['-u', '--unset', '-C', '--chdir']);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Given a segment whose program is a known wrapper, return the first
|
|
105
|
+
* non-flag argument (i.e. the wrapped program name). Returns `undefined`
|
|
106
|
+
* when no suitable argument is found.
|
|
107
|
+
*
|
|
108
|
+
* Handles `env` specially: skips `VAR=value` pairs and value-taking flags
|
|
109
|
+
* like `-u NAME` and `-C DIR`.
|
|
110
|
+
*/
|
|
111
|
+
function getWrappedProgram(seg: { program: string; args: string[] }): string | undefined {
|
|
112
|
+
const isEnv = seg.program === 'env';
|
|
113
|
+
for (let i = 0; i < seg.args.length; i++) {
|
|
114
|
+
const arg = seg.args[i];
|
|
115
|
+
if (arg.startsWith('-')) {
|
|
116
|
+
if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++; // skip the value argument
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (isEnv && arg.includes('=')) continue; // skip env VAR=value pairs
|
|
120
|
+
return arg;
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
59
125
|
function isHighRiskRm(args: string[]): boolean {
|
|
60
126
|
// rm with -r, -rf, -fr, or targeting root/home
|
|
61
127
|
for (const arg of args) {
|
|
@@ -245,7 +311,36 @@ async function buildCommandCandidates(toolName: string, input: Record<string, un
|
|
|
245
311
|
return [...new Set(candidates)];
|
|
246
312
|
}
|
|
247
313
|
|
|
248
|
-
export async function classifyRisk(toolName: string, input: Record<string, unknown>, workingDir?: string, preParsed?: ParsedCommand, manifestOverride?: ManifestOverride): Promise<RiskLevel> {
|
|
314
|
+
export async function classifyRisk(toolName: string, input: Record<string, unknown>, workingDir?: string, preParsed?: ParsedCommand, manifestOverride?: ManifestOverride, signal?: AbortSignal): Promise<RiskLevel> {
|
|
315
|
+
signal?.throwIfAborted();
|
|
316
|
+
|
|
317
|
+
// Check cache first (skip when preParsed is provided since caller already
|
|
318
|
+
// parsed and we'd just be duplicating the key computation cost).
|
|
319
|
+
const cacheKey = preParsed ? null : riskCacheKey(toolName, input, workingDir, manifestOverride);
|
|
320
|
+
if (cacheKey) {
|
|
321
|
+
const cached = riskCache.get(cacheKey);
|
|
322
|
+
if (cached !== undefined) {
|
|
323
|
+
// LRU refresh
|
|
324
|
+
riskCache.delete(cacheKey);
|
|
325
|
+
riskCache.set(cacheKey, cached);
|
|
326
|
+
return cached;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const result = await classifyRiskUncached(toolName, input, workingDir, preParsed, manifestOverride);
|
|
331
|
+
|
|
332
|
+
if (cacheKey) {
|
|
333
|
+
if (riskCache.size >= RISK_CACHE_MAX) {
|
|
334
|
+
const oldest = riskCache.keys().next().value;
|
|
335
|
+
if (oldest !== undefined) riskCache.delete(oldest);
|
|
336
|
+
}
|
|
337
|
+
riskCache.set(cacheKey, result);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function classifyRiskUncached(toolName: string, input: Record<string, unknown>, workingDir?: string, preParsed?: ParsedCommand, manifestOverride?: ManifestOverride): Promise<RiskLevel> {
|
|
249
344
|
if (toolName === 'file_read') return RiskLevel.Low;
|
|
250
345
|
if (toolName === 'file_write' || toolName === 'file_edit') {
|
|
251
346
|
const filePath = getStringField(input, 'path', 'file_path');
|
|
@@ -285,7 +380,7 @@ export async function classifyRisk(toolName: string, input: Record<string, unkno
|
|
|
285
380
|
const command = (input.command as string) ?? '';
|
|
286
381
|
if (!command.trim()) return RiskLevel.Low;
|
|
287
382
|
|
|
288
|
-
const parsed = preParsed ?? await
|
|
383
|
+
const parsed = preParsed ?? await cachedParse(command);
|
|
289
384
|
|
|
290
385
|
// Dangerous patterns → High
|
|
291
386
|
if (parsed.dangerousPatterns.length > 0) return RiskLevel.High;
|
|
@@ -307,11 +402,27 @@ export async function classifyRisk(toolName: string, input: Record<string, unkno
|
|
|
307
402
|
continue;
|
|
308
403
|
}
|
|
309
404
|
|
|
310
|
-
if (prog === 'chmod' || prog === 'chown' || prog === 'chgrp'
|
|
405
|
+
if (prog === 'chmod' || prog === 'chown' || prog === 'chgrp'
|
|
406
|
+
|| prog === 'sed' || prog === 'awk') {
|
|
407
|
+
maxRisk = RiskLevel.Medium;
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// curl/wget can download and execute arbitrary code from the internet.
|
|
412
|
+
// Also catch wrapped invocations like `env curl …` or `nice wget …`.
|
|
413
|
+
if (prog === 'curl' || prog === 'wget') {
|
|
311
414
|
maxRisk = RiskLevel.Medium;
|
|
312
415
|
continue;
|
|
313
416
|
}
|
|
314
417
|
|
|
418
|
+
if (WRAPPER_PROGRAMS.has(prog)) {
|
|
419
|
+
const wrapped = getWrappedProgram(seg);
|
|
420
|
+
if (wrapped === 'curl' || wrapped === 'wget') {
|
|
421
|
+
maxRisk = RiskLevel.Medium;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
315
426
|
if (prog === 'git') {
|
|
316
427
|
const subcommand = seg.args[0];
|
|
317
428
|
if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
|
|
@@ -360,17 +471,20 @@ export async function check(
|
|
|
360
471
|
workingDir: string,
|
|
361
472
|
policyContext?: PolicyContext,
|
|
362
473
|
manifestOverride?: ManifestOverride,
|
|
474
|
+
signal?: AbortSignal,
|
|
363
475
|
): Promise<PermissionCheckResult> {
|
|
476
|
+
signal?.throwIfAborted();
|
|
477
|
+
|
|
364
478
|
// For shell tools, parse once and share the result to avoid duplicate tree-sitter work.
|
|
365
479
|
let shellParsed: ParsedCommand | undefined;
|
|
366
480
|
if (toolName === 'bash' || toolName === 'host_bash') {
|
|
367
481
|
const command = ((input.command as string) ?? '').trim();
|
|
368
482
|
if (command) {
|
|
369
|
-
shellParsed = await
|
|
483
|
+
shellParsed = await cachedParse(command);
|
|
370
484
|
}
|
|
371
485
|
}
|
|
372
486
|
|
|
373
|
-
const risk = await classifyRisk(toolName, input, workingDir, shellParsed, manifestOverride);
|
|
487
|
+
const risk = await classifyRisk(toolName, input, workingDir, shellParsed, manifestOverride, signal);
|
|
374
488
|
|
|
375
489
|
// Build command string candidates for rule matching
|
|
376
490
|
const commandCandidates = await buildCommandCandidates(toolName, input, workingDir, shellParsed);
|
|
@@ -500,7 +614,8 @@ function friendlyHostname(url: URL): string {
|
|
|
500
614
|
return url.hostname.replace(/^www\./, '');
|
|
501
615
|
}
|
|
502
616
|
|
|
503
|
-
export async function generateAllowlistOptions(toolName: string, input: Record<string, unknown
|
|
617
|
+
export async function generateAllowlistOptions(toolName: string, input: Record<string, unknown>, signal?: AbortSignal): Promise<AllowlistOption[]> {
|
|
618
|
+
signal?.throwIfAborted();
|
|
504
619
|
if (toolName === 'bash' || toolName === 'host_bash') {
|
|
505
620
|
const command = ((input.command as string) ?? '').trim();
|
|
506
621
|
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;
|