@vellumai/assistant 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +18 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-orchestrator.test.ts +752 -271
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +5 -0
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1260 -85
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +4 -65
- package/src/__tests__/channel-guardian.test.ts +556 -0
- package/src/__tests__/channel-readiness-service.test.ts +74 -7
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +12 -7
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +6 -2
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +126 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +228 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
- package/src/__tests__/run-orchestrator.test.ts +20 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +237 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +2 -1
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +141 -21
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +45 -29
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +106 -5
- package/src/calls/call-orchestrator.ts +252 -54
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +7 -5
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +40 -15
- package/src/calls/twilio-routes.ts +60 -45
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
- package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
- package/src/config/bundled-skills/messaging/SKILL.md +12 -2
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +26 -2
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +156 -1137
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +107 -56
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +0 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -2463
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +4 -6
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/index.ts +9 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +54 -5
- package/src/daemon/handlers/shared.ts +3 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +2 -2
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +60 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +226 -2527
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -379
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +67 -2
- package/src/daemon/server.ts +60 -44
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +113 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +149 -15
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +28 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +24 -1
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +3 -1
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +200 -1
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +121 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +11 -42
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +6 -9
- package/src/memory/jobs-worker.ts +51 -36
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +160 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +130 -7
- package/src/memory/search/entity.ts +10 -19
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +21 -22
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/providers/sms/adapter.ts +3 -6
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +119 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +115 -5
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +0 -21
- package/src/runtime/channel-guardian-service.ts +48 -7
- package/src/runtime/channel-readiness-service.ts +160 -34
- package/src/runtime/channel-readiness-types.ts +10 -4
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -743
- package/src/runtime/http-types.ts +56 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +49 -6
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1634
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +52 -34
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +7 -3
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +72 -365
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
- package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
- package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
- package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
- package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
- package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
- package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
- package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
- package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
- package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
|
@@ -8,17 +8,19 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { v4 as uuid } from 'uuid';
|
|
11
|
-
import type { Message, ContentBlock
|
|
11
|
+
import type { Message, ContentBlock } from '../providers/types.js';
|
|
12
|
+
import type { ChannelId, TurnChannelContext } from '../channels/types.js';
|
|
12
13
|
import type { ServerMessage, UsageStats, SurfaceType, SurfaceData, DynamicPageSurfaceData } from './ipc-protocol.js';
|
|
13
14
|
import type { AgentLoop, CheckpointDecision, AgentEvent } from '../agent/loop.js';
|
|
14
15
|
import type { Provider } from '../providers/types.js';
|
|
15
16
|
import { createAssistantMessage } from '../agent/message-types.js';
|
|
16
17
|
import * as conversationStore from '../memory/conversation-store.js';
|
|
18
|
+
import { getConversationOriginChannel } from '../memory/conversation-store.js';
|
|
17
19
|
import type { PermissionPrompter } from '../permissions/prompter.js';
|
|
18
20
|
import { getConfig } from '../config/loader.js';
|
|
19
21
|
import { getLogger } from '../util/logger.js';
|
|
20
22
|
import type { TraceEmitter } from './trace-emitter.js';
|
|
21
|
-
import { classifySessionError, isUserCancellation,
|
|
23
|
+
import { classifySessionError, isUserCancellation, buildSessionErrorMessage } from './session-error.js';
|
|
22
24
|
import type { ToolProfiler } from '../events/tool-profiling-listener.js';
|
|
23
25
|
import type { ContextWindowManager } from '../context/window-manager.js';
|
|
24
26
|
import { getHookManager } from '../hooks/manager.js';
|
|
@@ -34,11 +36,9 @@ import {
|
|
|
34
36
|
stripInjectedContext,
|
|
35
37
|
} from './session-runtime-assembly.js';
|
|
36
38
|
import { buildTemporalContext } from './date-context.js';
|
|
37
|
-
import type { ActiveSurfaceContext, ChannelCapabilities, GuardianRuntimeContext } from './session-runtime-assembly.js';
|
|
39
|
+
import type { ActiveSurfaceContext, ChannelCapabilities, ChannelTurnContextParams, GuardianRuntimeContext } from './session-runtime-assembly.js';
|
|
38
40
|
import {
|
|
39
41
|
cleanAssistantContent,
|
|
40
|
-
drainDirectiveDisplayBuffer,
|
|
41
|
-
type DirectiveRequest,
|
|
42
42
|
type AssistantAttachmentDraft,
|
|
43
43
|
} from './assistant-attachments.js';
|
|
44
44
|
import { prepareMemoryContext } from './session-memory.js';
|
|
@@ -49,8 +49,6 @@ import {
|
|
|
49
49
|
} from './session-attachments.js';
|
|
50
50
|
import { consolidateAssistantMessages } from './session-history.js';
|
|
51
51
|
import { recordUsage } from './session-usage.js';
|
|
52
|
-
import { recordRequestLog } from '../memory/llm-request-log-store.js';
|
|
53
|
-
import { isProviderOrderingError } from './session-slash.js';
|
|
54
52
|
import { repairHistory, deepRepairHistory } from './history-repair.js';
|
|
55
53
|
import { stripMediaPayloadsForRetry, raceWithTimeout } from './session-media-retry.js';
|
|
56
54
|
import { commitTurnChanges } from '../workspace/turn-commit.js';
|
|
@@ -58,6 +56,11 @@ import { getWorkspaceGitService } from '../workspace/git-service.js';
|
|
|
58
56
|
import { commitAppTurnChanges } from '../memory/app-git-service.js';
|
|
59
57
|
import type { UsageActor } from '../usage/actors.js';
|
|
60
58
|
import type { SkillProjectionCache } from './session-skill-tools.js';
|
|
59
|
+
import {
|
|
60
|
+
createEventHandlerState,
|
|
61
|
+
dispatchAgentEvent,
|
|
62
|
+
type EventHandlerDeps,
|
|
63
|
+
} from './session-agent-loop-handlers.js';
|
|
61
64
|
|
|
62
65
|
const log = getLogger('session-agent-loop');
|
|
63
66
|
|
|
@@ -95,6 +98,7 @@ export interface AgentLoopSessionContext {
|
|
|
95
98
|
workspaceTopLevelContext: string | null;
|
|
96
99
|
workspaceTopLevelDirty: boolean;
|
|
97
100
|
channelCapabilities?: ChannelCapabilities;
|
|
101
|
+
commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
98
102
|
guardianContext?: GuardianRuntimeContext;
|
|
99
103
|
|
|
100
104
|
readonly coreToolNames: Set<string>;
|
|
@@ -112,6 +116,7 @@ export interface AgentLoopSessionContext {
|
|
|
112
116
|
lastAttachmentWarnings: string[];
|
|
113
117
|
|
|
114
118
|
hasNoClient: boolean;
|
|
119
|
+
readonly streamThinking: boolean;
|
|
115
120
|
readonly prompter: PermissionPrompter;
|
|
116
121
|
readonly queue: MessageQueue;
|
|
117
122
|
|
|
@@ -124,6 +129,7 @@ export interface AgentLoopSessionContext {
|
|
|
124
129
|
hasQueuedMessages(): boolean;
|
|
125
130
|
canHandoffAtCheckpoint(): boolean;
|
|
126
131
|
drainQueue(reason: QueueDrainReason): void;
|
|
132
|
+
getTurnChannelContext(): TurnChannelContext | null;
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
// ── runAgentLoop ─────────────────────────────────────────────────────
|
|
@@ -143,6 +149,18 @@ export async function runAgentLoopImpl(
|
|
|
143
149
|
const rlog = log.child({ conversationId: ctx.conversationId, requestId: reqId });
|
|
144
150
|
let yieldedForHandoff = false;
|
|
145
151
|
|
|
152
|
+
// Capture the turn channel context *before* any awaits so a second
|
|
153
|
+
// message from a different channel can't overwrite it mid-flight.
|
|
154
|
+
// When context is unavailable (e.g. regenerate after daemon restart),
|
|
155
|
+
// fall back to the conversation's persisted origin channel.
|
|
156
|
+
const capturedTurnChannelContext: TurnChannelContext = (() => {
|
|
157
|
+
const live = ctx.getTurnChannelContext();
|
|
158
|
+
if (live) return live;
|
|
159
|
+
const origin = getConversationOriginChannel(ctx.conversationId);
|
|
160
|
+
if (origin) return { userMessageChannel: origin, assistantMessageChannel: origin };
|
|
161
|
+
return { userMessageChannel: 'macos' as ChannelId, assistantMessageChannel: 'macos' as ChannelId };
|
|
162
|
+
})();
|
|
163
|
+
|
|
146
164
|
ctx.lastAssistantAttachments = [];
|
|
147
165
|
ctx.lastAttachmentWarnings = [];
|
|
148
166
|
|
|
@@ -204,19 +222,8 @@ export async function runAgentLoopImpl(
|
|
|
204
222
|
emitUsage(ctx, compacted.summaryInputTokens, compacted.summaryOutputTokens, compacted.summaryModel, onEvent, 'context_compactor', reqId);
|
|
205
223
|
}
|
|
206
224
|
|
|
207
|
-
|
|
208
|
-
let exchangeInputTokens = 0;
|
|
209
|
-
let exchangeOutputTokens = 0;
|
|
210
|
-
let model = '';
|
|
225
|
+
const state = createEventHandlerState();
|
|
211
226
|
let runMessages = ctx.messages;
|
|
212
|
-
const pendingToolResults = new Map<string, { content: string; isError: boolean; contentBlocks?: ContentBlock[] }>();
|
|
213
|
-
const persistedToolUseIds = new Set<string>();
|
|
214
|
-
const accumulatedDirectives: DirectiveRequest[] = [];
|
|
215
|
-
const accumulatedToolContentBlocks: ContentBlock[] = [];
|
|
216
|
-
const directiveWarnings: string[] = [];
|
|
217
|
-
let pendingDirectiveDisplayBuffer = '';
|
|
218
|
-
let lastAssistantMessageId: string | undefined;
|
|
219
|
-
let providerErrorUserMessage: string | null = null;
|
|
220
227
|
|
|
221
228
|
const memoryResult = await prepareMemoryContext(
|
|
222
229
|
{
|
|
@@ -235,11 +242,16 @@ export async function runAgentLoopImpl(
|
|
|
235
242
|
);
|
|
236
243
|
|
|
237
244
|
if (memoryResult.conflictClarification) {
|
|
245
|
+
const loopChannelMeta = {
|
|
246
|
+
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
247
|
+
assistantMessageChannel: capturedTurnChannelContext.assistantMessageChannel,
|
|
248
|
+
};
|
|
238
249
|
const assistantMessage = createAssistantMessage(memoryResult.conflictClarification);
|
|
239
250
|
conversationStore.addMessage(
|
|
240
251
|
ctx.conversationId,
|
|
241
252
|
'assistant',
|
|
242
253
|
JSON.stringify(assistantMessage.content),
|
|
254
|
+
loopChannelMeta,
|
|
243
255
|
);
|
|
244
256
|
ctx.messages.push(assistantMessage);
|
|
245
257
|
onEvent({
|
|
@@ -292,11 +304,21 @@ export async function runAgentLoopImpl(
|
|
|
292
304
|
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
293
305
|
});
|
|
294
306
|
|
|
307
|
+
// Use the channel context captured at the top of this function so it
|
|
308
|
+
// reflects the channel that originally sent *this* turn's message,
|
|
309
|
+
// even if a newer message from a different channel arrived since.
|
|
310
|
+
const channelTurnContext: ChannelTurnContextParams = {
|
|
311
|
+
turnContext: capturedTurnChannelContext,
|
|
312
|
+
conversationOriginChannel: getConversationOriginChannel(ctx.conversationId),
|
|
313
|
+
};
|
|
314
|
+
|
|
295
315
|
runMessages = applyRuntimeInjections(runMessages, {
|
|
296
316
|
softConflictInstruction,
|
|
297
317
|
activeSurface,
|
|
298
318
|
workspaceTopLevelContext: ctx.workspaceTopLevelContext,
|
|
299
319
|
channelCapabilities: ctx.channelCapabilities ?? null,
|
|
320
|
+
channelCommandContext: ctx.commandIntent ?? null,
|
|
321
|
+
channelTurnContext,
|
|
300
322
|
guardianContext: ctx.guardianContext ?? null,
|
|
301
323
|
temporalContext,
|
|
302
324
|
});
|
|
@@ -309,235 +331,21 @@ export async function runAgentLoopImpl(
|
|
|
309
331
|
runMessages = preRunRepair.messages;
|
|
310
332
|
}
|
|
311
333
|
|
|
312
|
-
let orderingErrorDetected = false;
|
|
313
|
-
let deferredOrderingError: string | null = null;
|
|
314
|
-
let contextTooLargeDetected = false;
|
|
315
334
|
let preRunHistoryLength = runMessages.length;
|
|
316
335
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
llmCallStartedEmitted = true;
|
|
325
|
-
ctx.traceEmitter.emit('llm_call_started', `LLM call to ${ctx.provider.name}`, {
|
|
326
|
-
requestId: reqId,
|
|
327
|
-
status: 'info',
|
|
328
|
-
attributes: { provider: ctx.provider.name, model: model || 'unknown' },
|
|
329
|
-
});
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
switch (event.type) {
|
|
333
|
-
case 'text_delta': {
|
|
334
|
-
emitLlmCallStartedIfNeeded();
|
|
335
|
-
pendingDirectiveDisplayBuffer += event.text;
|
|
336
|
-
const drained = drainDirectiveDisplayBuffer(pendingDirectiveDisplayBuffer);
|
|
337
|
-
pendingDirectiveDisplayBuffer = drained.bufferedRemainder;
|
|
338
|
-
if (drained.emitText.length > 0) {
|
|
339
|
-
onEvent({ type: 'assistant_text_delta', text: drained.emitText, sessionId: ctx.conversationId });
|
|
340
|
-
if (isFirstMessage) firstAssistantText += drained.emitText;
|
|
341
|
-
}
|
|
342
|
-
break;
|
|
343
|
-
}
|
|
344
|
-
case 'thinking_delta':
|
|
345
|
-
emitLlmCallStartedIfNeeded();
|
|
346
|
-
onEvent({ type: 'assistant_thinking_delta', thinking: event.thinking });
|
|
347
|
-
break;
|
|
348
|
-
case 'tool_use':
|
|
349
|
-
toolUseIdToName.set(event.id, event.name);
|
|
350
|
-
currentTurnToolNames.push(event.name);
|
|
351
|
-
onEvent({ type: 'tool_use_start', toolName: event.name, input: event.input, sessionId: ctx.conversationId });
|
|
352
|
-
break;
|
|
353
|
-
case 'tool_output_chunk': {
|
|
354
|
-
// Try to parse structured progress fields from the chunk.
|
|
355
|
-
// Cheap pre-check: only attempt JSON.parse when the chunk looks like an object.
|
|
356
|
-
let structured: { subType?: 'tool_start' | 'tool_complete' | 'status'; subToolName?: string; subToolInput?: string; subToolIsError?: boolean; subToolId?: string } | undefined;
|
|
357
|
-
const trimmed = event.chunk.trimStart();
|
|
358
|
-
if (trimmed.length > 0 && trimmed.length < 4096 && trimmed[0] === '{') {
|
|
359
|
-
try {
|
|
360
|
-
const parsed = JSON.parse(event.chunk);
|
|
361
|
-
const VALID_SUB_TYPES = new Set(['tool_start', 'tool_complete', 'status']);
|
|
362
|
-
if (parsed && typeof parsed === 'object' && typeof parsed.subType === 'string' && VALID_SUB_TYPES.has(parsed.subType)) {
|
|
363
|
-
structured = {
|
|
364
|
-
subType: parsed.subType as 'tool_start' | 'tool_complete' | 'status',
|
|
365
|
-
subToolName: typeof parsed.subToolName === 'string' ? parsed.subToolName : undefined,
|
|
366
|
-
subToolInput: typeof parsed.subToolInput === 'string' ? parsed.subToolInput : undefined,
|
|
367
|
-
subToolIsError: typeof parsed.subToolIsError === 'boolean' ? parsed.subToolIsError : undefined,
|
|
368
|
-
subToolId: typeof parsed.subToolId === 'string' ? parsed.subToolId : undefined,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
} catch {
|
|
372
|
-
// Not valid JSON — pass through as plain chunk
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
if (structured) {
|
|
376
|
-
onEvent({
|
|
377
|
-
type: 'tool_output_chunk',
|
|
378
|
-
chunk: event.chunk,
|
|
379
|
-
sessionId: ctx.conversationId,
|
|
380
|
-
subType: structured.subType,
|
|
381
|
-
subToolName: structured.subToolName,
|
|
382
|
-
subToolInput: structured.subToolInput,
|
|
383
|
-
subToolIsError: structured.subToolIsError,
|
|
384
|
-
subToolId: structured.subToolId,
|
|
385
|
-
});
|
|
386
|
-
} else {
|
|
387
|
-
onEvent({ type: 'tool_output_chunk', chunk: event.chunk, sessionId: ctx.conversationId });
|
|
388
|
-
}
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
case 'input_json_delta':
|
|
392
|
-
onEvent({ type: 'tool_input_delta', toolName: event.toolName, content: event.accumulatedJson, sessionId: ctx.conversationId });
|
|
393
|
-
break;
|
|
394
|
-
case 'tool_result': {
|
|
395
|
-
const imageBlock = event.contentBlocks?.find((b): b is ImageContent => b.type === 'image');
|
|
396
|
-
onEvent({ type: 'tool_result', toolName: '', result: event.content, isError: event.isError, diff: event.diff, status: event.status, sessionId: ctx.conversationId, imageData: imageBlock?.source.data });
|
|
397
|
-
pendingToolResults.set(event.toolUseId, { content: event.content, isError: event.isError, contentBlocks: event.contentBlocks });
|
|
398
|
-
{
|
|
399
|
-
const toolName = toolUseIdToName.get(event.toolUseId);
|
|
400
|
-
if (toolName === 'file_write' || toolName === 'bash') {
|
|
401
|
-
ctx.markWorkspaceTopLevelDirty();
|
|
402
|
-
} else if (toolName === 'file_edit' && !event.isError) {
|
|
403
|
-
ctx.markWorkspaceTopLevelDirty();
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
if (event.contentBlocks) {
|
|
407
|
-
for (const cb of event.contentBlocks) {
|
|
408
|
-
if (cb.type === 'image' || cb.type === 'file') {
|
|
409
|
-
accumulatedToolContentBlocks.push(cb);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
case 'error':
|
|
416
|
-
if (isProviderOrderingError(event.error.message)) {
|
|
417
|
-
orderingErrorDetected = true;
|
|
418
|
-
deferredOrderingError = event.error.message;
|
|
419
|
-
} else if (isContextTooLarge(event.error.message)) {
|
|
420
|
-
contextTooLargeDetected = true;
|
|
421
|
-
} else {
|
|
422
|
-
const classified = classifySessionError(event.error, { phase: 'agent_loop' });
|
|
423
|
-
onEvent(buildSessionErrorMessage(ctx.conversationId, classified));
|
|
424
|
-
providerErrorUserMessage = classified.userMessage;
|
|
425
|
-
}
|
|
426
|
-
break;
|
|
427
|
-
case 'message_complete': {
|
|
428
|
-
if (pendingDirectiveDisplayBuffer.length > 0) {
|
|
429
|
-
onEvent({
|
|
430
|
-
type: 'assistant_text_delta',
|
|
431
|
-
text: pendingDirectiveDisplayBuffer,
|
|
432
|
-
sessionId: ctx.conversationId,
|
|
433
|
-
});
|
|
434
|
-
if (isFirstMessage) firstAssistantText += pendingDirectiveDisplayBuffer;
|
|
435
|
-
pendingDirectiveDisplayBuffer = '';
|
|
436
|
-
}
|
|
437
|
-
if (pendingToolResults.size > 0) {
|
|
438
|
-
const toolResultBlocks = Array.from(pendingToolResults.entries()).map(
|
|
439
|
-
([toolUseId, result]) => ({
|
|
440
|
-
type: 'tool_result',
|
|
441
|
-
tool_use_id: toolUseId,
|
|
442
|
-
content: result.content,
|
|
443
|
-
is_error: result.isError,
|
|
444
|
-
...(result.contentBlocks ? { contentBlocks: result.contentBlocks } : {}),
|
|
445
|
-
}),
|
|
446
|
-
);
|
|
447
|
-
conversationStore.addMessage(
|
|
448
|
-
ctx.conversationId,
|
|
449
|
-
'user',
|
|
450
|
-
JSON.stringify(toolResultBlocks),
|
|
451
|
-
);
|
|
452
|
-
for (const id of pendingToolResults.keys()) {
|
|
453
|
-
persistedToolUseIds.add(id);
|
|
454
|
-
}
|
|
455
|
-
pendingToolResults.clear();
|
|
456
|
-
}
|
|
457
|
-
const { cleanedContent, directives: msgDirectives, warnings: msgWarnings } =
|
|
458
|
-
cleanAssistantContent(event.message.content);
|
|
459
|
-
accumulatedDirectives.push(...msgDirectives);
|
|
460
|
-
directiveWarnings.push(...msgWarnings);
|
|
461
|
-
if (msgDirectives.length > 0) {
|
|
462
|
-
rlog.info(
|
|
463
|
-
{ parsedDirectives: msgDirectives.map(d => ({ source: d.source, path: d.path, mimeType: d.mimeType })), totalAccumulated: accumulatedDirectives.length },
|
|
464
|
-
'Parsed attachment directives from assistant message',
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const contentWithSurfaces: ContentBlock[] = [...cleanedContent as ContentBlock[]];
|
|
469
|
-
for (const surface of ctx.currentTurnSurfaces) {
|
|
470
|
-
contentWithSurfaces.push({
|
|
471
|
-
type: 'ui_surface',
|
|
472
|
-
surfaceId: surface.surfaceId,
|
|
473
|
-
surfaceType: surface.surfaceType,
|
|
474
|
-
title: surface.title,
|
|
475
|
-
data: surface.data,
|
|
476
|
-
actions: surface.actions,
|
|
477
|
-
display: surface.display,
|
|
478
|
-
} as unknown as ContentBlock);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
const assistantMsg = conversationStore.addMessage(
|
|
482
|
-
ctx.conversationId,
|
|
483
|
-
'assistant',
|
|
484
|
-
JSON.stringify(contentWithSurfaces),
|
|
485
|
-
);
|
|
486
|
-
lastAssistantMessageId = assistantMsg.id;
|
|
487
|
-
|
|
488
|
-
ctx.currentTurnSurfaces = [];
|
|
489
|
-
|
|
490
|
-
const charCount = cleanedContent
|
|
491
|
-
.filter((b) => (b as Record<string, unknown>).type === 'text')
|
|
492
|
-
.reduce((sum: number, b) => sum + ((b as { text?: string }).text?.length ?? 0), 0);
|
|
493
|
-
const toolUseCount = event.message.content
|
|
494
|
-
.filter((b) => b.type === 'tool_use')
|
|
495
|
-
.length;
|
|
496
|
-
ctx.traceEmitter.emit('assistant_message', 'Assistant message complete', {
|
|
497
|
-
requestId: reqId,
|
|
498
|
-
status: 'success',
|
|
499
|
-
attributes: { charCount, toolUseCount },
|
|
500
|
-
});
|
|
501
|
-
break;
|
|
502
|
-
}
|
|
503
|
-
case 'usage':
|
|
504
|
-
exchangeInputTokens += event.inputTokens;
|
|
505
|
-
exchangeOutputTokens += event.outputTokens;
|
|
506
|
-
model = event.model;
|
|
507
|
-
|
|
508
|
-
if (event.rawRequest && event.rawResponse) {
|
|
509
|
-
try {
|
|
510
|
-
recordRequestLog(
|
|
511
|
-
ctx.conversationId,
|
|
512
|
-
JSON.stringify(event.rawRequest),
|
|
513
|
-
JSON.stringify(event.rawResponse),
|
|
514
|
-
);
|
|
515
|
-
} catch (err) {
|
|
516
|
-
rlog.warn({ err }, 'Failed to persist LLM request log (non-fatal)');
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
emitLlmCallStartedIfNeeded();
|
|
521
|
-
|
|
522
|
-
ctx.traceEmitter.emit('llm_call_finished', `LLM call to ${ctx.provider.name} finished`, {
|
|
523
|
-
requestId: reqId,
|
|
524
|
-
status: 'success',
|
|
525
|
-
attributes: {
|
|
526
|
-
provider: ctx.provider.name,
|
|
527
|
-
model: event.model,
|
|
528
|
-
inputTokens: event.inputTokens,
|
|
529
|
-
outputTokens: event.outputTokens,
|
|
530
|
-
latencyMs: event.providerDurationMs,
|
|
531
|
-
},
|
|
532
|
-
});
|
|
533
|
-
llmCallStartedEmitted = false;
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
336
|
+
const deps: EventHandlerDeps = {
|
|
337
|
+
ctx,
|
|
338
|
+
onEvent,
|
|
339
|
+
reqId,
|
|
340
|
+
isFirstMessage,
|
|
341
|
+
rlog,
|
|
342
|
+
turnChannelContext: capturedTurnChannelContext,
|
|
536
343
|
};
|
|
344
|
+
const eventHandler = (event: AgentEvent) => dispatchAgentEvent(state, deps, event);
|
|
537
345
|
|
|
538
346
|
const onCheckpoint = (): CheckpointDecision => {
|
|
539
|
-
const turnTools = currentTurnToolNames;
|
|
540
|
-
currentTurnToolNames = [];
|
|
347
|
+
const turnTools = state.currentTurnToolNames;
|
|
348
|
+
state.currentTurnToolNames = [];
|
|
541
349
|
|
|
542
350
|
if (ctx.canHandoffAtCheckpoint()) {
|
|
543
351
|
const inBrowserFlow = turnTools.length > 0
|
|
@@ -554,37 +362,37 @@ export async function runAgentLoopImpl(
|
|
|
554
362
|
|
|
555
363
|
let updatedHistory = await ctx.agentLoop.run(
|
|
556
364
|
runMessages,
|
|
557
|
-
|
|
365
|
+
eventHandler,
|
|
558
366
|
abortController.signal,
|
|
559
367
|
reqId,
|
|
560
368
|
onCheckpoint,
|
|
561
369
|
);
|
|
562
370
|
|
|
563
371
|
// One-shot ordering error retry
|
|
564
|
-
if (orderingErrorDetected && updatedHistory.length === preRunHistoryLength) {
|
|
372
|
+
if (state.orderingErrorDetected && updatedHistory.length === preRunHistoryLength) {
|
|
565
373
|
rlog.warn({ phase: 'retry' }, 'Provider ordering error detected, attempting one-shot deep-repair retry');
|
|
566
374
|
const retryRepair = deepRepairHistory(runMessages);
|
|
567
375
|
runMessages = retryRepair.messages;
|
|
568
376
|
preRepairMessages = retryRepair.messages;
|
|
569
377
|
preRunHistoryLength = runMessages.length;
|
|
570
|
-
orderingErrorDetected = false;
|
|
571
|
-
deferredOrderingError = null;
|
|
378
|
+
state.orderingErrorDetected = false;
|
|
379
|
+
state.deferredOrderingError = null;
|
|
572
380
|
|
|
573
381
|
updatedHistory = await ctx.agentLoop.run(
|
|
574
382
|
runMessages,
|
|
575
|
-
|
|
383
|
+
eventHandler,
|
|
576
384
|
abortController.signal,
|
|
577
385
|
reqId,
|
|
578
386
|
onCheckpoint,
|
|
579
387
|
);
|
|
580
388
|
|
|
581
|
-
if (orderingErrorDetected) {
|
|
389
|
+
if (state.orderingErrorDetected) {
|
|
582
390
|
rlog.error({ phase: 'retry' }, 'Deep-repair retry also failed with ordering error. Consider starting a new conversation if this persists.');
|
|
583
391
|
}
|
|
584
392
|
}
|
|
585
393
|
|
|
586
394
|
// One-shot context-too-large recovery
|
|
587
|
-
if (contextTooLargeDetected && updatedHistory.length === preRunHistoryLength) {
|
|
395
|
+
if (state.contextTooLargeDetected && updatedHistory.length === preRunHistoryLength) {
|
|
588
396
|
rlog.warn({ phase: 'retry' }, 'Context too large — attempting forced compaction and retry');
|
|
589
397
|
const emergencyCompact = await ctx.contextWindowManager.maybeCompact(
|
|
590
398
|
ctx.messages,
|
|
@@ -619,23 +427,25 @@ export async function runAgentLoopImpl(
|
|
|
619
427
|
activeSurface,
|
|
620
428
|
workspaceTopLevelContext: ctx.workspaceTopLevelContext,
|
|
621
429
|
channelCapabilities: ctx.channelCapabilities ?? null,
|
|
430
|
+
channelCommandContext: ctx.commandIntent ?? null,
|
|
431
|
+
channelTurnContext,
|
|
622
432
|
guardianContext: ctx.guardianContext ?? null,
|
|
623
433
|
temporalContext,
|
|
624
434
|
});
|
|
625
435
|
preRepairMessages = runMessages;
|
|
626
436
|
preRunHistoryLength = runMessages.length;
|
|
627
|
-
contextTooLargeDetected = false;
|
|
437
|
+
state.contextTooLargeDetected = false;
|
|
628
438
|
|
|
629
439
|
updatedHistory = await ctx.agentLoop.run(
|
|
630
440
|
runMessages,
|
|
631
|
-
|
|
441
|
+
eventHandler,
|
|
632
442
|
abortController.signal,
|
|
633
443
|
reqId,
|
|
634
444
|
onCheckpoint,
|
|
635
445
|
);
|
|
636
446
|
}
|
|
637
447
|
|
|
638
|
-
if (contextTooLargeDetected) {
|
|
448
|
+
if (state.contextTooLargeDetected) {
|
|
639
449
|
const mediaTrimmed = stripMediaPayloadsForRetry(ctx.messages);
|
|
640
450
|
if (mediaTrimmed.modified) {
|
|
641
451
|
rlog.warn(
|
|
@@ -652,16 +462,18 @@ export async function runAgentLoopImpl(
|
|
|
652
462
|
activeSurface,
|
|
653
463
|
workspaceTopLevelContext: ctx.workspaceTopLevelContext,
|
|
654
464
|
channelCapabilities: ctx.channelCapabilities ?? null,
|
|
465
|
+
channelCommandContext: ctx.commandIntent ?? null,
|
|
466
|
+
channelTurnContext,
|
|
655
467
|
guardianContext: ctx.guardianContext ?? null,
|
|
656
468
|
temporalContext,
|
|
657
469
|
});
|
|
658
470
|
preRepairMessages = runMessages;
|
|
659
471
|
preRunHistoryLength = runMessages.length;
|
|
660
|
-
contextTooLargeDetected = false;
|
|
472
|
+
state.contextTooLargeDetected = false;
|
|
661
473
|
|
|
662
474
|
updatedHistory = await ctx.agentLoop.run(
|
|
663
475
|
runMessages,
|
|
664
|
-
|
|
476
|
+
eventHandler,
|
|
665
477
|
abortController.signal,
|
|
666
478
|
reqId,
|
|
667
479
|
onCheckpoint,
|
|
@@ -669,7 +481,7 @@ export async function runAgentLoopImpl(
|
|
|
669
481
|
}
|
|
670
482
|
}
|
|
671
483
|
|
|
672
|
-
if (contextTooLargeDetected) {
|
|
484
|
+
if (state.contextTooLargeDetected) {
|
|
673
485
|
const classified = classifySessionError(
|
|
674
486
|
new Error('context_length_exceeded'),
|
|
675
487
|
{ phase: 'agent_loop' },
|
|
@@ -678,8 +490,8 @@ export async function runAgentLoopImpl(
|
|
|
678
490
|
}
|
|
679
491
|
}
|
|
680
492
|
|
|
681
|
-
if (deferredOrderingError) {
|
|
682
|
-
const classified = classifySessionError(new Error(deferredOrderingError), { phase: 'agent_loop' });
|
|
493
|
+
if (state.deferredOrderingError) {
|
|
494
|
+
const classified = classifySessionError(new Error(state.deferredOrderingError), { phase: 'agent_loop' });
|
|
683
495
|
onEvent(buildSessionErrorMessage(ctx.conversationId, classified));
|
|
684
496
|
}
|
|
685
497
|
|
|
@@ -688,8 +500,8 @@ export async function runAgentLoopImpl(
|
|
|
688
500
|
const msg = updatedHistory[i];
|
|
689
501
|
if (msg.role === 'user') {
|
|
690
502
|
for (const block of msg.content) {
|
|
691
|
-
if (block.type === 'tool_result' && !pendingToolResults.has(block.tool_use_id) && !persistedToolUseIds.has(block.tool_use_id)) {
|
|
692
|
-
pendingToolResults.set(block.tool_use_id, {
|
|
503
|
+
if (block.type === 'tool_result' && !state.pendingToolResults.has(block.tool_use_id) && !state.persistedToolUseIds.has(block.tool_use_id)) {
|
|
504
|
+
state.pendingToolResults.set(block.tool_use_id, {
|
|
693
505
|
content: block.content,
|
|
694
506
|
isError: block.is_error ?? false,
|
|
695
507
|
});
|
|
@@ -699,8 +511,8 @@ export async function runAgentLoopImpl(
|
|
|
699
511
|
}
|
|
700
512
|
|
|
701
513
|
// Flush remaining tool results
|
|
702
|
-
if (pendingToolResults.size > 0) {
|
|
703
|
-
const toolResultBlocks = Array.from(pendingToolResults.entries()).map(
|
|
514
|
+
if (state.pendingToolResults.size > 0) {
|
|
515
|
+
const toolResultBlocks = Array.from(state.pendingToolResults.entries()).map(
|
|
704
516
|
([toolUseId, result]) => ({
|
|
705
517
|
type: 'tool_result',
|
|
706
518
|
tool_use_id: toolUseId,
|
|
@@ -709,12 +521,17 @@ export async function runAgentLoopImpl(
|
|
|
709
521
|
...(result.contentBlocks ? { contentBlocks: result.contentBlocks } : {}),
|
|
710
522
|
}),
|
|
711
523
|
);
|
|
524
|
+
const toolResultMetadata = {
|
|
525
|
+
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
526
|
+
assistantMessageChannel: capturedTurnChannelContext.assistantMessageChannel,
|
|
527
|
+
};
|
|
712
528
|
conversationStore.addMessage(
|
|
713
529
|
ctx.conversationId,
|
|
714
530
|
'user',
|
|
715
531
|
JSON.stringify(toolResultBlocks),
|
|
532
|
+
toolResultMetadata,
|
|
716
533
|
);
|
|
717
|
-
pendingToolResults.clear();
|
|
534
|
+
state.pendingToolResults.clear();
|
|
718
535
|
}
|
|
719
536
|
|
|
720
537
|
// Reconstruct history
|
|
@@ -725,17 +542,22 @@ export async function runAgentLoopImpl(
|
|
|
725
542
|
});
|
|
726
543
|
|
|
727
544
|
const hasAssistantResponse = newMessages.some((msg) => msg.role === 'assistant');
|
|
728
|
-
if (!hasAssistantResponse && providerErrorUserMessage && !abortController.signal.aborted && !yieldedForHandoff) {
|
|
729
|
-
const
|
|
545
|
+
if (!hasAssistantResponse && state.providerErrorUserMessage && !abortController.signal.aborted && !yieldedForHandoff) {
|
|
546
|
+
const errChannelMeta = {
|
|
547
|
+
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
548
|
+
assistantMessageChannel: capturedTurnChannelContext.assistantMessageChannel,
|
|
549
|
+
};
|
|
550
|
+
const errorAssistantMessage = createAssistantMessage(state.providerErrorUserMessage);
|
|
730
551
|
conversationStore.addMessage(
|
|
731
552
|
ctx.conversationId,
|
|
732
553
|
'assistant',
|
|
733
554
|
JSON.stringify(errorAssistantMessage.content),
|
|
555
|
+
errChannelMeta,
|
|
734
556
|
);
|
|
735
557
|
newMessages.push(errorAssistantMessage);
|
|
736
558
|
onEvent({
|
|
737
559
|
type: 'assistant_text_delta',
|
|
738
|
-
text: providerErrorUserMessage,
|
|
560
|
+
text: state.providerErrorUserMessage,
|
|
739
561
|
sessionId: ctx.conversationId,
|
|
740
562
|
});
|
|
741
563
|
}
|
|
@@ -746,7 +568,7 @@ export async function runAgentLoopImpl(
|
|
|
746
568
|
stripDynamicProfile: (msgs) => stripDynamicProfileMessages(msgs, dynamicProfile.text),
|
|
747
569
|
});
|
|
748
570
|
|
|
749
|
-
emitUsage(ctx, exchangeInputTokens, exchangeOutputTokens, model, onEvent, 'main_agent', reqId);
|
|
571
|
+
emitUsage(ctx, state.exchangeInputTokens, state.exchangeOutputTokens, state.model, onEvent, 'main_agent', reqId);
|
|
750
572
|
|
|
751
573
|
void getHookManager().trigger('post-message', {
|
|
752
574
|
sessionId: ctx.conversationId,
|
|
@@ -754,12 +576,12 @@ export async function runAgentLoopImpl(
|
|
|
754
576
|
|
|
755
577
|
// Resolve attachments
|
|
756
578
|
const attachmentResult = await resolveAssistantAttachments(
|
|
757
|
-
accumulatedDirectives,
|
|
758
|
-
accumulatedToolContentBlocks,
|
|
759
|
-
directiveWarnings,
|
|
579
|
+
state.accumulatedDirectives,
|
|
580
|
+
state.accumulatedToolContentBlocks,
|
|
581
|
+
state.directiveWarnings,
|
|
760
582
|
ctx.workingDir,
|
|
761
583
|
async (filePath) => approveHostAttachmentRead(filePath, ctx.workingDir, ctx.prompter, ctx.conversationId, ctx.hasNoClient),
|
|
762
|
-
lastAssistantMessageId,
|
|
584
|
+
state.lastAssistantMessageId,
|
|
763
585
|
);
|
|
764
586
|
const { assistantAttachments, emittedAttachments } = attachmentResult;
|
|
765
587
|
|
|
@@ -804,9 +626,10 @@ export async function runAgentLoopImpl(
|
|
|
804
626
|
}
|
|
805
627
|
|
|
806
628
|
if (isFirstMessage) {
|
|
807
|
-
generateTitle(ctx, content, firstAssistantText
|
|
808
|
-
|
|
809
|
-
|
|
629
|
+
generateTitle(ctx, content, state.firstAssistantText, onEvent, abortController.signal)
|
|
630
|
+
.catch((err) => {
|
|
631
|
+
log.warn({ err, conversationId: ctx.conversationId }, 'Failed to generate conversation title (non-fatal, using default title)');
|
|
632
|
+
});
|
|
810
633
|
}
|
|
811
634
|
} catch (err) {
|
|
812
635
|
const errorCtx = { phase: 'agent_loop' as const, aborted: abortController.signal.aborted };
|
|
@@ -868,6 +691,9 @@ export async function runAgentLoopImpl(
|
|
|
868
691
|
ctx.currentActiveSurfaceId = undefined;
|
|
869
692
|
ctx.allowedToolNames = undefined;
|
|
870
693
|
ctx.preactivatedSkillIds = undefined;
|
|
694
|
+
// Channel command intents (e.g. Telegram /start) are single-turn metadata.
|
|
695
|
+
// Clear at turn end so they never leak into subsequent unrelated messages.
|
|
696
|
+
ctx.commandIntent = undefined;
|
|
871
697
|
|
|
872
698
|
if (userMessageId) {
|
|
873
699
|
consolidateAssistantMessages(ctx.conversationId, userMessageId);
|
|
@@ -883,13 +709,19 @@ async function generateTitle(
|
|
|
883
709
|
ctx: Pick<AgentLoopSessionContext, 'conversationId' | 'provider'>,
|
|
884
710
|
userMessage: string,
|
|
885
711
|
assistantResponse: string,
|
|
712
|
+
onEvent: (msg: ServerMessage) => void,
|
|
713
|
+
sessionSignal?: AbortSignal,
|
|
886
714
|
): Promise<void> {
|
|
715
|
+
const config = getConfig();
|
|
887
716
|
const prompt = `Generate a very short title for this conversation. Rules: at most 5 words, at most 40 characters, no quotes.\n\nUser: ${truncate(userMessage, 200, '')}\nAssistant: ${truncate(assistantResponse, 200, '')}`;
|
|
717
|
+
const signal = sessionSignal
|
|
718
|
+
? AbortSignal.any([sessionSignal, AbortSignal.timeout(10_000)])
|
|
719
|
+
: AbortSignal.timeout(10_000);
|
|
888
720
|
const response = await ctx.provider.sendMessage(
|
|
889
721
|
[{ role: 'user', content: [{ type: 'text', text: prompt }] }],
|
|
890
722
|
[],
|
|
891
723
|
undefined,
|
|
892
|
-
{ config: { max_tokens:
|
|
724
|
+
{ config: { max_tokens: config.daemon.titleGenerationMaxTokens }, signal },
|
|
893
725
|
);
|
|
894
726
|
|
|
895
727
|
const textBlock = response.content.find((b) => b.type === 'text');
|
|
@@ -898,7 +730,13 @@ async function generateTitle(
|
|
|
898
730
|
const words = title.split(/\s+/);
|
|
899
731
|
if (words.length > 5) title = words.slice(0, 5).join(' ');
|
|
900
732
|
if (title.length > 40) title = title.slice(0, 40).trimEnd();
|
|
733
|
+
if (!title) return;
|
|
901
734
|
conversationStore.updateConversationTitle(ctx.conversationId, title);
|
|
735
|
+
onEvent({
|
|
736
|
+
type: 'session_title_updated',
|
|
737
|
+
sessionId: ctx.conversationId,
|
|
738
|
+
title,
|
|
739
|
+
});
|
|
902
740
|
log.info({ conversationId: ctx.conversationId, title }, 'Auto-generated conversation title');
|
|
903
741
|
}
|
|
904
742
|
}
|
|
@@ -48,7 +48,7 @@ export function stripDynamicProfileMessages(messages: Message[], profileText: st
|
|
|
48
48
|
changed = true;
|
|
49
49
|
const stripped = nextText.replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
50
50
|
return stripped.length > 0 ? { ...block, text: stripped } : null;
|
|
51
|
-
}).filter((block): block is NonNullable<typeof block> => block
|
|
51
|
+
}).filter((block): block is NonNullable<typeof block> => block != null);
|
|
52
52
|
if (!changed) return messages;
|
|
53
53
|
// If stripping removed all content blocks, drop the message entirely
|
|
54
54
|
// to avoid sending an empty content array to the provider.
|
|
@@ -14,7 +14,7 @@ const log = getLogger('session-history');
|
|
|
14
14
|
|
|
15
15
|
function isUndoableUserMessage(message: Message): boolean {
|
|
16
16
|
if (message.role !== 'user') return false;
|
|
17
|
-
if (getSummaryFromContextMessage(message)
|
|
17
|
+
if (getSummaryFromContextMessage(message) != null) return false;
|
|
18
18
|
// A user message is undoable if it contains user-authored content (non-tool_result
|
|
19
19
|
// blocks). Messages that contain ONLY tool_result blocks (e.g. automated tool
|
|
20
20
|
// responses) are not undoable. Messages that have both tool_result and text blocks
|