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