@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
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timeline generation service.
|
|
3
|
-
*
|
|
4
|
-
* Aggregates sequential vision outputs into coherent timeline segments.
|
|
5
|
-
* Each segment groups adjacent keyframes that share similar scene characteristics
|
|
6
|
-
* into a single time range with merged attributes.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
getMediaAssetById,
|
|
11
|
-
getKeyframesForAsset,
|
|
12
|
-
getVisionOutputsForAsset,
|
|
13
|
-
deleteTimelineForAsset,
|
|
14
|
-
insertTimelineSegmentsBatch,
|
|
15
|
-
createProcessingStage,
|
|
16
|
-
updateProcessingStage,
|
|
17
|
-
getProcessingStagesForAsset,
|
|
18
|
-
type MediaVisionOutput,
|
|
19
|
-
type MediaKeyframe,
|
|
20
|
-
type MediaTimeline,
|
|
21
|
-
} from '../../../../memory/media-store.js';
|
|
22
|
-
|
|
23
|
-
export interface TimelineGenerationResult {
|
|
24
|
-
assetId: string;
|
|
25
|
-
segmentCount: number;
|
|
26
|
-
segments: MediaTimeline[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Generate a timeline for a media asset from its vision analysis outputs.
|
|
31
|
-
*
|
|
32
|
-
* Groups consecutive keyframes with similar scene descriptions into segments.
|
|
33
|
-
* If a timeline already exists for this asset, it is replaced.
|
|
34
|
-
*/
|
|
35
|
-
export function generateTimeline(
|
|
36
|
-
assetId: string,
|
|
37
|
-
options?: {
|
|
38
|
-
analysisType?: string;
|
|
39
|
-
onProgress?: (message: string) => void;
|
|
40
|
-
},
|
|
41
|
-
): TimelineGenerationResult {
|
|
42
|
-
const analysisType = options?.analysisType ?? 'scene_description';
|
|
43
|
-
const onProgress = options?.onProgress;
|
|
44
|
-
|
|
45
|
-
const asset = getMediaAssetById(assetId);
|
|
46
|
-
if (!asset) {
|
|
47
|
-
throw new Error(`Media asset not found: ${assetId}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const keyframes = getKeyframesForAsset(assetId);
|
|
51
|
-
if (keyframes.length === 0) {
|
|
52
|
-
throw new Error('No keyframes found for this asset. Run extract_keyframes first.');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const visionOutputs = getVisionOutputsForAsset(assetId, analysisType);
|
|
56
|
-
if (visionOutputs.length === 0) {
|
|
57
|
-
throw new Error(`No vision outputs found for analysis type "${analysisType}". Run analyze_keyframes first.`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Find or create the timeline_generation processing stage
|
|
61
|
-
const existingStages = getProcessingStagesForAsset(assetId);
|
|
62
|
-
let stage = existingStages.find((s) => s.stage === 'timeline_generation');
|
|
63
|
-
if (!stage) {
|
|
64
|
-
stage = createProcessingStage({ assetId, stage: 'timeline_generation' });
|
|
65
|
-
}
|
|
66
|
-
updateProcessingStage(stage.id, { status: 'running', startedAt: Date.now() });
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
// Build a map of keyframeId -> keyframe for timestamp lookup
|
|
70
|
-
const keyframeMap = new Map<string, MediaKeyframe>();
|
|
71
|
-
for (const kf of keyframes) {
|
|
72
|
-
keyframeMap.set(kf.id, kf);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Build a map of keyframeId -> vision output
|
|
76
|
-
const outputByKeyframe = new Map<string, MediaVisionOutput>();
|
|
77
|
-
for (const vo of visionOutputs) {
|
|
78
|
-
outputByKeyframe.set(vo.keyframeId, vo);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Sort keyframes by timestamp to ensure sequential processing
|
|
82
|
-
const sortedKeyframes = [...keyframes]
|
|
83
|
-
.filter((kf) => outputByKeyframe.has(kf.id))
|
|
84
|
-
.sort((a, b) => a.timestamp - b.timestamp);
|
|
85
|
-
|
|
86
|
-
if (sortedKeyframes.length === 0) {
|
|
87
|
-
updateProcessingStage(stage.id, {
|
|
88
|
-
status: 'completed',
|
|
89
|
-
progress: 100,
|
|
90
|
-
completedAt: Date.now(),
|
|
91
|
-
});
|
|
92
|
-
return { assetId, segmentCount: 0, segments: [] };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
onProgress?.('Aggregating vision outputs into timeline segments...');
|
|
96
|
-
|
|
97
|
-
// Aggregate consecutive frames into segments based on scene similarity
|
|
98
|
-
const segmentRows: Array<{
|
|
99
|
-
assetId: string;
|
|
100
|
-
startTime: number;
|
|
101
|
-
endTime: number;
|
|
102
|
-
segmentType: string;
|
|
103
|
-
attributes: Record<string, unknown>;
|
|
104
|
-
confidence: number;
|
|
105
|
-
}> = [];
|
|
106
|
-
|
|
107
|
-
let currentSegment = createSegmentFromOutput(
|
|
108
|
-
assetId,
|
|
109
|
-
sortedKeyframes[0],
|
|
110
|
-
outputByKeyframe.get(sortedKeyframes[0].id)!,
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
for (let i = 1; i < sortedKeyframes.length; i++) {
|
|
114
|
-
const kf = sortedKeyframes[i];
|
|
115
|
-
const vo = outputByKeyframe.get(kf.id)!;
|
|
116
|
-
|
|
117
|
-
if (shouldMergeIntoSegment(currentSegment, vo)) {
|
|
118
|
-
// Extend the current segment
|
|
119
|
-
currentSegment.endTime = kf.timestamp;
|
|
120
|
-
const newConfidence = vo.confidence ?? 0.5;
|
|
121
|
-
currentSegment.confidence =
|
|
122
|
-
(currentSegment.confidence * currentSegment.frameCount + newConfidence) / (currentSegment.frameCount + 1);
|
|
123
|
-
currentSegment.frameCount++;
|
|
124
|
-
mergeSubjects(currentSegment.attributes, vo.output);
|
|
125
|
-
mergeActions(currentSegment.attributes, vo.output);
|
|
126
|
-
} else {
|
|
127
|
-
// Finalize current segment and start a new one
|
|
128
|
-
segmentRows.push(currentSegment);
|
|
129
|
-
currentSegment = createSegmentFromOutput(assetId, kf, vo);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Update progress
|
|
133
|
-
const progress = Math.round((i / sortedKeyframes.length) * 100);
|
|
134
|
-
updateProcessingStage(stage.id, { progress });
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Don't forget the last segment
|
|
138
|
-
segmentRows.push(currentSegment);
|
|
139
|
-
|
|
140
|
-
// Clear existing timeline and insert new segments
|
|
141
|
-
deleteTimelineForAsset(assetId);
|
|
142
|
-
const segments = insertTimelineSegmentsBatch(segmentRows);
|
|
143
|
-
|
|
144
|
-
updateProcessingStage(stage.id, {
|
|
145
|
-
status: 'completed',
|
|
146
|
-
progress: 100,
|
|
147
|
-
completedAt: Date.now(),
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
onProgress?.(`Generated ${segments.length} timeline segments.`);
|
|
151
|
-
|
|
152
|
-
return { assetId, segmentCount: segments.length, segments };
|
|
153
|
-
} catch (err) {
|
|
154
|
-
updateProcessingStage(stage.id, {
|
|
155
|
-
status: 'failed',
|
|
156
|
-
lastError: (err as Error).message.slice(0, 500),
|
|
157
|
-
});
|
|
158
|
-
throw err;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// ---------------------------------------------------------------------------
|
|
163
|
-
// Internal helpers
|
|
164
|
-
// ---------------------------------------------------------------------------
|
|
165
|
-
|
|
166
|
-
interface PendingSegment {
|
|
167
|
-
assetId: string;
|
|
168
|
-
startTime: number;
|
|
169
|
-
endTime: number;
|
|
170
|
-
segmentType: string;
|
|
171
|
-
attributes: Record<string, unknown>;
|
|
172
|
-
confidence: number;
|
|
173
|
-
frameCount: number;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function createSegmentFromOutput(
|
|
177
|
-
assetId: string,
|
|
178
|
-
keyframe: MediaKeyframe,
|
|
179
|
-
vo: MediaVisionOutput,
|
|
180
|
-
): PendingSegment {
|
|
181
|
-
const sceneDescription = (vo.output.sceneDescription as string) ?? '';
|
|
182
|
-
const segmentType = deriveSegmentType(vo.output);
|
|
183
|
-
return {
|
|
184
|
-
assetId,
|
|
185
|
-
startTime: keyframe.timestamp,
|
|
186
|
-
endTime: keyframe.timestamp,
|
|
187
|
-
segmentType,
|
|
188
|
-
attributes: {
|
|
189
|
-
sceneDescription,
|
|
190
|
-
subjects: Array.isArray(vo.output.subjects) ? [...(vo.output.subjects as string[])] : [],
|
|
191
|
-
actions: Array.isArray(vo.output.actions) ? [...(vo.output.actions as string[])] : [],
|
|
192
|
-
context: (vo.output.context as string) ?? '',
|
|
193
|
-
},
|
|
194
|
-
confidence: vo.confidence ?? 0.5,
|
|
195
|
-
frameCount: 1,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Derive a generic segment type from vision output.
|
|
201
|
-
* Uses simple heuristics on the scene description — domain-specific
|
|
202
|
-
* interpretation belongs in the VLM prompt, not here.
|
|
203
|
-
*/
|
|
204
|
-
function deriveSegmentType(output: Record<string, unknown>): string {
|
|
205
|
-
const actions = output.actions as string[] | undefined;
|
|
206
|
-
if (actions && actions.length > 0) {
|
|
207
|
-
return 'activity';
|
|
208
|
-
}
|
|
209
|
-
const subjects = output.subjects as string[] | undefined;
|
|
210
|
-
if (subjects && subjects.length > 0) {
|
|
211
|
-
return 'scene';
|
|
212
|
-
}
|
|
213
|
-
return 'static';
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Decide whether a new vision output is similar enough to merge
|
|
218
|
-
* into the current segment.
|
|
219
|
-
*
|
|
220
|
-
* Uses a simple heuristic: same segment type and overlapping subjects.
|
|
221
|
-
*/
|
|
222
|
-
function shouldMergeIntoSegment(
|
|
223
|
-
segment: PendingSegment,
|
|
224
|
-
vo: MediaVisionOutput,
|
|
225
|
-
): boolean {
|
|
226
|
-
const newType = deriveSegmentType(vo.output);
|
|
227
|
-
if (newType !== segment.segmentType) return false;
|
|
228
|
-
|
|
229
|
-
// Check subject overlap
|
|
230
|
-
const existingSubjects = new Set(
|
|
231
|
-
(segment.attributes.subjects as string[]) ?? [],
|
|
232
|
-
);
|
|
233
|
-
const newSubjects = (vo.output.subjects as string[]) ?? [];
|
|
234
|
-
|
|
235
|
-
if (existingSubjects.size === 0 && newSubjects.length === 0) return true;
|
|
236
|
-
if (existingSubjects.size === 0 || newSubjects.length === 0) return false;
|
|
237
|
-
|
|
238
|
-
const overlap = newSubjects.filter((s) => existingSubjects.has(s)).length;
|
|
239
|
-
const unionSize = new Set([...existingSubjects, ...newSubjects]).size;
|
|
240
|
-
|
|
241
|
-
// Merge if at least 30% overlap (Jaccard similarity)
|
|
242
|
-
return unionSize > 0 && overlap / unionSize >= 0.3;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function mergeSubjects(
|
|
246
|
-
attributes: Record<string, unknown>,
|
|
247
|
-
newOutput: Record<string, unknown>,
|
|
248
|
-
): void {
|
|
249
|
-
const existing = new Set((attributes.subjects as string[]) ?? []);
|
|
250
|
-
const incoming = (newOutput.subjects as string[]) ?? [];
|
|
251
|
-
for (const s of incoming) {
|
|
252
|
-
existing.add(s);
|
|
253
|
-
}
|
|
254
|
-
attributes.subjects = [...existing];
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function mergeActions(
|
|
258
|
-
attributes: Record<string, unknown>,
|
|
259
|
-
newOutput: Record<string, unknown>,
|
|
260
|
-
): void {
|
|
261
|
-
const existing = new Set((attributes.actions as string[]) ?? []);
|
|
262
|
-
const incoming = (newOutput.actions as string[]) ?? [];
|
|
263
|
-
for (const a of incoming) {
|
|
264
|
-
existing.add(a);
|
|
265
|
-
}
|
|
266
|
-
attributes.actions = [...existing];
|
|
267
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
|
|
2
|
-
import { detectEvents, type DetectionConfig, type DetectionRule } from '../services/event-detection-service.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Sensible default detection rules for common event types.
|
|
6
|
-
* These are fallbacks when the caller doesn't provide explicit rules —
|
|
7
|
-
* the system is not limited to these event types.
|
|
8
|
-
*/
|
|
9
|
-
const DEFAULT_RULES_BY_EVENT_TYPE: Record<string, DetectionRule[]> = {
|
|
10
|
-
turnover: [
|
|
11
|
-
{ ruleType: 'segment_transition', params: { field: 'subjects' }, weight: 0.5 },
|
|
12
|
-
{ ruleType: 'short_segment', params: { maxDurationSeconds: 5 }, weight: 0.3 },
|
|
13
|
-
{ ruleType: 'attribute_match', params: { field: 'actions', pattern: 'steal|turnover|loss|intercept' }, weight: 0.2 },
|
|
14
|
-
],
|
|
15
|
-
scene_change: [
|
|
16
|
-
{ ruleType: 'segment_transition', params: { field: 'segmentType' }, weight: 1.0 },
|
|
17
|
-
],
|
|
18
|
-
short_play: [
|
|
19
|
-
{ ruleType: 'short_segment', params: { maxDurationSeconds: 3 }, weight: 0.6 },
|
|
20
|
-
{ ruleType: 'segment_transition', params: { field: 'subjects' }, weight: 0.4 },
|
|
21
|
-
],
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export async function run(
|
|
25
|
-
input: Record<string, unknown>,
|
|
26
|
-
context: ToolContext,
|
|
27
|
-
): Promise<ToolExecutionResult> {
|
|
28
|
-
const assetId = input.asset_id as string | undefined;
|
|
29
|
-
if (!assetId) {
|
|
30
|
-
return { content: 'asset_id is required.', isError: true };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const eventType = input.event_type as string | undefined;
|
|
34
|
-
if (!eventType) {
|
|
35
|
-
return { content: 'event_type is required.', isError: true };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Parse detection rules: use provided rules or fall back to defaults
|
|
39
|
-
let rules: DetectionRule[];
|
|
40
|
-
const rawRules = input.detection_rules;
|
|
41
|
-
|
|
42
|
-
if (rawRules) {
|
|
43
|
-
// Accept rules as a JSON string or as an already-parsed array
|
|
44
|
-
if (typeof rawRules === 'string') {
|
|
45
|
-
try {
|
|
46
|
-
const parsed = JSON.parse(rawRules);
|
|
47
|
-
if (!Array.isArray(parsed)) {
|
|
48
|
-
return { content: 'detection_rules must be a valid JSON array of rule objects.', isError: true };
|
|
49
|
-
}
|
|
50
|
-
rules = parsed as DetectionRule[];
|
|
51
|
-
} catch {
|
|
52
|
-
return { content: 'detection_rules must be a valid JSON array of rule objects.', isError: true };
|
|
53
|
-
}
|
|
54
|
-
} else if (Array.isArray(rawRules)) {
|
|
55
|
-
rules = rawRules as DetectionRule[];
|
|
56
|
-
} else {
|
|
57
|
-
return { content: 'detection_rules must be an array of rule objects.', isError: true };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Validate each rule has the required shape
|
|
61
|
-
for (const rule of rules) {
|
|
62
|
-
if (!rule.ruleType || typeof rule.ruleType !== 'string') {
|
|
63
|
-
return { content: 'Each detection rule must have a "ruleType" string.', isError: true };
|
|
64
|
-
}
|
|
65
|
-
if (rule.weight === undefined || typeof rule.weight !== 'number') {
|
|
66
|
-
return { content: 'Each detection rule must have a "weight" number.', isError: true };
|
|
67
|
-
}
|
|
68
|
-
if (!rule.params || typeof rule.params !== 'object') {
|
|
69
|
-
return { content: 'Each detection rule must have a "params" object.', isError: true };
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
// Use defaults for known event types, or a generic transition-based fallback
|
|
74
|
-
rules = DEFAULT_RULES_BY_EVENT_TYPE[eventType] ?? [
|
|
75
|
-
{ ruleType: 'segment_transition', params: { field: 'segmentType' }, weight: 0.6 },
|
|
76
|
-
{ ruleType: 'short_segment', params: { maxDurationSeconds: 5 }, weight: 0.4 },
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const config: DetectionConfig = { eventType, rules };
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const result = detectEvents(assetId, config, {
|
|
84
|
-
onProgress: (msg) => context.onOutput?.(`${msg}\n`),
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
content: JSON.stringify({
|
|
89
|
-
message: `Detected ${result.candidateCount} ${result.eventType} events`,
|
|
90
|
-
assetId: result.assetId,
|
|
91
|
-
eventType: result.eventType,
|
|
92
|
-
totalEvents: result.candidateCount,
|
|
93
|
-
rulesUsed: rules.map((r) => r.ruleType),
|
|
94
|
-
events: result.events.map((e) => ({
|
|
95
|
-
id: e.id,
|
|
96
|
-
startTime: e.startTime,
|
|
97
|
-
endTime: e.endTime,
|
|
98
|
-
confidence: e.confidence,
|
|
99
|
-
reasons: e.reasons,
|
|
100
|
-
})),
|
|
101
|
-
}, null, 2),
|
|
102
|
-
isError: false,
|
|
103
|
-
};
|
|
104
|
-
} catch (err) {
|
|
105
|
-
return {
|
|
106
|
-
content: `Event detection failed: ${(err as Error).message}`,
|
|
107
|
-
isError: true,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recalibration tool for media event detection.
|
|
3
|
-
*
|
|
4
|
-
* Reads all feedback for an asset, analyzes correction patterns, and
|
|
5
|
-
* re-ranks existing events using updated heuristics. Updates confidence
|
|
6
|
-
* scores in the media_events table.
|
|
7
|
-
*
|
|
8
|
-
* All interfaces are generic — works for any event type.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
|
|
12
|
-
import { getFeedbackForAsset, type EventFeedback } from '../services/feedback-store.js';
|
|
13
|
-
import { aggregateFeedback } from '../services/feedback-aggregation.js';
|
|
14
|
-
import { getEventsForAsset, getMediaAssetById } from '../../../../memory/media-store.js';
|
|
15
|
-
import { getDb } from '../../../../memory/db.js';
|
|
16
|
-
import { mediaEvents } from '../../../../memory/schema.js';
|
|
17
|
-
import { eq } from 'drizzle-orm';
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Types
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
interface RecalibrationAdjustment {
|
|
24
|
-
eventType: string;
|
|
25
|
-
action: string;
|
|
26
|
-
detail: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface EventUpdate {
|
|
30
|
-
eventId: string;
|
|
31
|
-
eventType: string;
|
|
32
|
-
oldConfidence: number;
|
|
33
|
-
newConfidence: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
// Tool entry point
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
|
|
40
|
-
export async function run(
|
|
41
|
-
input: Record<string, unknown>,
|
|
42
|
-
context: ToolContext,
|
|
43
|
-
): Promise<ToolExecutionResult> {
|
|
44
|
-
const assetId = input.asset_id as string | undefined;
|
|
45
|
-
if (!assetId) {
|
|
46
|
-
return { content: 'asset_id is required.', isError: true };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const asset = getMediaAssetById(assetId);
|
|
50
|
-
if (!asset) {
|
|
51
|
-
return { content: `Asset "${assetId}" not found.`, isError: true };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const allFeedback = getFeedbackForAsset(assetId);
|
|
55
|
-
if (allFeedback.length === 0) {
|
|
56
|
-
return {
|
|
57
|
-
content: JSON.stringify({
|
|
58
|
-
message: 'No feedback found for this asset. Submit feedback first, then recalibrate.',
|
|
59
|
-
assetId,
|
|
60
|
-
}, null, 2),
|
|
61
|
-
isError: false,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const aggregation = aggregateFeedback(assetId);
|
|
66
|
-
const allEvents = getEventsForAsset(assetId);
|
|
67
|
-
|
|
68
|
-
// Build a map of event ID to its feedback entries
|
|
69
|
-
const feedbackByEventId = new Map<string, EventFeedback[]>();
|
|
70
|
-
for (const fb of allFeedback) {
|
|
71
|
-
if (!feedbackByEventId.has(fb.eventId)) {
|
|
72
|
-
feedbackByEventId.set(fb.eventId, []);
|
|
73
|
-
}
|
|
74
|
-
feedbackByEventId.get(fb.eventId)!.push(fb);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Build a map of event ID to event type for filtering feedback by type
|
|
78
|
-
const eventTypeById = new Map<string, string>();
|
|
79
|
-
for (const ev of allEvents) {
|
|
80
|
-
eventTypeById.set(ev.id, ev.eventType);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const adjustments: RecalibrationAdjustment[] = [];
|
|
84
|
-
const eventUpdates: EventUpdate[] = [];
|
|
85
|
-
const db = getDb();
|
|
86
|
-
|
|
87
|
-
// Analyze patterns per event type
|
|
88
|
-
for (const stats of aggregation.statsByEventType) {
|
|
89
|
-
const { eventType, correct, incorrect, boundaryEdit, missed } = stats;
|
|
90
|
-
const totalReviewed = correct + incorrect + boundaryEdit + missed;
|
|
91
|
-
if (totalReviewed === 0) continue;
|
|
92
|
-
|
|
93
|
-
// Pattern 1: High false positive rate — penalize low-confidence events
|
|
94
|
-
const falsePositiveRate = totalReviewed > 0 ? incorrect / totalReviewed : 0;
|
|
95
|
-
if (falsePositiveRate > 0.3 && incorrect >= 2) {
|
|
96
|
-
adjustments.push({
|
|
97
|
-
eventType,
|
|
98
|
-
action: 'penalize_low_confidence',
|
|
99
|
-
detail: `False positive rate ${(falsePositiveRate * 100).toFixed(1)}% (${incorrect}/${totalReviewed}) — reducing confidence on unreviewed events of this type`,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Pattern 2: Many missed events — note for threshold adjustment
|
|
104
|
-
if (missed >= 2) {
|
|
105
|
-
adjustments.push({
|
|
106
|
-
eventType,
|
|
107
|
-
action: 'note_missed_events',
|
|
108
|
-
detail: `${missed} missed events reported — consider lowering detection threshold or adding detection rules for this type`,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Pattern 3: Boundary edits — compute average adjustment
|
|
113
|
-
if (boundaryEdit >= 1) {
|
|
114
|
-
const boundaryFeedback = allFeedback.filter(
|
|
115
|
-
(fb) => fb.feedbackType === 'boundary_edit' && eventTypeById.get(fb.eventId) === eventType,
|
|
116
|
-
);
|
|
117
|
-
let startAdjTotal = 0;
|
|
118
|
-
let endAdjTotal = 0;
|
|
119
|
-
let startAdjCount = 0;
|
|
120
|
-
let endAdjCount = 0;
|
|
121
|
-
|
|
122
|
-
for (const fb of boundaryFeedback) {
|
|
123
|
-
if (fb.originalStartTime !== null && fb.correctedStartTime !== null) {
|
|
124
|
-
startAdjTotal += fb.correctedStartTime - fb.originalStartTime;
|
|
125
|
-
startAdjCount++;
|
|
126
|
-
}
|
|
127
|
-
if (fb.originalEndTime !== null && fb.correctedEndTime !== null) {
|
|
128
|
-
endAdjTotal += fb.correctedEndTime - fb.originalEndTime;
|
|
129
|
-
endAdjCount++;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const avgStartAdj = startAdjCount > 0 ? startAdjTotal / startAdjCount : 0;
|
|
134
|
-
const avgEndAdj = endAdjCount > 0 ? endAdjTotal / endAdjCount : 0;
|
|
135
|
-
|
|
136
|
-
if (startAdjCount > 0 || endAdjCount > 0) {
|
|
137
|
-
adjustments.push({
|
|
138
|
-
eventType,
|
|
139
|
-
action: 'boundary_correction_pattern',
|
|
140
|
-
detail: `Average boundary adjustment: start ${avgStartAdj >= 0 ? '+' : ''}${avgStartAdj.toFixed(2)}s (n=${startAdjCount}), end ${avgEndAdj >= 0 ? '+' : ''}${avgEndAdj.toFixed(2)}s (n=${endAdjCount})`,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Re-rank events: adjust confidence based on feedback
|
|
147
|
-
for (const event of allEvents) {
|
|
148
|
-
const eventFeedback = feedbackByEventId.get(event.id);
|
|
149
|
-
if (!eventFeedback || eventFeedback.length === 0) {
|
|
150
|
-
// For events without direct feedback, apply type-level adjustments
|
|
151
|
-
const stats = aggregation.statsByEventType.find((s) => s.eventType === event.eventType);
|
|
152
|
-
if (stats) {
|
|
153
|
-
const totalReviewed = stats.correct + stats.incorrect + stats.boundaryEdit + stats.missed;
|
|
154
|
-
const falsePositiveRate = totalReviewed > 0 ? stats.incorrect / totalReviewed : 0;
|
|
155
|
-
|
|
156
|
-
// If high false positive rate for this type, reduce unreviewed event confidence
|
|
157
|
-
if (falsePositiveRate > 0.3 && totalReviewed >= 3) {
|
|
158
|
-
const penalty = Math.min(falsePositiveRate * 0.3, 0.2);
|
|
159
|
-
const newConfidence = Math.max(0.05, event.confidence - penalty);
|
|
160
|
-
if (newConfidence !== event.confidence) {
|
|
161
|
-
db.update(mediaEvents)
|
|
162
|
-
.set({ confidence: newConfidence })
|
|
163
|
-
.where(eq(mediaEvents.id, event.id))
|
|
164
|
-
.run();
|
|
165
|
-
eventUpdates.push({
|
|
166
|
-
eventId: event.id,
|
|
167
|
-
eventType: event.eventType,
|
|
168
|
-
oldConfidence: event.confidence,
|
|
169
|
-
newConfidence,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Direct feedback: adjust confidence based on the latest feedback
|
|
178
|
-
const latestFeedback = eventFeedback.sort((a, b) => b.createdAt - a.createdAt)[0];
|
|
179
|
-
let newConfidence = event.confidence;
|
|
180
|
-
|
|
181
|
-
switch (latestFeedback.feedbackType) {
|
|
182
|
-
case 'correct':
|
|
183
|
-
// Boost confidence toward 1.0
|
|
184
|
-
newConfidence = Math.min(1.0, event.confidence + (1.0 - event.confidence) * 0.3);
|
|
185
|
-
break;
|
|
186
|
-
case 'incorrect':
|
|
187
|
-
// Sharply reduce confidence
|
|
188
|
-
newConfidence = Math.max(0.05, event.confidence * 0.3);
|
|
189
|
-
break;
|
|
190
|
-
case 'boundary_edit':
|
|
191
|
-
// Slight confidence boost (event was real but boundaries were off)
|
|
192
|
-
newConfidence = Math.min(1.0, event.confidence + (1.0 - event.confidence) * 0.15);
|
|
193
|
-
break;
|
|
194
|
-
case 'missed':
|
|
195
|
-
// User-reported events keep their initial confidence
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
newConfidence = Math.round(newConfidence * 1000) / 1000;
|
|
200
|
-
|
|
201
|
-
if (newConfidence !== event.confidence) {
|
|
202
|
-
db.update(mediaEvents)
|
|
203
|
-
.set({ confidence: newConfidence })
|
|
204
|
-
.where(eq(mediaEvents.id, event.id))
|
|
205
|
-
.run();
|
|
206
|
-
eventUpdates.push({
|
|
207
|
-
eventId: event.id,
|
|
208
|
-
eventType: event.eventType,
|
|
209
|
-
oldConfidence: event.confidence,
|
|
210
|
-
newConfidence,
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
context.onOutput?.(`Recalibrated ${eventUpdates.length} events based on ${allFeedback.length} feedback entries.\n`);
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
content: JSON.stringify({
|
|
219
|
-
message: `Recalibration complete for asset ${assetId}`,
|
|
220
|
-
assetId,
|
|
221
|
-
totalFeedbackEntries: allFeedback.length,
|
|
222
|
-
adjustments,
|
|
223
|
-
eventsUpdated: eventUpdates.length,
|
|
224
|
-
eventUpdates: eventUpdates.map((u) => ({
|
|
225
|
-
eventId: u.eventId,
|
|
226
|
-
eventType: u.eventType,
|
|
227
|
-
oldConfidence: u.oldConfidence,
|
|
228
|
-
newConfidence: u.newConfidence,
|
|
229
|
-
delta: Math.round((u.newConfidence - u.oldConfidence) * 1000) / 1000,
|
|
230
|
-
})),
|
|
231
|
-
aggregation: aggregation.statsByEventType,
|
|
232
|
-
}, null, 2),
|
|
233
|
-
isError: false,
|
|
234
|
-
};
|
|
235
|
-
}
|