@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
|
@@ -9,7 +9,7 @@ const VALID_EXECUTION_TARGETS = new Set(['host', 'sandbox']);
|
|
|
9
9
|
* Throws descriptive errors for any validation failure.
|
|
10
10
|
*/
|
|
11
11
|
export function parseToolManifest(raw: unknown): SkillToolManifest {
|
|
12
|
-
if (raw
|
|
12
|
+
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
13
13
|
throw new Error('TOOLS.json must be a JSON object');
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -54,7 +54,7 @@ export function parseToolManifest(raw: unknown): SkillToolManifest {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function parseToolEntry(raw: unknown, prefix: string): SkillToolEntry {
|
|
57
|
-
if (raw
|
|
57
|
+
if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
58
58
|
throw new Error(`${prefix}: each tool entry must be a JSON object`);
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -97,7 +97,7 @@ function parseToolEntry(raw: unknown, prefix: string): SkillToolEntry {
|
|
|
97
97
|
const risk = entry.risk as SkillToolEntry['risk'];
|
|
98
98
|
|
|
99
99
|
// input_schema
|
|
100
|
-
if (!('input_schema' in entry) || entry.input_schema
|
|
100
|
+
if (!('input_schema' in entry) || entry.input_schema == null || typeof entry.input_schema !== 'object' || Array.isArray(entry.input_schema)) {
|
|
101
101
|
throw new Error(`${prefix}: missing or non-object "input_schema"`);
|
|
102
102
|
}
|
|
103
103
|
const input_schema = entry.input_schema as Record<string, unknown>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { gunzipSync } from 'node:zlib';
|
|
3
4
|
|
|
4
5
|
import type { CatalogEntry } from '../tools/skills/vellum-catalog.js';
|
|
5
6
|
import { getLogger } from '../util/logger.js';
|
|
7
|
+
import { readPlatformToken } from '../util/platform.js';
|
|
6
8
|
|
|
7
9
|
const log = getLogger('vellum-catalog-remote');
|
|
8
10
|
|
|
9
|
-
const
|
|
10
|
-
'https://raw.githubusercontent.com/vellum-ai/vellum-assistant/main/assistant/src/config/vellum-skills';
|
|
11
|
+
const PLATFORM_URL = process.env.VELLUM_ASSISTANT_PLATFORM_URL ?? 'https://assistant.vellum.ai';
|
|
11
12
|
|
|
12
13
|
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
13
14
|
|
|
@@ -43,7 +44,20 @@ function getBundledSkillContent(skillId: string): string | null {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
/**
|
|
47
|
+
/** Build request headers, including platform token when available. */
|
|
48
|
+
function buildPlatformHeaders(): Record<string, string> {
|
|
49
|
+
const headers: Record<string, string> = {};
|
|
50
|
+
const token = readPlatformToken();
|
|
51
|
+
if (token) {
|
|
52
|
+
headers['X-Session-Token'] = token;
|
|
53
|
+
}
|
|
54
|
+
return headers;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fetch catalog entries from the platform API. Falls back to bundled copy.
|
|
59
|
+
* Reads the platform token from ~/.vellum/platform-token automatically.
|
|
60
|
+
*/
|
|
47
61
|
export async function fetchCatalogEntries(): Promise<CatalogEntry[]> {
|
|
48
62
|
const now = Date.now();
|
|
49
63
|
if (cachedEntries && now - cacheTimestamp < CACHE_TTL_MS) {
|
|
@@ -51,8 +65,9 @@ export async function fetchCatalogEntries(): Promise<CatalogEntry[]> {
|
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
try {
|
|
54
|
-
const url = `${
|
|
68
|
+
const url = `${PLATFORM_URL}/v1/skills/`;
|
|
55
69
|
const response = await fetch(url, {
|
|
70
|
+
headers: buildPlatformHeaders(),
|
|
56
71
|
signal: AbortSignal.timeout(5000),
|
|
57
72
|
});
|
|
58
73
|
|
|
@@ -63,14 +78,14 @@ export async function fetchCatalogEntries(): Promise<CatalogEntry[]> {
|
|
|
63
78
|
const manifest: CatalogManifest = await response.json();
|
|
64
79
|
const skills = manifest.skills;
|
|
65
80
|
if (!Array.isArray(skills) || skills.length === 0) {
|
|
66
|
-
throw new Error('
|
|
81
|
+
throw new Error('Platform catalog has invalid or empty skills array');
|
|
67
82
|
}
|
|
68
83
|
cachedEntries = skills;
|
|
69
84
|
cacheTimestamp = now;
|
|
70
|
-
log.info({ count: cachedEntries.length }, 'Fetched
|
|
85
|
+
log.info({ count: cachedEntries.length }, 'Fetched vellum-skills catalog from platform API');
|
|
71
86
|
return cachedEntries;
|
|
72
87
|
} catch (err) {
|
|
73
|
-
log.warn({ err }, 'Failed to fetch
|
|
88
|
+
log.warn({ err }, 'Failed to fetch catalog from platform API, falling back to bundled copy');
|
|
74
89
|
const bundled = loadBundledCatalog();
|
|
75
90
|
// Cache the bundled result too so we don't re-fetch on every call during outage
|
|
76
91
|
cachedEntries = bundled;
|
|
@@ -79,28 +94,72 @@ export async function fetchCatalogEntries(): Promise<CatalogEntry[]> {
|
|
|
79
94
|
}
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Extract SKILL.md content from a tar archive (uncompressed).
|
|
99
|
+
* Tar format: 512-byte header blocks followed by file data blocks.
|
|
100
|
+
*/
|
|
101
|
+
function extractSkillMdFromTar(tarBuffer: Buffer): string | null {
|
|
102
|
+
let offset = 0;
|
|
103
|
+
while (offset + 512 <= tarBuffer.length) {
|
|
104
|
+
const header = tarBuffer.subarray(offset, offset + 512);
|
|
105
|
+
|
|
106
|
+
// Check for end-of-archive (two consecutive zero blocks)
|
|
107
|
+
if (header.every((b) => b === 0)) break;
|
|
108
|
+
|
|
109
|
+
// Extract filename (bytes 0-99, null-terminated)
|
|
110
|
+
const nameEnd = header.indexOf(0, 0);
|
|
111
|
+
const name = header.subarray(0, Math.min(nameEnd >= 0 ? nameEnd : 100, 100)).toString('utf-8');
|
|
112
|
+
|
|
113
|
+
// Extract file size (bytes 124-135, octal string)
|
|
114
|
+
const sizeStr = header.subarray(124, 136).toString('utf-8').trim();
|
|
115
|
+
const size = parseInt(sizeStr, 8) || 0;
|
|
116
|
+
|
|
117
|
+
offset += 512; // move past header
|
|
118
|
+
|
|
119
|
+
if (name.endsWith('SKILL.md') || name === 'SKILL.md') {
|
|
120
|
+
return tarBuffer.subarray(offset, offset + size).toString('utf-8');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Skip to next header (data blocks are padded to 512 bytes)
|
|
124
|
+
offset += Math.ceil(size / 512) * 512;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Fetch a skill's SKILL.md content from the platform tar API.
|
|
131
|
+
* GET /v1/skills/{skill_id}/ returns a tar.gz archive containing all skill files.
|
|
132
|
+
* Falls back to bundled copy on failure.
|
|
133
|
+
*/
|
|
83
134
|
export async function fetchSkillContent(skillId: string): Promise<string | null> {
|
|
84
135
|
try {
|
|
85
|
-
const url = `${
|
|
136
|
+
const url = `${PLATFORM_URL}/v1/skills/${encodeURIComponent(skillId)}/`;
|
|
86
137
|
const response = await fetch(url, {
|
|
87
|
-
|
|
138
|
+
headers: buildPlatformHeaders(),
|
|
139
|
+
signal: AbortSignal.timeout(15000),
|
|
88
140
|
});
|
|
89
141
|
|
|
90
142
|
if (!response.ok) {
|
|
91
143
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
92
144
|
}
|
|
93
145
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
146
|
+
const gzipBuffer = Buffer.from(await response.arrayBuffer());
|
|
147
|
+
const tarBuffer = gunzipSync(gzipBuffer);
|
|
148
|
+
const skillMd = extractSkillMdFromTar(tarBuffer);
|
|
149
|
+
|
|
150
|
+
if (skillMd) {
|
|
151
|
+
return skillMd;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
log.warn({ skillId }, 'SKILL.md not found in platform tar archive, falling back to bundled');
|
|
97
155
|
} catch (err) {
|
|
98
|
-
log.warn({ err, skillId }, 'Failed to fetch
|
|
99
|
-
return getBundledSkillContent(skillId);
|
|
156
|
+
log.warn({ err, skillId }, 'Failed to fetch skill content from platform API, falling back to bundled');
|
|
100
157
|
}
|
|
158
|
+
|
|
159
|
+
return getBundledSkillContent(skillId);
|
|
101
160
|
}
|
|
102
161
|
|
|
103
|
-
/** Check if a skill ID exists in the
|
|
162
|
+
/** Check if a skill ID exists in the catalog. */
|
|
104
163
|
export async function checkVellumSkill(skillId: string): Promise<boolean> {
|
|
105
164
|
const entries = await fetchCatalogEntries();
|
|
106
165
|
return entries.some((e) => e.id === skillId);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getLogger } from '../util/logger.js';
|
|
2
|
+
import { ProviderError } from '../util/errors.js';
|
|
2
3
|
|
|
3
4
|
const log = getLogger('slack-webhook');
|
|
4
5
|
|
|
@@ -56,6 +57,6 @@ export async function postToSlackWebhook(
|
|
|
56
57
|
|
|
57
58
|
if (!response.ok) {
|
|
58
59
|
const body = await response.text();
|
|
59
|
-
throw new
|
|
60
|
+
throw new ProviderError(`Slack webhook returned ${response.status}: ${body}`, 'slack', response.status);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -43,6 +43,12 @@ export async function executeSwarm(opts: ExecuteSwarmOptions): Promise<SwarmExec
|
|
|
43
43
|
const { plan, limits, backend, workingDir, model, onStatus, signal } = opts;
|
|
44
44
|
const startTime = Date.now();
|
|
45
45
|
|
|
46
|
+
// Safety net: reject cyclic plans even if the caller skipped validation
|
|
47
|
+
const cyclePath = detectCycle(plan.tasks);
|
|
48
|
+
if (cyclePath) {
|
|
49
|
+
throw new Error(`Swarm plan contains a dependency cycle: ${cyclePath.join(' -> ')}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
onStatus?.({ kind: 'plan_created', message: `Plan with ${plan.tasks.length} tasks` });
|
|
47
53
|
|
|
48
54
|
const results = new Map<string, SwarmTaskResult>();
|
|
@@ -128,7 +134,7 @@ export async function executeSwarm(opts: ExecuteSwarmOptions): Promise<SwarmExec
|
|
|
128
134
|
const r = results.get(depId);
|
|
129
135
|
return r ? { taskId: depId, summary: r.summary } : null;
|
|
130
136
|
})
|
|
131
|
-
.filter((d): d is { taskId: string; summary: string } => d
|
|
137
|
+
.filter((d): d is { taskId: string; summary: string } => d != null);
|
|
132
138
|
|
|
133
139
|
let result = await runWorkerTask({
|
|
134
140
|
task,
|
|
@@ -144,6 +150,11 @@ export async function executeSwarm(opts: ExecuteSwarmOptions): Promise<SwarmExec
|
|
|
144
150
|
let retries = 0;
|
|
145
151
|
while (result.status === 'failed' && retries < limits.maxRetriesPerTask && !signal?.aborted) {
|
|
146
152
|
retries++;
|
|
153
|
+
// Exponential backoff with ±25% jitter to prevent thundering herd
|
|
154
|
+
const baseDelayMs = 1000 * Math.pow(2, retries - 1);
|
|
155
|
+
const jitter = baseDelayMs * 0.25 * (2 * Math.random() - 1);
|
|
156
|
+
await abortableSleep(baseDelayMs + jitter, signal);
|
|
157
|
+
if (signal?.aborted) break;
|
|
147
158
|
result = await runWorkerTask({
|
|
148
159
|
task,
|
|
149
160
|
upstreamContext: plan.objective,
|
|
@@ -253,6 +264,86 @@ function blockDependents(
|
|
|
253
264
|
}
|
|
254
265
|
}
|
|
255
266
|
|
|
267
|
+
/**
|
|
268
|
+
* DFS-based cycle detection. Returns the cycle path (e.g. ['a', 'b', 'c', 'a'])
|
|
269
|
+
* if a cycle exists, or null if the graph is a valid DAG.
|
|
270
|
+
*/
|
|
271
|
+
function detectCycle(tasks: SwarmTaskNode[]): string[] | null {
|
|
272
|
+
const adj = new Map<string, string[]>();
|
|
273
|
+
for (const t of tasks) {
|
|
274
|
+
adj.set(t.id, []);
|
|
275
|
+
}
|
|
276
|
+
for (const t of tasks) {
|
|
277
|
+
for (const dep of t.dependencies) {
|
|
278
|
+
// Edge from dep -> t.id (dep must finish before t)
|
|
279
|
+
adj.get(dep)?.push(t.id);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
284
|
+
const color = new Map<string, number>();
|
|
285
|
+
for (const t of tasks) color.set(t.id, WHITE);
|
|
286
|
+
|
|
287
|
+
const parent = new Map<string, string>();
|
|
288
|
+
|
|
289
|
+
// Iterative DFS to avoid stack overflow on deep acyclic chains
|
|
290
|
+
for (const t of tasks) {
|
|
291
|
+
if (color.get(t.id) !== WHITE) continue;
|
|
292
|
+
|
|
293
|
+
const stack: Array<{ node: string; neighborIdx: number }> = [
|
|
294
|
+
{ node: t.id, neighborIdx: 0 },
|
|
295
|
+
];
|
|
296
|
+
color.set(t.id, GRAY);
|
|
297
|
+
|
|
298
|
+
while (stack.length > 0) {
|
|
299
|
+
const frame = stack[stack.length - 1];
|
|
300
|
+
const neighbors = adj.get(frame.node) ?? [];
|
|
301
|
+
|
|
302
|
+
if (frame.neighborIdx >= neighbors.length) {
|
|
303
|
+
// All neighbors visited — mark node as fully processed
|
|
304
|
+
color.set(frame.node, BLACK);
|
|
305
|
+
stack.pop();
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const neighbor = neighbors[frame.neighborIdx];
|
|
310
|
+
frame.neighborIdx++;
|
|
311
|
+
|
|
312
|
+
if (color.get(neighbor) === GRAY) {
|
|
313
|
+
// Back edge found — reconstruct cycle path
|
|
314
|
+
const cycle = [neighbor];
|
|
315
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
316
|
+
cycle.push(stack[i].node);
|
|
317
|
+
if (stack[i].node === neighbor) break;
|
|
318
|
+
}
|
|
319
|
+
cycle.reverse();
|
|
320
|
+
return cycle;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (color.get(neighbor) === WHITE) {
|
|
324
|
+
parent.set(neighbor, frame.node);
|
|
325
|
+
color.set(neighbor, GRAY);
|
|
326
|
+
stack.push({ node: neighbor, neighborIdx: 0 });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Resolves after `ms` milliseconds, or immediately if the signal fires first. */
|
|
334
|
+
function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
335
|
+
if (signal?.aborted) return Promise.resolve();
|
|
336
|
+
return new Promise<void>((resolve) => {
|
|
337
|
+
const timer = setTimeout(done, ms);
|
|
338
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
339
|
+
function done() {
|
|
340
|
+
clearTimeout(timer);
|
|
341
|
+
signal?.removeEventListener('abort', done);
|
|
342
|
+
resolve();
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
256
347
|
function buildMarkdownFallback(objective: string, results: SwarmTaskResult[]): string {
|
|
257
348
|
const lines: string[] = [`## Swarm Results: ${objective}`, ''];
|
|
258
349
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Provider, Message } from '../providers/types.js';
|
|
2
|
+
import { parseJsonSafe } from '../util/json.js';
|
|
2
3
|
import type { SwarmPlan, SwarmTaskNode } from './types.js';
|
|
3
4
|
import type { SwarmLimits } from './limits.js';
|
|
4
5
|
import { validateAndNormalizePlan } from './plan-validator.js';
|
|
@@ -61,7 +62,7 @@ export function parsePlanJSON(raw: string): { tasks: Array<{ id: string; role: s
|
|
|
61
62
|
// Try all fenced code blocks — LLMs sometimes emit non-JSON blocks before the plan
|
|
62
63
|
const fencedRegex = /```(?:json)?\s*\n?([\s\S]*?)\n?```/g;
|
|
63
64
|
let match;
|
|
64
|
-
while ((match = fencedRegex.exec(raw))
|
|
65
|
+
while ((match = fencedRegex.exec(raw)) != null) {
|
|
65
66
|
const result = tryParsePlan(match[1]);
|
|
66
67
|
if (result) return result;
|
|
67
68
|
}
|
|
@@ -71,15 +72,11 @@ export function parsePlanJSON(raw: string): { tasks: Array<{ id: string; role: s
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
function tryParsePlan(jsonStr: string): { tasks: Array<{ id: string; role: string; objective: string; dependencies: string[] }> } | null {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return parsed;
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
} catch {
|
|
81
|
-
return null;
|
|
75
|
+
const parsed = parseJsonSafe<{ tasks?: unknown }>(jsonStr.trim());
|
|
76
|
+
if (parsed && Array.isArray(parsed.tasks)) {
|
|
77
|
+
return parsed as { tasks: Array<{ id: string; role: string; objective: string; dependencies: string[] }> };
|
|
82
78
|
}
|
|
79
|
+
return null;
|
|
83
80
|
}
|
|
84
81
|
|
|
85
82
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SwarmRole, SwarmTaskResult } from './types.js';
|
|
2
2
|
import { truncate } from '../util/truncate.js';
|
|
3
|
+
import { parseJsonSafe } from '../util/json.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Build a role-specific worker prompt for a swarm task.
|
|
@@ -56,18 +57,14 @@ export function parseWorkerOutput(raw: string): Pick<SwarmTaskResult, 'summary'
|
|
|
56
57
|
|
|
57
58
|
// Walk backwards to prefer the final valid contract block.
|
|
58
59
|
for (let i = jsonBlocks.length - 1; i >= 0; i--) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
68
|
-
} catch {
|
|
69
|
-
// Malformed JSON — try the next block up.
|
|
70
|
-
}
|
|
60
|
+
const parsed = parseJsonSafe<Record<string, unknown>>(jsonBlocks[i][1]);
|
|
61
|
+
if (!parsed || typeof parsed.summary !== 'string') continue;
|
|
62
|
+
return {
|
|
63
|
+
summary: parsed.summary,
|
|
64
|
+
artifacts: Array.isArray(parsed.artifacts) ? parsed.artifacts : [],
|
|
65
|
+
issues: Array.isArray(parsed.issues) ? parsed.issues : [],
|
|
66
|
+
nextSteps: Array.isArray(parsed.nextSteps) ? parsed.nextSteps : [],
|
|
67
|
+
};
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
return {
|
|
@@ -4,6 +4,7 @@ import { messages as messagesTable } from '../memory/schema.js';
|
|
|
4
4
|
import { createTask } from './task-store.js';
|
|
5
5
|
import type { Task } from './task-store.js';
|
|
6
6
|
import { truncate } from '../util/truncate.js';
|
|
7
|
+
import { parseJsonSafe } from '../util/json.js';
|
|
7
8
|
import { sanitizeToolList } from './tool-sanitizer.js';
|
|
8
9
|
|
|
9
10
|
/** Output schema for the task compiler. */
|
|
@@ -91,20 +92,14 @@ export function saveCompiledTask(compiled: CompiledTask, conversationId: string)
|
|
|
91
92
|
* string or a JSON array of Anthropic content blocks.
|
|
92
93
|
*/
|
|
93
94
|
function extractTextContent(content: string): string {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.join('\n');
|
|
101
|
-
}
|
|
102
|
-
// If it parsed but isn't an array, treat as plain text
|
|
103
|
-
return content;
|
|
104
|
-
} catch {
|
|
105
|
-
// Not JSON — it's a plain text message
|
|
106
|
-
return content;
|
|
95
|
+
const parsed = parseJsonSafe(content);
|
|
96
|
+
if (Array.isArray(parsed)) {
|
|
97
|
+
return parsed
|
|
98
|
+
.filter((block: Record<string, unknown>) => block.type === 'text')
|
|
99
|
+
.map((block: Record<string, unknown>) => block.text as string)
|
|
100
|
+
.join('\n');
|
|
107
101
|
}
|
|
102
|
+
return content;
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
/**
|
|
@@ -118,22 +113,18 @@ function extractToolNames(
|
|
|
118
113
|
for (const msg of msgs) {
|
|
119
114
|
if (msg.role !== 'assistant') continue;
|
|
120
115
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
132
|
-
tools.add(block.name);
|
|
133
|
-
}
|
|
116
|
+
const parsed = parseJsonSafe(msg.content);
|
|
117
|
+
if (!Array.isArray(parsed)) continue;
|
|
118
|
+
|
|
119
|
+
for (const block of parsed) {
|
|
120
|
+
if (
|
|
121
|
+
block &&
|
|
122
|
+
typeof block === 'object' &&
|
|
123
|
+
block.type === 'tool_use' &&
|
|
124
|
+
typeof block.name === 'string'
|
|
125
|
+
) {
|
|
126
|
+
tools.add(block.name);
|
|
134
127
|
}
|
|
135
|
-
} catch {
|
|
136
|
-
// Not JSON content — skip
|
|
137
128
|
}
|
|
138
129
|
}
|
|
139
130
|
|
package/src/tasks/task-runner.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface TaskRunResult {
|
|
|
25
25
|
export function renderTemplate(template: string, inputs: Record<string, string>): string {
|
|
26
26
|
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
27
27
|
if (key in inputs) return inputs[key];
|
|
28
|
-
return
|
|
28
|
+
return `<MISSING: ${key}>`;
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -12,7 +12,7 @@ import { RiskLevel } from '../../permissions/types.js';
|
|
|
12
12
|
import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
|
|
13
13
|
import type { ToolDefinition } from '../../providers/types.js';
|
|
14
14
|
import { registerTool } from '../registry.js';
|
|
15
|
-
import { getDb } from '../../memory/db.js';
|
|
15
|
+
import { getDb, rawAll } from '../../memory/db.js';
|
|
16
16
|
import { attachments, messageAttachments, messages, conversations } from '../../memory/schema.js';
|
|
17
17
|
import type { StoredAttachment } from '../../memory/attachments-store.js';
|
|
18
18
|
import { isAttachmentVisible, type AttachmentContext } from '../../daemon/media-visibility-policy.js';
|
|
@@ -151,17 +151,16 @@ export function searchAttachments(params: AssetSearchParams): StoredAttachment[]
|
|
|
151
151
|
.where(eq(messages.conversationId, params.conversation_id))
|
|
152
152
|
.all()
|
|
153
153
|
.map((r) => r.attachmentId)
|
|
154
|
-
.filter((id): id is string => id !==
|
|
154
|
+
.filter((id): id is string => id !== undefined);
|
|
155
155
|
|
|
156
156
|
if (linkedIds.length === 0) {
|
|
157
157
|
return [];
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
// Use raw SQL IN clause for the attachment ID set
|
|
161
|
-
const raw = (db as unknown as { $client: import('bun:sqlite').Database }).$client;
|
|
162
160
|
const placeholders = linkedIds.map(() => '?').join(', ');
|
|
163
161
|
|
|
164
|
-
// Build WHERE clauses for raw query
|
|
162
|
+
// Build WHERE clauses for raw query (FTS5 virtual table not involved,
|
|
163
|
+
// but dynamic IN-list with optional filters is simpler in raw SQL)
|
|
165
164
|
const whereParts: string[] = [`a.id IN (${placeholders})`];
|
|
166
165
|
const bindValues: (string | number)[] = [...linkedIds];
|
|
167
166
|
|
|
@@ -182,15 +181,8 @@ export function searchAttachments(params: AssetSearchParams): StoredAttachment[]
|
|
|
182
181
|
}
|
|
183
182
|
}
|
|
184
183
|
const limit = Math.min(params.limit ?? DEFAULT_LIMIT, MAX_RESULTS);
|
|
185
|
-
const stmt = raw.prepare(
|
|
186
|
-
`SELECT a.id, a.original_filename, a.mime_type, a.size_bytes, a.kind, a.thumbnail_base64, a.created_at
|
|
187
|
-
FROM attachments a
|
|
188
|
-
WHERE ${whereParts.join(' AND ')}
|
|
189
|
-
ORDER BY a.created_at DESC
|
|
190
|
-
LIMIT ?`,
|
|
191
|
-
);
|
|
192
184
|
|
|
193
|
-
|
|
185
|
+
interface AttachmentRow {
|
|
194
186
|
id: string;
|
|
195
187
|
original_filename: string;
|
|
196
188
|
mime_type: string;
|
|
@@ -198,7 +190,16 @@ export function searchAttachments(params: AssetSearchParams): StoredAttachment[]
|
|
|
198
190
|
kind: string;
|
|
199
191
|
thumbnail_base64: string | null;
|
|
200
192
|
created_at: number;
|
|
201
|
-
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const rows = rawAll<AttachmentRow>(
|
|
196
|
+
`SELECT a.id, a.original_filename, a.mime_type, a.size_bytes, a.kind, a.thumbnail_base64, a.created_at
|
|
197
|
+
FROM attachments a
|
|
198
|
+
WHERE ${whereParts.join(' AND ')}
|
|
199
|
+
ORDER BY a.created_at DESC
|
|
200
|
+
LIMIT ?`,
|
|
201
|
+
...bindValues, limit,
|
|
202
|
+
);
|
|
202
203
|
|
|
203
204
|
return rows.map((r) => ({
|
|
204
205
|
id: r.id,
|
|
@@ -93,6 +93,10 @@ export async function executeBrowserNavigate(
|
|
|
93
93
|
input: Record<string, unknown>,
|
|
94
94
|
context: ToolContext,
|
|
95
95
|
): Promise<ToolExecutionResult> {
|
|
96
|
+
if (context.signal?.aborted) {
|
|
97
|
+
return { content: 'Error: operation was cancelled', isError: true };
|
|
98
|
+
}
|
|
99
|
+
|
|
96
100
|
const parsedUrl = parseUrl(input.url);
|
|
97
101
|
if (!parsedUrl) {
|
|
98
102
|
return { content: 'Error: url is required and must be a valid HTTP(S) URL', isError: true };
|
|
@@ -316,6 +320,10 @@ export async function executeBrowserNavigate(
|
|
|
316
320
|
if (challenge?.type === 'captcha') {
|
|
317
321
|
log.info('CAPTCHA detected, waiting up to 5s for auto-resolve');
|
|
318
322
|
for (let i = 0; i < 5; i++) {
|
|
323
|
+
if (context.signal?.aborted) {
|
|
324
|
+
if (sender) updateBrowserStatus(context.sessionId, sender, 'idle');
|
|
325
|
+
return { content: 'Navigation cancelled.', isError: true };
|
|
326
|
+
}
|
|
319
327
|
await new Promise((r) => setTimeout(r, 1000));
|
|
320
328
|
const still = await detectCaptchaChallenge(page);
|
|
321
329
|
if (!still) {
|
|
@@ -797,11 +805,15 @@ export async function executeBrowserWaitFor(
|
|
|
797
805
|
input: Record<string, unknown>,
|
|
798
806
|
context: ToolContext,
|
|
799
807
|
): Promise<ToolExecutionResult> {
|
|
808
|
+
if (context.signal?.aborted) {
|
|
809
|
+
return { content: 'Error: operation was cancelled', isError: true };
|
|
810
|
+
}
|
|
811
|
+
|
|
800
812
|
const selector = typeof input.selector === 'string' && input.selector ? input.selector : null;
|
|
801
813
|
const text = typeof input.text === 'string' && input.text ? input.text : null;
|
|
802
814
|
const duration = typeof input.duration === 'number' ? input.duration : null;
|
|
803
815
|
|
|
804
|
-
const modeCount = [selector, text, duration].filter((v) => v
|
|
816
|
+
const modeCount = [selector, text, duration].filter((v) => v != null).length;
|
|
805
817
|
if (modeCount === 0) {
|
|
806
818
|
return { content: 'Error: Exactly one of selector, text, or duration is required.', isError: true };
|
|
807
819
|
}
|