@vellumai/assistant 0.9.0 → 0.10.0-staging.2
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/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { and, asc, desc, eq, isNotNull, like, sql } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
+
import type { ChannelId } from "../channels/types.js";
|
|
4
5
|
import { getDb } from "../memory/db-connection.js";
|
|
5
6
|
import {
|
|
6
7
|
assistantContactMetadata,
|
|
7
8
|
contactChannels,
|
|
8
9
|
contacts,
|
|
9
10
|
} from "../memory/schema.js";
|
|
11
|
+
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
10
12
|
import { emitContactChange } from "./contact-events.js";
|
|
11
13
|
import type {
|
|
12
14
|
AssistantContactMetadata,
|
|
@@ -27,6 +29,27 @@ function escapeLike(value: string): string {
|
|
|
27
29
|
return value.replace(/%/g, "").replace(/_/g, "");
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Find the first contact_channels row whose (type, address) matches.
|
|
34
|
+
* Uses COLLATE NOCASE to find legacy lowercased rows (pre-migration 290).
|
|
35
|
+
*/
|
|
36
|
+
function findConflictingChannel(
|
|
37
|
+
db: ReturnType<typeof getDb>,
|
|
38
|
+
type: string,
|
|
39
|
+
address: string,
|
|
40
|
+
) {
|
|
41
|
+
return db
|
|
42
|
+
.select()
|
|
43
|
+
.from(contactChannels)
|
|
44
|
+
.where(
|
|
45
|
+
and(
|
|
46
|
+
eq(contactChannels.type, type),
|
|
47
|
+
sql`${contactChannels.address} = ${address} COLLATE NOCASE`,
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
.get();
|
|
51
|
+
}
|
|
52
|
+
|
|
30
53
|
/**
|
|
31
54
|
* Pure slug transform applied to a display name. No DB lookup, no collision
|
|
32
55
|
* handling — callers that need a collision-free filename should use
|
|
@@ -93,7 +116,6 @@ function parseChannel(
|
|
|
93
116
|
type: row.type,
|
|
94
117
|
address: row.address,
|
|
95
118
|
isPrimary: row.isPrimary,
|
|
96
|
-
externalUserId: row.externalUserId,
|
|
97
119
|
externalChatId: row.externalChatId,
|
|
98
120
|
status: row.status as ContactChannel["status"],
|
|
99
121
|
policy: row.policy as ContactChannel["policy"],
|
|
@@ -139,7 +161,6 @@ interface SyncChannelData {
|
|
|
139
161
|
type: string;
|
|
140
162
|
address: string;
|
|
141
163
|
isPrimary?: boolean;
|
|
142
|
-
externalUserId?: string | null;
|
|
143
164
|
externalChatId?: string | null;
|
|
144
165
|
status?: ChannelStatus;
|
|
145
166
|
policy?: ChannelPolicy;
|
|
@@ -152,15 +173,7 @@ interface SyncChannelData {
|
|
|
152
173
|
|
|
153
174
|
// ── CRUD ─────────────────────────────────────────────────────────────
|
|
154
175
|
|
|
155
|
-
/** Retrieve a contact by ID.
|
|
156
|
-
* Used by functions that have already resolved identity through channel lookups. */
|
|
157
|
-
export function getContactInternal(id: string): ContactWithChannels | null {
|
|
158
|
-
const db = getDb();
|
|
159
|
-
const row = db.select().from(contacts).where(eq(contacts.id, id)).get();
|
|
160
|
-
if (!row) return null;
|
|
161
|
-
return withChannels(parseContact(row));
|
|
162
|
-
}
|
|
163
|
-
|
|
176
|
+
/** Retrieve a contact by ID. */
|
|
164
177
|
export function getContact(id: string): ContactWithChannels | null {
|
|
165
178
|
const db = getDb();
|
|
166
179
|
const row = db.select().from(contacts).where(eq(contacts.id, id)).get();
|
|
@@ -168,6 +181,9 @@ export function getContact(id: string): ContactWithChannels | null {
|
|
|
168
181
|
return withChannels(parseContact(row));
|
|
169
182
|
}
|
|
170
183
|
|
|
184
|
+
/** @deprecated Use {@link getContact} directly. */
|
|
185
|
+
export const getContactInternal = getContact;
|
|
186
|
+
|
|
171
187
|
/**
|
|
172
188
|
* Look up a single contact channel by its primary key.
|
|
173
189
|
* Returns the parsed channel row, or null if it does not exist.
|
|
@@ -199,6 +215,15 @@ export function upsertContact(params: {
|
|
|
199
215
|
const db = getDb();
|
|
200
216
|
const now = Date.now();
|
|
201
217
|
|
|
218
|
+
// Canonicalize all channel addresses up front so every downstream path
|
|
219
|
+
// (lookups, inserts, conflict checks) uses the canonical form.
|
|
220
|
+
const canonicalChannels = params.channels?.map((ch) => ({
|
|
221
|
+
...ch,
|
|
222
|
+
address:
|
|
223
|
+
canonicalizeInboundIdentity(ch.type as ChannelId, ch.address) ??
|
|
224
|
+
ch.address,
|
|
225
|
+
}));
|
|
226
|
+
|
|
202
227
|
let contactId = params.id;
|
|
203
228
|
|
|
204
229
|
// If an ID is provided, check if the contact exists for update
|
|
@@ -226,10 +251,10 @@ export function upsertContact(params: {
|
|
|
226
251
|
.where(eq(contacts.id, contactId))
|
|
227
252
|
.run();
|
|
228
253
|
|
|
229
|
-
if (
|
|
254
|
+
if (canonicalChannels) {
|
|
230
255
|
syncChannels(
|
|
231
256
|
contactId,
|
|
232
|
-
|
|
257
|
+
canonicalChannels,
|
|
233
258
|
now,
|
|
234
259
|
params.reassignConflictingChannels,
|
|
235
260
|
);
|
|
@@ -240,20 +265,10 @@ export function upsertContact(params: {
|
|
|
240
265
|
}
|
|
241
266
|
}
|
|
242
267
|
|
|
243
|
-
// Try to find by channel
|
|
244
|
-
if (!contactId &&
|
|
245
|
-
for (const ch of
|
|
246
|
-
|
|
247
|
-
const existingChannel = db
|
|
248
|
-
.select()
|
|
249
|
-
.from(contactChannels)
|
|
250
|
-
.where(
|
|
251
|
-
and(
|
|
252
|
-
eq(contactChannels.type, ch.type),
|
|
253
|
-
eq(contactChannels.address, ch.address.toLowerCase()),
|
|
254
|
-
),
|
|
255
|
-
)
|
|
256
|
-
.get();
|
|
268
|
+
// Try to find by channel canonical identity to avoid duplicates
|
|
269
|
+
if (!contactId && canonicalChannels && canonicalChannels.length > 0) {
|
|
270
|
+
for (const ch of canonicalChannels) {
|
|
271
|
+
const existingChannel = findConflictingChannel(db, ch.type, ch.address);
|
|
257
272
|
|
|
258
273
|
if (existingChannel) {
|
|
259
274
|
contactId = existingChannel.contactId;
|
|
@@ -274,7 +289,7 @@ export function upsertContact(params: {
|
|
|
274
289
|
.where(eq(contacts.id, contactId))
|
|
275
290
|
.run();
|
|
276
291
|
|
|
277
|
-
syncChannels(contactId,
|
|
292
|
+
syncChannels(contactId, canonicalChannels, now);
|
|
278
293
|
emitContactChange();
|
|
279
294
|
return { ...getContactInternal(contactId)!, created: false };
|
|
280
295
|
}
|
|
@@ -318,10 +333,10 @@ export function upsertContact(params: {
|
|
|
318
333
|
})
|
|
319
334
|
.run();
|
|
320
335
|
|
|
321
|
-
if (
|
|
336
|
+
if (canonicalChannels) {
|
|
322
337
|
syncChannels(
|
|
323
338
|
contactId,
|
|
324
|
-
|
|
339
|
+
canonicalChannels,
|
|
325
340
|
now,
|
|
326
341
|
params.reassignConflictingChannels,
|
|
327
342
|
);
|
|
@@ -345,9 +360,8 @@ function syncChannels(
|
|
|
345
360
|
const db = getDb();
|
|
346
361
|
|
|
347
362
|
for (const ch of channels) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
// Check if this channel already exists for this contact
|
|
363
|
+
// Match by (type, address) — the canonical identity for all channel types.
|
|
364
|
+
// COLLATE NOCASE catches legacy rows that were lowercased by old write paths.
|
|
351
365
|
const existing = db
|
|
352
366
|
.select()
|
|
353
367
|
.from(contactChannels)
|
|
@@ -355,7 +369,7 @@ function syncChannels(
|
|
|
355
369
|
and(
|
|
356
370
|
eq(contactChannels.contactId, contactId),
|
|
357
371
|
eq(contactChannels.type, ch.type),
|
|
358
|
-
|
|
372
|
+
sql`${contactChannels.address} = ${ch.address} COLLATE NOCASE`,
|
|
359
373
|
),
|
|
360
374
|
)
|
|
361
375
|
.get();
|
|
@@ -367,9 +381,9 @@ function syncChannels(
|
|
|
367
381
|
const isBlocked = existing.status === "blocked";
|
|
368
382
|
|
|
369
383
|
const updateSet: Record<string, unknown> = {};
|
|
384
|
+
// Self-heal legacy lowercased addresses to canonical form.
|
|
385
|
+
if (existing.address !== ch.address) updateSet.address = ch.address;
|
|
370
386
|
if (ch.isPrimary !== undefined) updateSet.isPrimary = ch.isPrimary;
|
|
371
|
-
if (ch.externalUserId !== undefined)
|
|
372
|
-
updateSet.externalUserId = ch.externalUserId;
|
|
373
387
|
if (ch.externalChatId !== undefined)
|
|
374
388
|
updateSet.externalChatId = ch.externalChatId;
|
|
375
389
|
if (!isBlocked) {
|
|
@@ -394,17 +408,8 @@ function syncChannels(
|
|
|
394
408
|
continue;
|
|
395
409
|
}
|
|
396
410
|
|
|
397
|
-
// Check if this channel
|
|
398
|
-
const conflicting = db
|
|
399
|
-
.select()
|
|
400
|
-
.from(contactChannels)
|
|
401
|
-
.where(
|
|
402
|
-
and(
|
|
403
|
-
eq(contactChannels.type, ch.type),
|
|
404
|
-
eq(contactChannels.address, normalizedAddress),
|
|
405
|
-
),
|
|
406
|
-
)
|
|
407
|
-
.get();
|
|
411
|
+
// Check if this channel's canonical identity conflicts with another contact.
|
|
412
|
+
const conflicting = findConflictingChannel(db, ch.type, ch.address);
|
|
408
413
|
|
|
409
414
|
if (conflicting) {
|
|
410
415
|
if (reassignConflicting) {
|
|
@@ -419,8 +424,6 @@ function syncChannels(
|
|
|
419
424
|
contactId,
|
|
420
425
|
updatedAt: now,
|
|
421
426
|
};
|
|
422
|
-
if (ch.externalUserId !== undefined)
|
|
423
|
-
reassignSet.externalUserId = ch.externalUserId;
|
|
424
427
|
if (ch.externalChatId !== undefined)
|
|
425
428
|
reassignSet.externalChatId = ch.externalChatId;
|
|
426
429
|
if (!isBlocked) {
|
|
@@ -451,9 +454,8 @@ function syncChannels(
|
|
|
451
454
|
id: uuid(),
|
|
452
455
|
contactId,
|
|
453
456
|
type: ch.type,
|
|
454
|
-
address:
|
|
457
|
+
address: ch.address,
|
|
455
458
|
isPrimary: ch.isPrimary ?? false,
|
|
456
|
-
externalUserId: ch.externalUserId ?? null,
|
|
457
459
|
externalChatId: ch.externalChatId ?? null,
|
|
458
460
|
status: ch.status ?? "unverified",
|
|
459
461
|
policy: ch.policy ?? "allow",
|
|
@@ -480,8 +482,8 @@ export function searchContacts(params: {
|
|
|
480
482
|
|
|
481
483
|
// Search by channel address first (exact or partial match)
|
|
482
484
|
if (params.channelAddress) {
|
|
483
|
-
const
|
|
484
|
-
if (!
|
|
485
|
+
const escapedAddress = escapeLike(params.channelAddress);
|
|
486
|
+
if (!escapedAddress) return [];
|
|
485
487
|
const channelRows = db
|
|
486
488
|
.select({ contactId: contactChannels.contactId })
|
|
487
489
|
.from(contactChannels)
|
|
@@ -490,9 +492,9 @@ export function searchContacts(params: {
|
|
|
490
492
|
params.channelType
|
|
491
493
|
? and(
|
|
492
494
|
eq(contactChannels.type, params.channelType),
|
|
493
|
-
like(contactChannels.address, `%${
|
|
495
|
+
like(contactChannels.address, `%${escapedAddress}%`),
|
|
494
496
|
)
|
|
495
|
-
: and(like(contactChannels.address, `%${
|
|
497
|
+
: and(like(contactChannels.address, `%${escapedAddress}%`)),
|
|
496
498
|
)
|
|
497
499
|
.all();
|
|
498
500
|
|
|
@@ -675,6 +677,8 @@ export function mergeContacts(
|
|
|
675
677
|
.all();
|
|
676
678
|
|
|
677
679
|
for (const ch of donorChannels) {
|
|
680
|
+
// COLLATE NOCASE catches legacy lowercased rows so we don't try to
|
|
681
|
+
// move a donor channel that collides with an existing survivor channel.
|
|
678
682
|
const exists = tx
|
|
679
683
|
.select()
|
|
680
684
|
.from(contactChannels)
|
|
@@ -682,7 +686,7 @@ export function mergeContacts(
|
|
|
682
686
|
and(
|
|
683
687
|
eq(contactChannels.contactId, keepId),
|
|
684
688
|
eq(contactChannels.type, ch.type),
|
|
685
|
-
|
|
689
|
+
sql`${contactChannels.address} = ${ch.address} COLLATE NOCASE`,
|
|
686
690
|
),
|
|
687
691
|
)
|
|
688
692
|
.get();
|
|
@@ -705,11 +709,15 @@ export function mergeContacts(
|
|
|
705
709
|
|
|
706
710
|
/**
|
|
707
711
|
* Find a contact by a specific channel address. Returns null if not found.
|
|
712
|
+
* Canonicalizes the address before querying. Uses COLLATE NOCASE to match
|
|
713
|
+
* legacy lowercased rows that migration 290 couldn't restore.
|
|
708
714
|
*/
|
|
709
715
|
export function findContactByAddress(
|
|
710
716
|
type: string,
|
|
711
717
|
address: string,
|
|
712
718
|
): ContactWithChannels | null {
|
|
719
|
+
const canonical =
|
|
720
|
+
canonicalizeInboundIdentity(type as ChannelId, address) ?? address;
|
|
713
721
|
const db = getDb();
|
|
714
722
|
const channel = db
|
|
715
723
|
.select()
|
|
@@ -717,31 +725,7 @@ export function findContactByAddress(
|
|
|
717
725
|
.where(
|
|
718
726
|
and(
|
|
719
727
|
eq(contactChannels.type, type),
|
|
720
|
-
|
|
721
|
-
),
|
|
722
|
-
)
|
|
723
|
-
.get();
|
|
724
|
-
|
|
725
|
-
if (!channel) return null;
|
|
726
|
-
return getContactInternal(channel.contactId);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Find a contact by channel external user ID. This is the key lookup for trust
|
|
731
|
-
* resolution — maps a channel-native sender identity to its parent Contact.
|
|
732
|
-
*/
|
|
733
|
-
export function findContactByChannelExternalId(
|
|
734
|
-
channelType: string,
|
|
735
|
-
externalUserId: string,
|
|
736
|
-
): ContactWithChannels | null {
|
|
737
|
-
const db = getDb();
|
|
738
|
-
const channel = db
|
|
739
|
-
.select()
|
|
740
|
-
.from(contactChannels)
|
|
741
|
-
.where(
|
|
742
|
-
and(
|
|
743
|
-
eq(contactChannels.type, channelType),
|
|
744
|
-
eq(contactChannels.externalUserId, externalUserId),
|
|
728
|
+
sql`${contactChannels.address} = ${canonical} COLLATE NOCASE`,
|
|
745
729
|
),
|
|
746
730
|
)
|
|
747
731
|
.get();
|
|
@@ -751,8 +735,9 @@ export function findContactByChannelExternalId(
|
|
|
751
735
|
}
|
|
752
736
|
|
|
753
737
|
/**
|
|
754
|
-
* Find a contact by channel external chat ID.
|
|
755
|
-
*
|
|
738
|
+
* Find a contact by channel external chat ID. Fallback for callers that only
|
|
739
|
+
* have a chat ID (no user-level address) — matches by (type, externalChatId).
|
|
740
|
+
* No unique constraint exists on externalChatId, so ORDER BY is needed.
|
|
756
741
|
*/
|
|
757
742
|
function findContactByChannelExternalChatId(
|
|
758
743
|
channelType: string,
|
|
@@ -768,30 +753,40 @@ function findContactByChannelExternalChatId(
|
|
|
768
753
|
eq(contactChannels.externalChatId, externalChatId),
|
|
769
754
|
),
|
|
770
755
|
)
|
|
756
|
+
.orderBy(
|
|
757
|
+
sql`CASE ${contactChannels.status}
|
|
758
|
+
WHEN 'active' THEN 0
|
|
759
|
+
WHEN 'unverified' THEN 1
|
|
760
|
+
ELSE 2
|
|
761
|
+
END`,
|
|
762
|
+
desc(contactChannels.updatedAt),
|
|
763
|
+
)
|
|
771
764
|
.get();
|
|
772
765
|
if (!channel) return null;
|
|
773
766
|
return getContactInternal(channel.contactId);
|
|
774
767
|
}
|
|
775
768
|
|
|
776
769
|
/**
|
|
777
|
-
* Find a contact and matching channel by trying
|
|
770
|
+
* Find a contact and matching channel by trying address first, then
|
|
778
771
|
* falling back to externalChatId. Mirrors the findMember lookup strategy.
|
|
779
772
|
*/
|
|
780
773
|
export function findContactChannel(params: {
|
|
781
774
|
channelType: string;
|
|
782
|
-
|
|
775
|
+
address?: string;
|
|
783
776
|
externalChatId?: string;
|
|
784
777
|
}): { contact: ContactWithChannels; channel: ContactChannel } | null {
|
|
785
|
-
if (params.
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
778
|
+
if (params.address) {
|
|
779
|
+
const canonical =
|
|
780
|
+
canonicalizeInboundIdentity(
|
|
781
|
+
params.channelType as ChannelId,
|
|
782
|
+
params.address,
|
|
783
|
+
) ?? params.address;
|
|
784
|
+
const contact = findContactByAddress(params.channelType, canonical);
|
|
790
785
|
if (contact) {
|
|
791
786
|
const ch = contact.channels.find(
|
|
792
787
|
(c) =>
|
|
793
788
|
c.type === params.channelType &&
|
|
794
|
-
c.
|
|
789
|
+
c.address.toLowerCase() === canonical.toLowerCase(),
|
|
795
790
|
);
|
|
796
791
|
if (ch) return { contact, channel: ch };
|
|
797
792
|
}
|
|
@@ -952,8 +947,6 @@ export function updateContactPrincipalAndChannel(
|
|
|
952
947
|
): boolean {
|
|
953
948
|
const db = getDb();
|
|
954
949
|
const now = Date.now();
|
|
955
|
-
const normalizedAddress = newPrincipalId.toLowerCase();
|
|
956
|
-
|
|
957
950
|
// Look up the channel we're about to update so we know its type.
|
|
958
951
|
const channel = db
|
|
959
952
|
.select()
|
|
@@ -962,17 +955,8 @@ export function updateContactPrincipalAndChannel(
|
|
|
962
955
|
.get();
|
|
963
956
|
if (!channel) return false;
|
|
964
957
|
|
|
965
|
-
// Guard: check if another channel row
|
|
966
|
-
const conflicting = db
|
|
967
|
-
.select()
|
|
968
|
-
.from(contactChannels)
|
|
969
|
-
.where(
|
|
970
|
-
and(
|
|
971
|
-
eq(contactChannels.type, channel.type),
|
|
972
|
-
eq(contactChannels.address, normalizedAddress),
|
|
973
|
-
),
|
|
974
|
-
)
|
|
975
|
-
.get();
|
|
958
|
+
// Guard: check if another channel row holds this canonical identity.
|
|
959
|
+
const conflicting = findConflictingChannel(db, channel.type, newPrincipalId);
|
|
976
960
|
|
|
977
961
|
if (conflicting && conflicting.id !== channelId) {
|
|
978
962
|
return false;
|
|
@@ -986,8 +970,7 @@ export function updateContactPrincipalAndChannel(
|
|
|
986
970
|
|
|
987
971
|
db.update(contactChannels)
|
|
988
972
|
.set({
|
|
989
|
-
|
|
990
|
-
address: normalizedAddress,
|
|
973
|
+
address: newPrincipalId,
|
|
991
974
|
updatedAt: now,
|
|
992
975
|
})
|
|
993
976
|
.where(eq(contactChannels.id, channelId))
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* identity and access-control state.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { ChannelId } from "../channels/types.js";
|
|
10
|
-
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
11
9
|
import { emitContactChange } from "./contact-events.js";
|
|
12
10
|
import {
|
|
13
11
|
findContactChannel,
|
|
@@ -25,7 +23,6 @@ import type {
|
|
|
25
23
|
ContactWriteResult,
|
|
26
24
|
} from "./types.js";
|
|
27
25
|
|
|
28
|
-
|
|
29
26
|
// ── Guardian operations ──────────────────────────────────────────────
|
|
30
27
|
|
|
31
28
|
/**
|
|
@@ -68,12 +65,7 @@ export function upsertContactChannel(params: {
|
|
|
68
65
|
let address: string;
|
|
69
66
|
|
|
70
67
|
if (params.externalUserId) {
|
|
71
|
-
|
|
72
|
-
canonicalizeInboundIdentity(
|
|
73
|
-
params.sourceChannel as ChannelId,
|
|
74
|
-
params.externalUserId,
|
|
75
|
-
) ?? params.externalUserId;
|
|
76
|
-
address = canonical;
|
|
68
|
+
address = params.externalUserId;
|
|
77
69
|
} else if (params.externalChatId) {
|
|
78
70
|
address = params.externalChatId;
|
|
79
71
|
} else {
|
|
@@ -81,11 +73,11 @@ export function upsertContactChannel(params: {
|
|
|
81
73
|
return null;
|
|
82
74
|
}
|
|
83
75
|
|
|
84
|
-
let displayName = params.displayName ??
|
|
76
|
+
let displayName = params.displayName ?? address;
|
|
85
77
|
|
|
86
78
|
// When binding a channel to a specific contact (invite redemption), preserve
|
|
87
|
-
// the target contact's curated displayName
|
|
88
|
-
//
|
|
79
|
+
// the target contact's curated displayName instead of overwriting it
|
|
80
|
+
// with the raw platform identity.
|
|
89
81
|
if (params.contactId) {
|
|
90
82
|
const targetContact = getContact(params.contactId);
|
|
91
83
|
if (targetContact?.displayName?.trim().length) {
|
|
@@ -93,13 +85,6 @@ export function upsertContactChannel(params: {
|
|
|
93
85
|
}
|
|
94
86
|
}
|
|
95
87
|
|
|
96
|
-
const canonicalId = params.externalUserId
|
|
97
|
-
? (canonicalizeInboundIdentity(
|
|
98
|
-
params.sourceChannel as ChannelId,
|
|
99
|
-
params.externalUserId,
|
|
100
|
-
) ?? params.externalUserId)
|
|
101
|
-
: null;
|
|
102
|
-
|
|
103
88
|
upsertContact({
|
|
104
89
|
id: params.contactId,
|
|
105
90
|
displayName,
|
|
@@ -108,7 +93,6 @@ export function upsertContactChannel(params: {
|
|
|
108
93
|
{
|
|
109
94
|
type: params.sourceChannel,
|
|
110
95
|
address,
|
|
111
|
-
externalUserId: canonicalId,
|
|
112
96
|
externalChatId: params.externalChatId ?? null,
|
|
113
97
|
status: (params.status as ChannelStatus) ?? undefined,
|
|
114
98
|
policy: (params.policy as ChannelPolicy) ?? undefined,
|
|
@@ -133,7 +117,7 @@ export function upsertContactChannel(params: {
|
|
|
133
117
|
|
|
134
118
|
const contactResult = findContactChannel({
|
|
135
119
|
channelType: params.sourceChannel,
|
|
136
|
-
|
|
120
|
+
address,
|
|
137
121
|
externalChatId: params.externalChatId,
|
|
138
122
|
});
|
|
139
123
|
|
|
@@ -174,4 +158,3 @@ export function revokeMember(
|
|
|
174
158
|
emitContactChange();
|
|
175
159
|
return { contact, channel: updatedChannel };
|
|
176
160
|
}
|
|
177
|
-
|
package/src/contacts/types.ts
CHANGED
|
@@ -62,7 +62,6 @@ export interface ContactChannel {
|
|
|
62
62
|
type: string; // 'email' | 'slack' | 'whatsapp' | 'phone' | etc.
|
|
63
63
|
address: string;
|
|
64
64
|
isPrimary: boolean;
|
|
65
|
-
externalUserId: string | null;
|
|
66
65
|
externalChatId: string | null;
|
|
67
66
|
status: ChannelStatus;
|
|
68
67
|
policy: ChannelPolicy;
|
package/src/context/compactor.ts
CHANGED
|
@@ -37,10 +37,8 @@ import type {
|
|
|
37
37
|
ProviderResponse,
|
|
38
38
|
ToolDefinition,
|
|
39
39
|
} from "../providers/types.js";
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
type TrustClass,
|
|
43
|
-
} from "../runtime/actor-trust-resolver.js";
|
|
40
|
+
import { type TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
41
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
44
42
|
import { getLogger } from "../util/logger.js";
|
|
45
43
|
import { stripInjectionsForCompaction } from "./strip-injections.js";
|
|
46
44
|
import {
|
|
@@ -387,7 +385,7 @@ export function collectImageManifest(
|
|
|
387
385
|
actorTrustClass?: TrustClass,
|
|
388
386
|
): ManifestEntry[] {
|
|
389
387
|
const allRows = getMessages(conversationId);
|
|
390
|
-
const rows =
|
|
388
|
+
const rows = !resolveCapabilities(actorTrustClass).canAccessMemory
|
|
391
389
|
? filterMessagesForUntrustedActor(allRows)
|
|
392
390
|
: allRows;
|
|
393
391
|
const entries: ManifestEntry[] = [];
|
|
@@ -847,6 +845,69 @@ function buildCompactionRequest(
|
|
|
847
845
|
return [...stripHistoricalWebSearchResults(history).messages, instruction];
|
|
848
846
|
}
|
|
849
847
|
|
|
848
|
+
// Token headroom a compaction summary call reserves on top of its history: room
|
|
849
|
+
// for the instruction message and the summary the model emits, so a request
|
|
850
|
+
// front-truncated to `compactionPrefixBudget` still fits the context window.
|
|
851
|
+
const COMPACTION_INSTRUCTION_TOKEN_RESERVE = 800;
|
|
852
|
+
const COMPACTION_OUTPUT_BUDGET_RATIO = 0.15;
|
|
853
|
+
|
|
854
|
+
// Largest history (in estimated tokens) a compaction summary call may carry
|
|
855
|
+
// while leaving room for the instruction and the emitted summary within
|
|
856
|
+
// `maxInputTokens`.
|
|
857
|
+
function compactionPrefixBudget(maxInputTokens: number): number {
|
|
858
|
+
return (
|
|
859
|
+
maxInputTokens -
|
|
860
|
+
COMPACTION_INSTRUCTION_TOKEN_RESERVE -
|
|
861
|
+
Math.floor(maxInputTokens * COMPACTION_OUTPUT_BUDGET_RATIO)
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Front-truncate the history handed to a compaction summary call so the call
|
|
866
|
+
// itself fits the context window. Drops messages from the front until the
|
|
867
|
+
// estimated prompt fits `budgetTokens`, then prepends a marker noting how many
|
|
868
|
+
// were dropped so the model knows the summary covers only the visible portion.
|
|
869
|
+
// Returns the input untouched when it already fits or holds a single message —
|
|
870
|
+
// so a below-budget call keeps its prefix byte-aligned with the agent's warm
|
|
871
|
+
// cache and pays no extra cache write.
|
|
872
|
+
function truncateHistoryToBudget(args: {
|
|
873
|
+
messages: Message[];
|
|
874
|
+
systemPrompt: string;
|
|
875
|
+
budgetTokens: number;
|
|
876
|
+
providerName: string;
|
|
877
|
+
}): Message[] {
|
|
878
|
+
const { messages, systemPrompt, budgetTokens, providerName } = args;
|
|
879
|
+
let estimate = estimatePromptTokens(messages, systemPrompt, { providerName });
|
|
880
|
+
if (estimate <= budgetTokens || messages.length <= 1) {
|
|
881
|
+
return messages;
|
|
882
|
+
}
|
|
883
|
+
let dropCount = 0;
|
|
884
|
+
while (estimate > budgetTokens && dropCount < messages.length - 1) {
|
|
885
|
+
dropCount++;
|
|
886
|
+
estimate = estimatePromptTokens(messages.slice(dropCount), systemPrompt, {
|
|
887
|
+
providerName,
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
if (dropCount === 0) {
|
|
891
|
+
return messages;
|
|
892
|
+
}
|
|
893
|
+
log.info(
|
|
894
|
+
{ dropCount, budgetTokens, totalMessages: messages.length },
|
|
895
|
+
"Compaction summary input exceeds context window — truncating from front",
|
|
896
|
+
);
|
|
897
|
+
return [
|
|
898
|
+
{
|
|
899
|
+
role: "user" as const,
|
|
900
|
+
content: [
|
|
901
|
+
{
|
|
902
|
+
type: "text" as const,
|
|
903
|
+
text: `[${dropCount} earlier messages truncated — summary covers only the visible portion]`,
|
|
904
|
+
},
|
|
905
|
+
],
|
|
906
|
+
},
|
|
907
|
+
...messages.slice(dropCount),
|
|
908
|
+
];
|
|
909
|
+
}
|
|
910
|
+
|
|
850
911
|
export async function runAssistantDrivenCompaction(
|
|
851
912
|
args: CompactionRunArgs,
|
|
852
913
|
): Promise<CompactionRunResult> {
|
|
@@ -881,7 +942,19 @@ export async function runAssistantDrivenCompaction(
|
|
|
881
942
|
args.targetTokens,
|
|
882
943
|
);
|
|
883
944
|
|
|
884
|
-
|
|
945
|
+
// Bound the summary call's own input to the context window. With no tool
|
|
946
|
+
// pair to anchor an emergency split, an overflow recovery routes the full
|
|
947
|
+
// history straight here, so the summary call must front-truncate itself or
|
|
948
|
+
// it overflows in turn. `args.messages` stays intact for tail resolution
|
|
949
|
+
// below — only the outbound request is truncated. A below-budget history is
|
|
950
|
+
// returned untouched, keeping the prefix aligned with the agent's warm cache.
|
|
951
|
+
const summaryHistory = truncateHistoryToBudget({
|
|
952
|
+
messages: args.messages,
|
|
953
|
+
systemPrompt: args.systemPrompt,
|
|
954
|
+
budgetTokens: compactionPrefixBudget(args.maxInputTokens),
|
|
955
|
+
providerName: args.provider.tokenEstimationProvider ?? args.provider.name,
|
|
956
|
+
});
|
|
957
|
+
const requestMessages = buildCompactionRequest(summaryHistory, instruction);
|
|
885
958
|
|
|
886
959
|
let response: ProviderResponse;
|
|
887
960
|
try {
|
|
@@ -1132,8 +1205,8 @@ export async function runAssistantDrivenCompaction(
|
|
|
1132
1205
|
// from the moment of capture — keeping them would (a) waste tokens on
|
|
1133
1206
|
// outdated content, (b) duplicate against the freshly re-injected blocks
|
|
1134
1207
|
// the next turn produces, and (c) leak `<system_reminder>` text the model
|
|
1135
|
-
// is not supposed to see in history. `<turn_context>`
|
|
1136
|
-
//
|
|
1208
|
+
// is not supposed to see in history. `<turn_context>` is intentionally
|
|
1209
|
+
// preserved by `RUNTIME_INJECTION_PREFIXES`.
|
|
1137
1210
|
const tailMessages = stripInjectionsForCompaction(
|
|
1138
1211
|
args.messages.slice(tailIndex),
|
|
1139
1212
|
);
|
|
@@ -1296,55 +1369,16 @@ export async function runEmergencyCompaction(
|
|
|
1296
1369
|
const keptTail = stripInjectionsForCompaction(
|
|
1297
1370
|
args.messages.slice(splitIndex),
|
|
1298
1371
|
);
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
//
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
const prefixBudget = args.maxInputTokens - instructionBudget - outputBudget;
|
|
1307
|
-
|
|
1308
|
-
let prefixEstimate = estimatePromptTokens(prefix, args.systemPrompt, {
|
|
1372
|
+
// Bound the prefix to the context window so the summary call fits, reserving
|
|
1373
|
+
// budget for the instruction message and the emitted summary. Truncates from
|
|
1374
|
+
// the front, keeping the recent portion the summary most needs.
|
|
1375
|
+
const prefix = truncateHistoryToBudget({
|
|
1376
|
+
messages: args.messages.slice(0, splitIndex),
|
|
1377
|
+
systemPrompt: args.systemPrompt,
|
|
1378
|
+
budgetTokens: compactionPrefixBudget(args.maxInputTokens),
|
|
1309
1379
|
providerName: args.provider.tokenEstimationProvider ?? args.provider.name,
|
|
1310
1380
|
});
|
|
1311
1381
|
|
|
1312
|
-
if (prefixEstimate > prefixBudget && prefix.length > 1) {
|
|
1313
|
-
log.info(
|
|
1314
|
-
{
|
|
1315
|
-
prefixEstimate,
|
|
1316
|
-
prefixBudget,
|
|
1317
|
-
prefixMessages: prefix.length,
|
|
1318
|
-
},
|
|
1319
|
-
"Emergency compaction: prefix exceeds context window — truncating from front",
|
|
1320
|
-
);
|
|
1321
|
-
// Drop messages from the front until we fit. Keep at least the first
|
|
1322
|
-
// message (may be an existing summary) and try to preserve recent context.
|
|
1323
|
-
let dropCount = 0;
|
|
1324
|
-
while (prefixEstimate > prefixBudget && dropCount < prefix.length - 1) {
|
|
1325
|
-
dropCount++;
|
|
1326
|
-
const truncated = prefix.slice(dropCount);
|
|
1327
|
-
prefixEstimate = estimatePromptTokens(truncated, args.systemPrompt, {
|
|
1328
|
-
providerName:
|
|
1329
|
-
args.provider.tokenEstimationProvider ?? args.provider.name,
|
|
1330
|
-
});
|
|
1331
|
-
}
|
|
1332
|
-
if (dropCount > 0) {
|
|
1333
|
-
prefix = [
|
|
1334
|
-
{
|
|
1335
|
-
role: "user" as const,
|
|
1336
|
-
content: [
|
|
1337
|
-
{
|
|
1338
|
-
type: "text" as const,
|
|
1339
|
-
text: `[${dropCount} earlier messages truncated — summary covers only the visible portion]`,
|
|
1340
|
-
},
|
|
1341
|
-
],
|
|
1342
|
-
},
|
|
1343
|
-
...prefix.slice(dropCount),
|
|
1344
|
-
];
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
1382
|
const instruction: Message = {
|
|
1349
1383
|
role: "user",
|
|
1350
1384
|
content: [{ type: "text", text: EMERGENCY_COMPACTION_PROMPT }],
|