@vellumai/assistant 0.3.4 → 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/Dockerfile +2 -0
- package/README.md +88 -2
- 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 +31 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +799 -249
- 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 +32 -2
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1277 -98
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +36 -50
- package/src/__tests__/channel-guardian.test.ts +630 -22
- package/src/__tests__/channel-readiness-service.test.ts +324 -0
- 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 +14 -8
- 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 +7 -2
- package/src/__tests__/daemon-lifecycle.test.ts +13 -12
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/entity-search.test.ts +615 -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 +533 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +291 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -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 +6 -0
- package/src/__tests__/run-orchestrator.test.ts +42 -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 +321 -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 +126 -0
- 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 +167 -11
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- 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 +46 -30
- package/src/__tests__/work-item-output.test.ts +110 -0
- 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 +114 -10
- package/src/calls/call-orchestrator.ts +268 -59
- 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 +22 -14
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +308 -7
- package/src/calls/twilio-routes.ts +65 -12
- 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/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
- 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 +259 -0
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
- package/src/config/bundled-skills/messaging/SKILL.md +33 -8
- 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/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- 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 +28 -3
- 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 +158 -1133
- 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 +131 -56
- package/src/config/templates/IDENTITY.md +2 -2
- 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 +6 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
- 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 -1689
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +180 -0
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +11 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +56 -5
- package/src/daemon/handlers/shared.ts +6 -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 +17 -9
- 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 +70 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +229 -2426
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -377
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +68 -3
- package/src/daemon/server.ts +66 -46
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +117 -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 +199 -10
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +30 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +35 -2
- 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 +6 -4
- 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 +202 -2
- 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 +265 -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 +69 -0
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +8 -10
- package/src/memory/jobs-worker.ts +55 -36
- package/src/memory/media-store.ts +759 -0
- 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 +165 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +228 -7
- package/src/memory/search/entity.ts +205 -31
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +27 -23
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/search/types.ts +24 -0
- 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/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +201 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- 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 +133 -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 +253 -0
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +11 -24
- package/src/runtime/channel-guardian-service.ts +88 -21
- package/src/runtime/channel-readiness-service.ts +418 -0
- package/src/runtime/channel-readiness-types.ts +35 -0
- 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 -717
- package/src/runtime/http-types.ts +59 -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 +51 -7
- 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 -1588
- 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 +86 -35
- 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/materialize.ts +2 -2
- 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/calls/call-start.ts +1 -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/execution-target.ts +11 -1
- 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 +8 -4
- 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/types.ts +2 -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/router.ts +1 -1
- 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 +105 -363
- 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/vellum-skills/google-oauth-setup/SKILL.md +0 -199
package/src/tools/executor.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { readFileSync
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { pathExists, safeStatSync } from '../util/fs.js';
|
|
2
3
|
import { getTool, getAllTools } from './registry.js';
|
|
3
4
|
import type { ToolContext, ToolExecutionResult, ToolLifecycleEvent } from './types.js';
|
|
4
5
|
import { RiskLevel } from '../permissions/types.js';
|
|
@@ -20,6 +21,7 @@ import { getTaskRunRules } from '../tasks/ephemeral-permissions.js';
|
|
|
20
21
|
import { safeTimeoutMs, executeWithTimeout } from './execution-timeout.js';
|
|
21
22
|
import { buildPolicyContext } from './policy-context.js';
|
|
22
23
|
import { resolveExecutionTarget } from './execution-target.js';
|
|
24
|
+
import { isToolBlocked } from '../security/parental-control-store.js';
|
|
23
25
|
|
|
24
26
|
const log = getLogger('tool-executor');
|
|
25
27
|
|
|
@@ -52,6 +54,61 @@ export class ToolExecutor {
|
|
|
52
54
|
startedAtMs: startTime,
|
|
53
55
|
});
|
|
54
56
|
|
|
57
|
+
// Bail out immediately if the session was aborted before this tool started.
|
|
58
|
+
// Individual tool implementations check the signal themselves for mid-run
|
|
59
|
+
// cancellation, but a pre-execution check prevents expensive permission
|
|
60
|
+
// lookups and prompter interactions after the user has already cancelled.
|
|
61
|
+
if (context.signal?.aborted) {
|
|
62
|
+
const durationMs = Date.now() - startTime;
|
|
63
|
+
emitLifecycleEvent(context, {
|
|
64
|
+
type: 'error',
|
|
65
|
+
toolName: name,
|
|
66
|
+
executionTarget,
|
|
67
|
+
input,
|
|
68
|
+
workingDir: context.workingDir,
|
|
69
|
+
sessionId: context.sessionId,
|
|
70
|
+
conversationId: context.conversationId,
|
|
71
|
+
requestId: context.requestId,
|
|
72
|
+
riskLevel,
|
|
73
|
+
decision: 'error',
|
|
74
|
+
durationMs,
|
|
75
|
+
errorMessage: 'Cancelled',
|
|
76
|
+
isExpected: true,
|
|
77
|
+
errorCategory: 'tool_failure',
|
|
78
|
+
});
|
|
79
|
+
return { content: 'Cancelled', isError: true };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Reject tools blocked by parental control settings before any permission check.
|
|
83
|
+
if (isToolBlocked(name)) {
|
|
84
|
+
log.warn(
|
|
85
|
+
{
|
|
86
|
+
toolName: name,
|
|
87
|
+
sessionId: context.sessionId,
|
|
88
|
+
conversationId: context.conversationId,
|
|
89
|
+
principal: context.principal,
|
|
90
|
+
reason: 'blocked_by_parental_controls',
|
|
91
|
+
},
|
|
92
|
+
'Parental control blocked tool invocation',
|
|
93
|
+
);
|
|
94
|
+
const durationMs = Date.now() - startTime;
|
|
95
|
+
emitLifecycleEvent(context, {
|
|
96
|
+
type: 'permission_denied',
|
|
97
|
+
toolName: name,
|
|
98
|
+
executionTarget,
|
|
99
|
+
input,
|
|
100
|
+
workingDir: context.workingDir,
|
|
101
|
+
sessionId: context.sessionId,
|
|
102
|
+
conversationId: context.conversationId,
|
|
103
|
+
requestId: context.requestId,
|
|
104
|
+
riskLevel,
|
|
105
|
+
decision: 'deny',
|
|
106
|
+
reason: 'Blocked by parental control settings',
|
|
107
|
+
durationMs,
|
|
108
|
+
});
|
|
109
|
+
return { content: 'This tool is blocked by parental control settings.', isError: true };
|
|
110
|
+
}
|
|
111
|
+
|
|
55
112
|
// Gate tools not active for the current turn
|
|
56
113
|
if (context.allowedToolNames && !context.allowedToolNames.has(name)) {
|
|
57
114
|
const msg = `Tool "${name}" is not currently active. Load the skill that provides this tool first.`;
|
|
@@ -129,14 +186,14 @@ export class ToolExecutor {
|
|
|
129
186
|
|
|
130
187
|
try {
|
|
131
188
|
// Check permissions
|
|
132
|
-
const risk = await classifyRisk(name, input, context.workingDir);
|
|
189
|
+
const risk = await classifyRisk(name, input, context.workingDir, undefined, undefined, context.signal);
|
|
133
190
|
riskLevel = risk;
|
|
134
191
|
|
|
135
192
|
// Build principal context from tool metadata so policy rules can
|
|
136
193
|
// distinguish skill-provided tools from core built-ins. Also includes
|
|
137
194
|
// ephemeral rules when executing within a task run.
|
|
138
195
|
const policyContext = buildPolicyContext(tool, context);
|
|
139
|
-
const result = await check(name, input, context.workingDir, policyContext);
|
|
196
|
+
const result = await check(name, input, context.workingDir, policyContext, undefined, context.signal);
|
|
140
197
|
|
|
141
198
|
// Private threads force prompting for side-effect tools even when a
|
|
142
199
|
// trust/allow rule would auto-allow. Deny decisions are preserved —
|
|
@@ -198,7 +255,7 @@ export class ToolExecutor {
|
|
|
198
255
|
}
|
|
199
256
|
|
|
200
257
|
// Need user approval
|
|
201
|
-
const allowlistOptions = await generateAllowlistOptions(name, input);
|
|
258
|
+
const allowlistOptions = await generateAllowlistOptions(name, input, context.signal);
|
|
202
259
|
const scopeOptions = generateScopeOptions(context.workingDir, name);
|
|
203
260
|
|
|
204
261
|
// Compute preview diff for file tools so the user sees what will change
|
|
@@ -256,6 +313,7 @@ export class ToolExecutor {
|
|
|
256
313
|
context.conversationId,
|
|
257
314
|
executionTarget,
|
|
258
315
|
persistentDecisionsAllowed,
|
|
316
|
+
context.signal,
|
|
259
317
|
);
|
|
260
318
|
|
|
261
319
|
decision = response.decision;
|
|
@@ -575,6 +633,7 @@ export class ToolExecutor {
|
|
|
575
633
|
context.conversationId,
|
|
576
634
|
executionTarget,
|
|
577
635
|
false, // no persistent decisions
|
|
636
|
+
context.signal,
|
|
578
637
|
);
|
|
579
638
|
|
|
580
639
|
if (response.decision === 'deny' || response.decision === 'always_deny') {
|
|
@@ -790,10 +849,10 @@ function computePreviewDiff(
|
|
|
790
849
|
const pathCheck = sandboxPolicy(rawPath, workingDir, { mustExist: false });
|
|
791
850
|
if (!pathCheck.ok) return undefined;
|
|
792
851
|
const filePath = pathCheck.resolved;
|
|
793
|
-
const isNewFile = !
|
|
852
|
+
const isNewFile = !pathExists(filePath);
|
|
794
853
|
if (!isNewFile) {
|
|
795
|
-
const stat =
|
|
796
|
-
if (stat.size > MAX_FILE_SIZE_BYTES) return undefined;
|
|
854
|
+
const stat = safeStatSync(filePath);
|
|
855
|
+
if (!stat || stat.size > MAX_FILE_SIZE_BYTES) return undefined;
|
|
797
856
|
}
|
|
798
857
|
const oldContent = isNewFile ? '' : readFileSync(filePath, 'utf-8');
|
|
799
858
|
return { filePath, oldContent, newContent: content, isNewFile };
|
|
@@ -807,8 +866,8 @@ function computePreviewDiff(
|
|
|
807
866
|
const pathCheck = sandboxPolicy(rawPath, workingDir);
|
|
808
867
|
if (!pathCheck.ok) return undefined;
|
|
809
868
|
const filePath = pathCheck.resolved;
|
|
810
|
-
|
|
811
|
-
|
|
869
|
+
const stat = safeStatSync(filePath);
|
|
870
|
+
if (!stat) return undefined;
|
|
812
871
|
if (stat.size > MAX_FILE_SIZE_BYTES) return undefined;
|
|
813
872
|
const content = readFileSync(filePath, 'utf-8');
|
|
814
873
|
const replaceAll = input.replace_all === true;
|
|
@@ -139,7 +139,7 @@ class CliDiscoverTool implements Tool {
|
|
|
139
139
|
if (checkAuth && AUTH_CHECK_COMMANDS[name]) {
|
|
140
140
|
const [cmd, ...args] = AUTH_CHECK_COMMANDS[name];
|
|
141
141
|
const authOutput = await runQuick(cmd, args);
|
|
142
|
-
result.authenticated = authOutput
|
|
142
|
+
result.authenticated = authOutput != null && authOutput.length > 0;
|
|
143
143
|
if (authOutput) {
|
|
144
144
|
// Keep auth info brief — first line, max 200 chars
|
|
145
145
|
result.authInfo = truncate(authOutput.split('\n')[0], 200, '');
|
|
@@ -132,7 +132,7 @@ export function forwardHttpRequest(
|
|
|
132
132
|
if (policyCallback) {
|
|
133
133
|
policyCallback(hostname, parsed.port ? Number(parsed.port) : null, path, 'http')
|
|
134
134
|
.then((extraHeaders) => {
|
|
135
|
-
if (extraHeaders
|
|
135
|
+
if (extraHeaders == null) {
|
|
136
136
|
clientRes.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
137
137
|
clientRes.end('Forbidden');
|
|
138
138
|
return;
|
|
@@ -200,7 +200,7 @@ async function processRequest(
|
|
|
200
200
|
port,
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
if (rewriteResult
|
|
203
|
+
if (rewriteResult == null) {
|
|
204
204
|
const body = 'Forbidden';
|
|
205
205
|
tlsSocket.write(
|
|
206
206
|
`HTTP/1.1 403 Forbidden\r\nContent-Length: ${body.length}\r\nContent-Type: text/plain\r\n\r\n${body}`,
|
|
@@ -111,7 +111,7 @@ export function createProxyServer(config: ProxyServerConfig = {}): Server {
|
|
|
111
111
|
|
|
112
112
|
config.policyCallback(connectTarget.host, connectTarget.port === 443 ? null : connectTarget.port, '/', 'https')
|
|
113
113
|
.then((extraHeaders) => {
|
|
114
|
-
if (extraHeaders
|
|
114
|
+
if (extraHeaders == null) {
|
|
115
115
|
clientSocket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
|
|
116
116
|
clientSocket.destroy();
|
|
117
117
|
return;
|
|
@@ -21,6 +21,7 @@ import type { CredentialInjectionTemplate } from '../../credentials/policy-types
|
|
|
21
21
|
import { getSecureKey } from '../../../security/secure-keys.js';
|
|
22
22
|
import { buildDecisionTrace, stripQueryString } from './logging.js';
|
|
23
23
|
import { getLogger } from '../../../util/logger.js';
|
|
24
|
+
import { silentlyWithLog } from '../../../util/silently.js';
|
|
24
25
|
|
|
25
26
|
const log = getLogger('proxy-session');
|
|
26
27
|
|
|
@@ -63,12 +64,12 @@ function cloneSession(s: ProxySession): ProxySession {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
function resetIdleTimer(managed: ManagedSession): void {
|
|
66
|
-
if (managed.idleTimer
|
|
67
|
+
if (managed.idleTimer != null) {
|
|
67
68
|
clearTimeout(managed.idleTimer);
|
|
68
69
|
}
|
|
69
70
|
managed.idleTimer = setTimeout(() => {
|
|
70
71
|
if (managed.session.status === 'active') {
|
|
71
|
-
stopSession(managed.session.id)
|
|
72
|
+
silentlyWithLog(stopSession(managed.session.id), 'idle session cleanup');
|
|
72
73
|
}
|
|
73
74
|
}, managed.config.idleTimeoutMs);
|
|
74
75
|
}
|
|
@@ -343,7 +344,7 @@ export async function stopSession(sessionId: ProxySessionId): Promise<void> {
|
|
|
343
344
|
managed.session.status = 'stopping';
|
|
344
345
|
|
|
345
346
|
const doStop = async () => {
|
|
346
|
-
if (managed.idleTimer
|
|
347
|
+
if (managed.idleTimer != null) {
|
|
347
348
|
clearTimeout(managed.idleTimer);
|
|
348
349
|
managed.idleTimer = null;
|
|
349
350
|
}
|
|
@@ -375,7 +376,7 @@ export function getSessionEnv(
|
|
|
375
376
|
): ProxyEnvVars {
|
|
376
377
|
const managed = sessions.get(sessionId);
|
|
377
378
|
if (!managed) throw new Error(`Session not found: ${sessionId}`);
|
|
378
|
-
if (managed.session.status !== 'active' || managed.session.port
|
|
379
|
+
if (managed.session.status !== 'active' || managed.session.port == null) {
|
|
379
380
|
throw new Error(`Session ${sessionId} is not active`);
|
|
380
381
|
}
|
|
381
382
|
|
|
@@ -529,6 +530,6 @@ export function getSessionsForConversation(conversationId: string): ProxySession
|
|
|
529
530
|
*/
|
|
530
531
|
export async function stopAllSessions(): Promise<void> {
|
|
531
532
|
const ids = [...sessions.keys()];
|
|
532
|
-
await Promise.all(ids.map((id) => stopSession(id).catch(() => {})));
|
|
533
|
+
await Promise.all(ids.map((id) => stopSession(id).catch((err: unknown) => log.debug({ err, id }, 'session shutdown error'))));
|
|
533
534
|
sessions.clear();
|
|
534
535
|
}
|
|
@@ -63,6 +63,7 @@ type WebFetchRequestExecutor = (
|
|
|
63
63
|
type ExecuteWebFetchOptions = {
|
|
64
64
|
resolveHostAddresses?: ResolveHostAddresses;
|
|
65
65
|
requestExecutor?: WebFetchRequestExecutor;
|
|
66
|
+
signal?: AbortSignal;
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
type NodeHttpResponseLike = {
|
|
@@ -451,6 +452,17 @@ export async function executeWebFetch(
|
|
|
451
452
|
controller.abort();
|
|
452
453
|
}, timeoutSeconds * 1000);
|
|
453
454
|
|
|
455
|
+
// Forward external cancellation signal to our controller
|
|
456
|
+
const externalSignal = options?.signal;
|
|
457
|
+
const onExternalAbort = () => controller.abort();
|
|
458
|
+
if (externalSignal) {
|
|
459
|
+
if (externalSignal.aborted) {
|
|
460
|
+
controller.abort();
|
|
461
|
+
} else {
|
|
462
|
+
externalSignal.addEventListener('abort', onExternalAbort, { once: true });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
454
466
|
try {
|
|
455
467
|
log.debug({ url: safeRequestedUrl, timeoutSeconds, maxChars, startIndex, rawMode }, 'Fetching webpage');
|
|
456
468
|
|
|
@@ -651,6 +663,9 @@ export async function executeWebFetch(
|
|
|
651
663
|
};
|
|
652
664
|
} catch (err) {
|
|
653
665
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
666
|
+
if (externalSignal?.aborted) {
|
|
667
|
+
return { content: 'Error: web fetch was cancelled', isError: true };
|
|
668
|
+
}
|
|
654
669
|
return { content: `Error: web fetch timed out after ${timeoutSeconds}s`, isError: true };
|
|
655
670
|
}
|
|
656
671
|
|
|
@@ -659,6 +674,7 @@ export async function executeWebFetch(
|
|
|
659
674
|
return { content: `Error: Web fetch failed: ${msg}`, isError: true };
|
|
660
675
|
} finally {
|
|
661
676
|
clearTimeout(timeoutHandle);
|
|
677
|
+
externalSignal?.removeEventListener('abort', onExternalAbort);
|
|
662
678
|
}
|
|
663
679
|
}
|
|
664
680
|
|
|
@@ -705,8 +721,8 @@ class WebFetchTool implements Tool {
|
|
|
705
721
|
};
|
|
706
722
|
}
|
|
707
723
|
|
|
708
|
-
async execute(input: Record<string, unknown>,
|
|
709
|
-
return executeWebFetch(input);
|
|
724
|
+
async execute(input: Record<string, unknown>, context: ToolContext): Promise<ToolExecutionResult> {
|
|
725
|
+
return executeWebFetch(input, { signal: context.signal });
|
|
710
726
|
}
|
|
711
727
|
}
|
|
712
728
|
|
|
@@ -115,6 +115,7 @@ async function executeBraveSearch(
|
|
|
115
115
|
offset: number,
|
|
116
116
|
freshness: string | undefined,
|
|
117
117
|
apiKey: string,
|
|
118
|
+
signal?: AbortSignal,
|
|
118
119
|
): Promise<ToolExecutionResult> {
|
|
119
120
|
const params = new URLSearchParams({
|
|
120
121
|
q: query,
|
|
@@ -136,6 +137,7 @@ async function executeBraveSearch(
|
|
|
136
137
|
'Accept-Encoding': 'gzip',
|
|
137
138
|
'X-Subscription-Token': apiKey,
|
|
138
139
|
},
|
|
140
|
+
signal,
|
|
139
141
|
});
|
|
140
142
|
|
|
141
143
|
if (response.ok) {
|
|
@@ -170,6 +172,7 @@ async function executeBraveSearch(
|
|
|
170
172
|
async function executePerplexitySearch(
|
|
171
173
|
query: string,
|
|
172
174
|
apiKey: string,
|
|
175
|
+
signal?: AbortSignal,
|
|
173
176
|
): Promise<ToolExecutionResult> {
|
|
174
177
|
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
175
178
|
const response = await fetch(PERPLEXITY_API_URL, {
|
|
@@ -184,6 +187,7 @@ async function executePerplexitySearch(
|
|
|
184
187
|
{ role: 'user', content: query },
|
|
185
188
|
],
|
|
186
189
|
}),
|
|
190
|
+
signal,
|
|
187
191
|
});
|
|
188
192
|
|
|
189
193
|
if (response.ok) {
|
|
@@ -249,7 +253,7 @@ class WebSearchTool implements Tool {
|
|
|
249
253
|
};
|
|
250
254
|
}
|
|
251
255
|
|
|
252
|
-
async execute(input: Record<string, unknown>,
|
|
256
|
+
async execute(input: Record<string, unknown>, context: ToolContext): Promise<ToolExecutionResult> {
|
|
253
257
|
const query = input.query;
|
|
254
258
|
if (!query || typeof query !== 'string') {
|
|
255
259
|
return { content: 'Error: query is required and must be a string', isError: true };
|
|
@@ -268,7 +272,7 @@ class WebSearchTool implements Tool {
|
|
|
268
272
|
apiKey = fallbackKey;
|
|
269
273
|
} else {
|
|
270
274
|
return {
|
|
271
|
-
content: 'Error: No web search API key configured. Set PERPLEXITY_API_KEY or BRAVE_API_KEY environment variable, or configure
|
|
275
|
+
content: 'Error: No web search API key configured. Set a PERPLEXITY_API_KEY or BRAVE_API_KEY environment variable, or configure it from the Settings page under API Keys.',
|
|
272
276
|
isError: true,
|
|
273
277
|
};
|
|
274
278
|
}
|
|
@@ -281,10 +285,10 @@ class WebSearchTool implements Tool {
|
|
|
281
285
|
const count = typeof input.count === 'number' ? Math.min(20, Math.max(1, Math.round(input.count))) : 10;
|
|
282
286
|
const offset = typeof input.offset === 'number' ? Math.min(9, Math.max(0, Math.round(input.offset))) : 0;
|
|
283
287
|
const freshness = typeof input.freshness === 'string' ? input.freshness : undefined;
|
|
284
|
-
return await executeBraveSearch(query, count, offset, freshness, apiKey);
|
|
288
|
+
return await executeBraveSearch(query, count, offset, freshness, apiKey, context.signal);
|
|
285
289
|
}
|
|
286
290
|
|
|
287
|
-
return await executePerplexitySearch(query, apiKey);
|
|
291
|
+
return await executePerplexitySearch(query, apiKey, context.signal);
|
|
288
292
|
} catch (err) {
|
|
289
293
|
const msg = err instanceof Error ? err.message : String(err);
|
|
290
294
|
log.error({ err }, 'Web search failed');
|
|
@@ -2,6 +2,7 @@ import { and, asc, eq, lte } from 'drizzle-orm';
|
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
3
|
import { getDb } from '../../memory/db.js';
|
|
4
4
|
import { reminders } from '../../memory/schema.js';
|
|
5
|
+
import { createRowMapper, cast } from '../../util/row-mapper.js';
|
|
5
6
|
|
|
6
7
|
export interface ReminderRow {
|
|
7
8
|
id: string;
|
|
@@ -16,6 +17,19 @@ export interface ReminderRow {
|
|
|
16
17
|
updatedAt: number;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
const parseRow = createRowMapper<typeof reminders.$inferSelect, ReminderRow>({
|
|
21
|
+
id: 'id',
|
|
22
|
+
label: 'label',
|
|
23
|
+
message: 'message',
|
|
24
|
+
fireAt: 'fireAt',
|
|
25
|
+
mode: { from: 'mode', transform: cast<ReminderRow['mode']>() },
|
|
26
|
+
status: { from: 'status', transform: cast<ReminderRow['status']>() },
|
|
27
|
+
firedAt: 'firedAt',
|
|
28
|
+
conversationId: 'conversationId',
|
|
29
|
+
createdAt: 'createdAt',
|
|
30
|
+
updatedAt: 'updatedAt',
|
|
31
|
+
});
|
|
32
|
+
|
|
19
33
|
export function insertReminder(params: {
|
|
20
34
|
label: string;
|
|
21
35
|
message: string;
|
|
@@ -131,18 +145,3 @@ export function setReminderConversationId(id: string, conversationId: string): v
|
|
|
131
145
|
.where(eq(reminders.id, id))
|
|
132
146
|
.run();
|
|
133
147
|
}
|
|
134
|
-
|
|
135
|
-
function parseRow(row: typeof reminders.$inferSelect): ReminderRow {
|
|
136
|
-
return {
|
|
137
|
-
id: row.id,
|
|
138
|
-
label: row.label,
|
|
139
|
-
message: row.message,
|
|
140
|
-
fireAt: row.fireAt,
|
|
141
|
-
mode: row.mode as ReminderRow['mode'],
|
|
142
|
-
status: row.status as ReminderRow['status'],
|
|
143
|
-
firedAt: row.firedAt,
|
|
144
|
-
conversationId: row.conversationId,
|
|
145
|
-
createdAt: row.createdAt,
|
|
146
|
-
updatedAt: row.updatedAt,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
@@ -66,6 +66,7 @@ export async function executeScheduleCreate(
|
|
|
66
66
|
return {
|
|
67
67
|
content: [
|
|
68
68
|
`Schedule created successfully.`,
|
|
69
|
+
` ID: ${job.id}`,
|
|
69
70
|
` Name: ${job.name}`,
|
|
70
71
|
` Syntax: ${job.syntax}`,
|
|
71
72
|
` Schedule: ${scheduleDescription}${job.timezone ? ` (${job.timezone})` : ''}`,
|
|
@@ -27,6 +27,7 @@ export async function executeScheduleList(
|
|
|
27
27
|
const runs = getScheduleRuns(jobId, 5);
|
|
28
28
|
const lines = [
|
|
29
29
|
`Schedule: ${job.name}`,
|
|
30
|
+
` ID: ${job.id}`,
|
|
30
31
|
` Syntax: ${job.syntax}`,
|
|
31
32
|
` Expression: ${job.expression}`,
|
|
32
33
|
` Schedule: ${describeSchedule(job)}${job.timezone ? ` (${job.timezone})` : ''}`,
|
|
@@ -62,7 +63,7 @@ export async function executeScheduleList(
|
|
|
62
63
|
for (const job of jobs) {
|
|
63
64
|
const status = job.enabled ? 'enabled' : 'disabled';
|
|
64
65
|
const next = job.enabled ? formatLocalDate(job.nextRunAt) : 'n/a';
|
|
65
|
-
lines.push(` - [${status}] ${job.name} ([${job.syntax}] ${describeSchedule(job)}) — next: ${next}`);
|
|
66
|
+
lines.push(` - [${status}] ${job.name} (id: ${job.id}) ([${job.syntax}] ${describeSchedule(job)}) — next: ${next}`);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
return { content: lines.join('\n'), isError: false };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
|
|
4
|
+
import { pathExists, ensureDir } from '../../../util/fs.js';
|
|
4
5
|
import type { PathResult, PathFailureReason } from './path-policy.js';
|
|
5
6
|
import { checkFileSizeOnDisk, checkContentSize } from './size-guard.js';
|
|
6
7
|
import { applyEdit } from './edit-engine.js';
|
|
@@ -55,7 +56,7 @@ export class FileSystemOps {
|
|
|
55
56
|
}
|
|
56
57
|
const filePath = pathCheck.resolved;
|
|
57
58
|
|
|
58
|
-
if (!
|
|
59
|
+
if (!pathExists(filePath)) {
|
|
59
60
|
return { ok: false, error: Err.notFound(filePath) };
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -108,13 +109,10 @@ export class FileSystemOps {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
try {
|
|
111
|
-
|
|
112
|
-
if (!existsSync(dir)) {
|
|
113
|
-
mkdirSync(dir, { recursive: true });
|
|
114
|
-
}
|
|
112
|
+
ensureDir(dirname(filePath));
|
|
115
113
|
|
|
116
114
|
let oldContent = '';
|
|
117
|
-
const isNewFile = !
|
|
115
|
+
const isNewFile = !pathExists(filePath);
|
|
118
116
|
if (!isNewFile) {
|
|
119
117
|
try {
|
|
120
118
|
oldContent = readFileSync(filePath, 'utf-8');
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ToolExecutionResult, ToolContext, ExecutionTarget } from '../types.js';
|
|
2
2
|
import type { SkillToolScript } from './script-contract.js';
|
|
3
|
-
import { join, resolve } from 'node:path';
|
|
3
|
+
import { basename, join, resolve } from 'node:path';
|
|
4
4
|
import { runSkillToolScriptSandbox } from './sandbox-runner.js';
|
|
5
5
|
import { computeSkillVersionHash } from '../../skills/version-hash.js';
|
|
6
|
+
import { bundledToolRegistry } from '../../config/bundled-tool-registry.js';
|
|
6
7
|
|
|
7
8
|
export interface RunSkillToolScriptOptions {
|
|
8
9
|
/** Where to execute: 'host' runs in-process, 'sandbox' runs in an isolated subprocess. */
|
|
@@ -17,6 +18,10 @@ export interface RunSkillToolScriptOptions {
|
|
|
17
18
|
* to `computeSkillVersionHash` from the version-hash module. Provided as an
|
|
18
19
|
* option to support testing and custom resolution strategies. */
|
|
19
20
|
skillDirHashResolver?: (skillDir: string) => string;
|
|
21
|
+
/** Whether this is a bundled (first-party) skill. When true and running inside
|
|
22
|
+
* a compiled binary, the runner uses the pre-imported bundled tool registry
|
|
23
|
+
* instead of a dynamic filesystem import. */
|
|
24
|
+
bundled?: boolean;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
/**
|
|
@@ -65,20 +70,30 @@ export async function runSkillToolScript(
|
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// For bundled skills, use the pre-imported registry instead of dynamic import.
|
|
74
|
+
// In compiled binaries the scripts' relative imports (e.g. ../../../../tools/...)
|
|
75
|
+
// can't resolve because those modules are inside the virtual /$bunfs/ filesystem.
|
|
76
|
+
let module: SkillToolScript | undefined;
|
|
77
|
+
if (options?.bundled) {
|
|
78
|
+
const registryKey = `${basename(skillDir)}:${executorPath}`;
|
|
79
|
+
module = bundledToolRegistry.get(registryKey);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!module) {
|
|
83
|
+
try {
|
|
84
|
+
module = await import(scriptPath);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
87
|
+
return { content: `Failed to load skill tool script "${executorPath}": ${message}`, isError: true };
|
|
88
|
+
}
|
|
74
89
|
}
|
|
75
90
|
|
|
76
|
-
if (typeof module
|
|
91
|
+
if (typeof module!.run !== 'function') {
|
|
77
92
|
return { content: `Skill tool script "${executorPath}" does not export a "run" function`, isError: true };
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
try {
|
|
81
|
-
return await module
|
|
96
|
+
return await module!.run(input, context);
|
|
82
97
|
} catch (err) {
|
|
83
98
|
const message = err instanceof Error ? err.message : String(err);
|
|
84
99
|
return { content: `Skill tool script "${executorPath}" threw an error: ${message}`, isError: true };
|
|
@@ -126,7 +126,7 @@ export async function executeTaskListAdd(
|
|
|
126
126
|
if (workItem.notes) {
|
|
127
127
|
lines.push(` Notes: ${workItem.notes}`);
|
|
128
128
|
}
|
|
129
|
-
if (workItem.sortIndex !==
|
|
129
|
+
if (workItem.sortIndex !== undefined) {
|
|
130
130
|
lines.push(` Sort index: ${workItem.sortIndex}`);
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -221,7 +221,7 @@ export async function executeTaskListAdd(
|
|
|
221
221
|
if (workItem.notes) {
|
|
222
222
|
lines.push(` Notes: ${workItem.notes}`);
|
|
223
223
|
}
|
|
224
|
-
if (workItem.sortIndex !==
|
|
224
|
+
if (workItem.sortIndex !== undefined) {
|
|
225
225
|
lines.push(` Sort index: ${workItem.sortIndex}`);
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -8,6 +8,7 @@ import type { ToolDefinition } from '../../providers/types.js';
|
|
|
8
8
|
import { registerTool } from '../registry.js';
|
|
9
9
|
import { getConfig } from '../../config/loader.js';
|
|
10
10
|
import { getLogger } from '../../util/logger.js';
|
|
11
|
+
import { parseJsonSafe } from '../../util/json.js';
|
|
11
12
|
import { wrapCommand } from './sandbox.js';
|
|
12
13
|
import { buildSanitizedEnv } from './safe-env.js';
|
|
13
14
|
|
|
@@ -126,9 +127,7 @@ export class EvaluateTypescriptTool implements Tool {
|
|
|
126
127
|
if (Buffer.byteLength(mockInputJson) > MAX_MOCK_INPUT_BYTES) {
|
|
127
128
|
return { content: `Error: mock_input_json exceeds maximum size of ${MAX_MOCK_INPUT_BYTES} bytes`, isError: true };
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
JSON.parse(mockInputJson);
|
|
131
|
-
} catch {
|
|
130
|
+
if (parseJsonSafe(mockInputJson) === undefined && mockInputJson.trim() !== 'null') {
|
|
132
131
|
return { content: 'Error: mock_input_json must be valid JSON', isError: true };
|
|
133
132
|
}
|
|
134
133
|
|
|
@@ -170,7 +169,7 @@ export class EvaluateTypescriptTool implements Tool {
|
|
|
170
169
|
timeoutSec: number,
|
|
171
170
|
timeoutMs: number,
|
|
172
171
|
maxOutputChars: number,
|
|
173
|
-
|
|
172
|
+
context: ToolContext,
|
|
174
173
|
): Promise<EvalResult> {
|
|
175
174
|
return new Promise<EvalResult>((resolve) => {
|
|
176
175
|
const startTime = Date.now();
|
|
@@ -198,11 +197,24 @@ export class EvaluateTypescriptTool implements Tool {
|
|
|
198
197
|
child.kill('SIGKILL');
|
|
199
198
|
}, timeoutMs);
|
|
200
199
|
|
|
200
|
+
// Cooperative cancellation via AbortSignal
|
|
201
|
+
const onAbort = () => {
|
|
202
|
+
child.kill('SIGKILL');
|
|
203
|
+
};
|
|
204
|
+
if (context.signal) {
|
|
205
|
+
if (context.signal.aborted) {
|
|
206
|
+
child.kill('SIGKILL');
|
|
207
|
+
} else {
|
|
208
|
+
context.signal.addEventListener('abort', onAbort, { once: true });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
201
212
|
child.stdout.on('data', (data: Buffer) => stdoutChunks.push(data));
|
|
202
213
|
child.stderr.on('data', (data: Buffer) => stderrChunks.push(data));
|
|
203
214
|
|
|
204
215
|
child.on('close', (code) => {
|
|
205
216
|
clearTimeout(timer);
|
|
217
|
+
context.signal?.removeEventListener('abort', onAbort);
|
|
206
218
|
const durationMs = Date.now() - startTime;
|
|
207
219
|
|
|
208
220
|
let stdout = Buffer.concat(stdoutChunks).toString();
|
|
@@ -222,14 +234,10 @@ export class EvaluateTypescriptTool implements Tool {
|
|
|
222
234
|
const lineStart = stdout.lastIndexOf('\n', markerIdx) + 1;
|
|
223
235
|
const lineEnd = stdout.indexOf('\n', markerIdx);
|
|
224
236
|
const line = stdout.slice(lineStart, lineEnd === -1 ? undefined : lineEnd);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
// malformed line — continue scanning earlier occurrences
|
|
237
|
+
const parsed = parseJsonSafe<Record<string, unknown>>(line);
|
|
238
|
+
if (parsed && typeof parsed === 'object' && '__eval_result' in parsed) {
|
|
239
|
+
result = parsed.__eval_result;
|
|
240
|
+
break;
|
|
233
241
|
}
|
|
234
242
|
searchFrom = lineStart;
|
|
235
243
|
}
|
|
@@ -256,6 +264,7 @@ export class EvaluateTypescriptTool implements Tool {
|
|
|
256
264
|
|
|
257
265
|
child.on('error', (err) => {
|
|
258
266
|
clearTimeout(timer);
|
|
267
|
+
context.signal?.removeEventListener('abort', onAbort);
|
|
259
268
|
const durationMs = Date.now() - startTime;
|
|
260
269
|
resolve({
|
|
261
270
|
ok: false,
|