@vellumai/assistant 0.9.1-staging.1 → 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/docs/activation-funnel-telemetry.md +24 -18
- 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 +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- 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 +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- 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__/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 +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- 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-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-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- 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 +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- 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__/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 +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- 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__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -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__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- 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 +150 -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__/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__/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-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -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 +33 -33
- 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 +26 -0
- package/src/api/responses/home.ts +26 -0
- 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 +181 -78
- 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-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- 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 +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- 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/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -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/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/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- 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-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -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 +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.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/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/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 +34 -0
- 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/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 +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- 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 +10 -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 +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- 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/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/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- 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 +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- 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/index.ts +27 -0
- 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 +35 -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 +75 -7
- 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 +37 -4
- 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/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- 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/__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 +49 -21
- 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/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- 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 +6 -22
- 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 +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- 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-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- 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/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- 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 +40 -7
- 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 +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- 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 +11 -4
- 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/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- 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.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- 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 -3
- package/src/workflows/run-manager.ts +64 -0
- 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/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
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 {
|
|
@@ -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 }],
|
|
@@ -28,6 +28,33 @@ import type { Message } from "../providers/types.js";
|
|
|
28
28
|
*/
|
|
29
29
|
export type InjectionMatcher = string | { prefix: string; suffix: string };
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Whether an injected text block matches any of the given matchers. A plain
|
|
33
|
+
* string matches by prefix; a `{ prefix, suffix }` wrapper requires both ends
|
|
34
|
+
* so user-authored text merely opening with an injection-like tag is kept.
|
|
35
|
+
*/
|
|
36
|
+
function textBlockMatchesInjection(
|
|
37
|
+
text: string,
|
|
38
|
+
matchers: InjectionMatcher[],
|
|
39
|
+
): boolean {
|
|
40
|
+
return matchers.some((m) =>
|
|
41
|
+
typeof m === "string"
|
|
42
|
+
? text.startsWith(m)
|
|
43
|
+
: text.startsWith(m.prefix) && text.endsWith(m.suffix),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Drop the matching injection text blocks from a single content array. */
|
|
48
|
+
function filterInjectionBlocks(
|
|
49
|
+
content: Message["content"],
|
|
50
|
+
matchers: InjectionMatcher[],
|
|
51
|
+
): Message["content"] {
|
|
52
|
+
return content.filter(
|
|
53
|
+
(block) =>
|
|
54
|
+
block.type !== "text" || !textBlockMatchesInjection(block.text, matchers),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
31
58
|
/**
|
|
32
59
|
* Remove text blocks from user messages that match any of the given matchers.
|
|
33
60
|
* If stripping removes all content blocks from a message, the message itself
|
|
@@ -43,14 +70,7 @@ export function stripUserTextBlocksByPrefix(
|
|
|
43
70
|
return messages
|
|
44
71
|
.map((message) => {
|
|
45
72
|
if (message.role !== "user") return message;
|
|
46
|
-
const nextContent = message.content
|
|
47
|
-
if (block.type !== "text") return true;
|
|
48
|
-
return !matchers.some((m) =>
|
|
49
|
-
typeof m === "string"
|
|
50
|
-
? block.text.startsWith(m)
|
|
51
|
-
: block.text.startsWith(m.prefix) && block.text.endsWith(m.suffix),
|
|
52
|
-
);
|
|
53
|
-
});
|
|
73
|
+
const nextContent = filterInjectionBlocks(message.content, matchers);
|
|
54
74
|
if (nextContent.length === message.content.length) return message;
|
|
55
75
|
if (nextContent.length === 0) return null;
|
|
56
76
|
return { ...message, content: nextContent };
|
|
@@ -60,6 +80,30 @@ export function stripUserTextBlocksByPrefix(
|
|
|
60
80
|
);
|
|
61
81
|
}
|
|
62
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Tail-scoped variant of {@link stripUserTextBlocksByPrefix}: remove matching
|
|
85
|
+
* text blocks from ONLY the last message, and only when that message is a user
|
|
86
|
+
* message. This mirrors the runtime injection step, which applies every
|
|
87
|
+
* per-turn block to the tail user message, so clearing the tail's stale copies
|
|
88
|
+
* before re-injection makes re-injection idempotent.
|
|
89
|
+
*
|
|
90
|
+
* The tail message is retained even if all of its blocks are removed, so the
|
|
91
|
+
* role-`"user"` tail invariant the re-injection step relies on is preserved.
|
|
92
|
+
* Earlier messages are untouched, so per-message historical grounding (e.g.
|
|
93
|
+
* each turn's `<turn_context>`) survives and the stable cached prefix — which
|
|
94
|
+
* lives entirely before the tail — is never disturbed.
|
|
95
|
+
*/
|
|
96
|
+
export function stripTailUserTextBlocksByPrefix(
|
|
97
|
+
messages: Message[],
|
|
98
|
+
matchers: InjectionMatcher[],
|
|
99
|
+
): Message[] {
|
|
100
|
+
const last = messages[messages.length - 1];
|
|
101
|
+
if (!last || last.role !== "user") return messages;
|
|
102
|
+
const nextContent = filterInjectionBlocks(last.content, matchers);
|
|
103
|
+
if (nextContent.length === last.content.length) return messages;
|
|
104
|
+
return [...messages.slice(0, -1), { ...last, content: nextContent }];
|
|
105
|
+
}
|
|
106
|
+
|
|
63
107
|
/**
|
|
64
108
|
* Full-wrapper matcher for the memory-v3 ephemeral `<memory_spotlight>` block.
|
|
65
109
|
* Shared by the per-turn scoped strip ({@link stripSpotlightInjections}) and
|
|
@@ -88,8 +132,12 @@ export const NOW_SCRATCHPAD_STRIP_PREFIXES: InjectionMatcher[] = [
|
|
|
88
132
|
"<now_scratchpad>",
|
|
89
133
|
];
|
|
90
134
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Matchers stripped by the compaction pipeline (order doesn't matter — single
|
|
137
|
+
* pass). Exported so the post-compaction re-injection path can reuse this set
|
|
138
|
+
* as the base of its idempotency strip without duplicating it.
|
|
139
|
+
*/
|
|
140
|
+
export const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
|
|
93
141
|
"<channel_capabilities>",
|
|
94
142
|
"<channel_command_context>",
|
|
95
143
|
"<disk_pressure_warning>",
|
|
@@ -305,7 +305,7 @@ export function estimateMessagesTokens(
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
/** Estimate token cost for a single tool definition. */
|
|
308
|
-
function estimateToolDefinitionTokens(tool: ToolDefinition): number {
|
|
308
|
+
export function estimateToolDefinitionTokens(tool: ToolDefinition): number {
|
|
309
309
|
return (
|
|
310
310
|
TOOL_DEFINITION_OVERHEAD_TOKENS +
|
|
311
311
|
estimateTextTokens(tool.name) +
|
|
@@ -236,8 +236,9 @@ describe("disposeConversation — auto-analysis enqueue", () => {
|
|
|
236
236
|
|
|
237
237
|
test("untrusted conversation — enqueues neither graph_extract nor conversation_analyze", () => {
|
|
238
238
|
// `unknown` is the trust class used for untrusted actors. The disposal
|
|
239
|
-
// code short-circuits
|
|
240
|
-
// path should fire. This preserves the
|
|
239
|
+
// code short-circuits when `resolveCapabilities(trustClass).canAccessMemory`
|
|
240
|
+
// is false, so neither enqueue path should fire. This preserves the
|
|
241
|
+
// memory trust boundary.
|
|
241
242
|
const ctx = makeDisposeContext({
|
|
242
243
|
conversationId: "conv-untrusted",
|
|
243
244
|
trustClass: "unknown",
|
|
@@ -1184,6 +1184,7 @@ export async function finalizePendingToolResultRow(
|
|
|
1184
1184
|
let provenanceTrustClass:
|
|
1185
1185
|
| "guardian"
|
|
1186
1186
|
| "trusted_contact"
|
|
1187
|
+
| "unverified_contact"
|
|
1187
1188
|
| "unknown"
|
|
1188
1189
|
| undefined;
|
|
1189
1190
|
let automated: boolean | undefined;
|
|
@@ -1845,6 +1846,7 @@ export async function handleMessageComplete(
|
|
|
1845
1846
|
let provenanceTrustClass:
|
|
1846
1847
|
| "guardian"
|
|
1847
1848
|
| "trusted_contact"
|
|
1849
|
+
| "unverified_contact"
|
|
1848
1850
|
| "unknown"
|
|
1849
1851
|
| undefined;
|
|
1850
1852
|
let automated: boolean | undefined;
|
|
@@ -70,9 +70,9 @@ import type { UserPromptSubmitContext } from "../plugin-api/types.js";
|
|
|
70
70
|
import { runHook } from "../plugins/pipeline.js";
|
|
71
71
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
72
72
|
import type { Provider } from "../providers/types.js";
|
|
73
|
-
import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
|
|
74
73
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
75
74
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
75
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
76
76
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
77
77
|
import type { ActivationMomentParam } from "../telemetry/activation-funnel.js";
|
|
78
78
|
import type { UsageActor } from "../usage/actors.js";
|
|
@@ -187,6 +187,67 @@ export interface AssistantSurface {
|
|
|
187
187
|
activationMoment?: ActivationMomentParam;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
// ── abort watchdog ───────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Generous backstop that drives an aborted turn to its `finally` even if some
|
|
194
|
+
* awaited operation fails to observe the abort signal.
|
|
195
|
+
*
|
|
196
|
+
* Abort is otherwise cooperative and already wired into the slow paths: the
|
|
197
|
+
* provider call forwards the signal to its HTTP/streaming fetch, and tool
|
|
198
|
+
* execution races the signal so a stuck tool can't block cancellation. This
|
|
199
|
+
* watchdog only fires when a future code path silently ignores abort — without
|
|
200
|
+
* it, such a path would hang the loop forever and latch the conversation's
|
|
201
|
+
* `processing` flag true (the wedged "Thinking…" indicator). It is
|
|
202
|
+
* defense-in-depth, not the primary mechanism, so the timeout is deliberately
|
|
203
|
+
* generous; in the common case abort settles in-flight work well before it.
|
|
204
|
+
*/
|
|
205
|
+
const ABORT_WATCHDOG_MS = 45_000;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Race `work` against an abort watchdog. The watchdog stays disarmed until the
|
|
209
|
+
* signal fires; once it does, the turn has `timeoutMs` to settle before the
|
|
210
|
+
* watchdog rejects with the signal's own abort reason so the caller unwinds to
|
|
211
|
+
* its `finally` and the reason classifies as a user cancellation downstream.
|
|
212
|
+
* The abandoned `work` promise keeps running detached — its eventual rejection
|
|
213
|
+
* is swallowed so it can't surface as an unhandled rejection.
|
|
214
|
+
*/
|
|
215
|
+
async function withAbortWatchdog<T>(
|
|
216
|
+
work: Promise<T>,
|
|
217
|
+
signal: AbortSignal,
|
|
218
|
+
timeoutMs: number,
|
|
219
|
+
onFire: () => void,
|
|
220
|
+
): Promise<T> {
|
|
221
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
222
|
+
let onAbort: (() => void) | undefined;
|
|
223
|
+
const watchdog = new Promise<never>((_, reject) => {
|
|
224
|
+
const arm = () => {
|
|
225
|
+
timer = setTimeout(() => {
|
|
226
|
+
onFire();
|
|
227
|
+
// Propagate the signal's own reason (an `AbortReason` for daemon-owned
|
|
228
|
+
// cancels) so the loop's catch classifies this as a user cancellation;
|
|
229
|
+
// fall back to a tagged AbortError when the signal carries no reason.
|
|
230
|
+
reject(
|
|
231
|
+
signal.reason ??
|
|
232
|
+
new DOMException("The operation was aborted", "AbortError"),
|
|
233
|
+
);
|
|
234
|
+
}, timeoutMs);
|
|
235
|
+
};
|
|
236
|
+
if (signal.aborted) arm();
|
|
237
|
+
else {
|
|
238
|
+
onAbort = arm;
|
|
239
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
try {
|
|
243
|
+
return await Promise.race([work, watchdog]);
|
|
244
|
+
} finally {
|
|
245
|
+
if (timer) clearTimeout(timer);
|
|
246
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
247
|
+
void work.catch(() => {});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
190
251
|
// ── runAgentLoop ─────────────────────────────────────────────────────
|
|
191
252
|
|
|
192
253
|
export async function runAgentLoopImpl(
|
|
@@ -902,22 +963,36 @@ export async function runAgentLoopImpl(
|
|
|
902
963
|
msgs: Message[],
|
|
903
964
|
compactInPlace = false,
|
|
904
965
|
): Promise<Message[]> => {
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
966
|
+
const watchdogMs = ctx.abortWatchdogMs ?? ABORT_WATCHDOG_MS;
|
|
967
|
+
const { history, exitReason, newMessages } = await withAbortWatchdog(
|
|
968
|
+
ctx.agentLoop.run({
|
|
969
|
+
messages: msgs,
|
|
970
|
+
onEvent: eventHandler,
|
|
971
|
+
signal: abortController.signal,
|
|
972
|
+
requestId: reqId,
|
|
973
|
+
onCheckpoint,
|
|
974
|
+
callSite: turnCallSite,
|
|
975
|
+
trust: loopTrust,
|
|
976
|
+
overrideProfile: turnOverrideProfile,
|
|
977
|
+
...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
|
|
978
|
+
resolveOverrideProfile: resolveCurrentOverrideProfile,
|
|
979
|
+
resolveContextWindow,
|
|
980
|
+
compactInPlace,
|
|
981
|
+
isNonInteractive,
|
|
982
|
+
modelProfileKey,
|
|
983
|
+
}),
|
|
984
|
+
abortController.signal,
|
|
985
|
+
watchdogMs,
|
|
986
|
+
() =>
|
|
987
|
+
rlog.error(
|
|
988
|
+
{
|
|
989
|
+
conversationId: ctx.conversationId,
|
|
990
|
+
requestId: reqId,
|
|
991
|
+
timeoutMs: watchdogMs,
|
|
992
|
+
},
|
|
993
|
+
"Abort watchdog fired — agent loop did not settle after cancel; forcing turn to finally",
|
|
994
|
+
),
|
|
995
|
+
);
|
|
921
996
|
lastRunNewMessages = newMessages;
|
|
922
997
|
if (exitReason === "handoff") {
|
|
923
998
|
yieldedForHandoff = true;
|
|
@@ -1450,6 +1525,12 @@ export async function runAgentLoopImpl(
|
|
|
1450
1525
|
|
|
1451
1526
|
ctx.profiler.emitSummary(ctx.traceEmitter, reqId);
|
|
1452
1527
|
|
|
1528
|
+
// Tear down this turn's per-turn state. Abort reliably drives the loop to
|
|
1529
|
+
// this `finally` within a bounded time — cooperative signal propagation
|
|
1530
|
+
// (provider fetch + tool race) backed by the abort watchdog — so a
|
|
1531
|
+
// cancelled turn always unwinds before any resend can start a new one.
|
|
1532
|
+
// There is therefore only ever one turn alive, and clearing the shared
|
|
1533
|
+
// state below cannot clobber a concurrent turn.
|
|
1453
1534
|
ctx.abortController = null;
|
|
1454
1535
|
ctx.setProcessing(false);
|
|
1455
1536
|
ctx.onConfirmationOutcome = undefined;
|
|
@@ -1589,9 +1670,9 @@ export async function applyCompactionResult(
|
|
|
1589
1670
|
// in-context boundary rather than the raw mirror so the persisted count
|
|
1590
1671
|
// stays consistent with what the new summary represents and never
|
|
1591
1672
|
// double-counts an unsliced untrusted view.
|
|
1592
|
-
const inContextCompactedCount =
|
|
1673
|
+
const inContextCompactedCount = !resolveCapabilities(
|
|
1593
1674
|
ctx.trustContext?.trustClass,
|
|
1594
|
-
)
|
|
1675
|
+
).canAccessMemory
|
|
1595
1676
|
? 0
|
|
1596
1677
|
: ctx.contextCompactedMessageCount;
|
|
1597
1678
|
ctx.contextCompactedMessageCount =
|
|
@@ -23,7 +23,7 @@ const log = getLogger("conversation-history");
|
|
|
23
23
|
|
|
24
24
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
25
25
|
|
|
26
|
-
function isToolResultBlock(
|
|
26
|
+
export function isToolResultBlock(
|
|
27
27
|
block: ContentBlock | Record<string, unknown>,
|
|
28
28
|
): boolean {
|
|
29
29
|
return (
|
|
@@ -16,10 +16,8 @@ import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
|
16
16
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
17
17
|
import { disposeContextWindowManager } from "../plugins/defaults/compaction/manager-store.js";
|
|
18
18
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
type TrustClass,
|
|
22
|
-
} from "../runtime/actor-trust-resolver.js";
|
|
19
|
+
import { type TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
20
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
23
21
|
import { unregisterConversationSender } from "../tools/browser/browser-screencast.js";
|
|
24
22
|
import { type AbortReason, createAbortReason } from "../util/abort-reasons.js";
|
|
25
23
|
import { getLogger } from "../util/logger.js";
|
|
@@ -147,7 +145,7 @@ export function disposeConversation(ctx: DisposeContext): void {
|
|
|
147
145
|
// Trigger graph extraction for end-of-conversation sweep.
|
|
148
146
|
// Only extract from guardian conversations to preserve the memory trust
|
|
149
147
|
// boundary — untrusted content must not influence future memory retrieval.
|
|
150
|
-
if (
|
|
148
|
+
if (resolveCapabilities(ctx.trustContext?.trustClass).canAccessMemory) {
|
|
151
149
|
// Recursion guard: skip graph_extract for auto-analysis conversations.
|
|
152
150
|
// The analysis agent writes memory directly via tools, so extracting
|
|
153
151
|
// from its reflective musings would double-write into the memory graph.
|
|
@@ -28,7 +28,10 @@ import {
|
|
|
28
28
|
import { extractPreferences } from "../notifications/preference-extractor.js";
|
|
29
29
|
import { createPreference } from "../notifications/preferences-store.js";
|
|
30
30
|
import type { ContextWindowResult } from "../plugins/defaults/compaction/window-manager.js";
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
type GuardianPendingScope,
|
|
33
|
+
routeGuardianReply,
|
|
34
|
+
} from "../runtime/guardian-reply-router.js";
|
|
32
35
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
33
36
|
import { getLogger } from "../util/logger.js";
|
|
34
37
|
import type { CleanResult, Conversation } from "./conversation.js";
|
|
@@ -153,7 +156,7 @@ function resolveQueuedTurnInterfaceContext(
|
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
/** Build a SlashContext from the current conversation state and config. */
|
|
156
|
-
function buildSlashContext(
|
|
159
|
+
export function buildSlashContext(
|
|
157
160
|
content: string,
|
|
158
161
|
conversation: Conversation,
|
|
159
162
|
): SlashContext | undefined {
|
|
@@ -1407,9 +1410,14 @@ export async function processMessage(
|
|
|
1407
1410
|
"vellum",
|
|
1408
1411
|
).map((request) => request.id)
|
|
1409
1412
|
: [];
|
|
1410
|
-
|
|
1413
|
+
// Empty hints → leave the scope unset (identity-fallback): the desktop
|
|
1414
|
+
// guardian can still resolve their pending work by identity/principal.
|
|
1415
|
+
const pendingScope: GuardianPendingScope | undefined =
|
|
1411
1416
|
canonicalPendingRequestHintIdsForConversation.length > 0
|
|
1412
|
-
?
|
|
1417
|
+
? {
|
|
1418
|
+
mode: "scoped",
|
|
1419
|
+
requestIds: canonicalPendingRequestHintIdsForConversation,
|
|
1420
|
+
}
|
|
1413
1421
|
: undefined;
|
|
1414
1422
|
|
|
1415
1423
|
// ── Canonical guardian reply router (desktop/conversation path) ──
|
|
@@ -1428,7 +1436,7 @@ export async function processMessage(
|
|
|
1428
1436
|
conversation.trustContext?.guardianPrincipalId ?? undefined,
|
|
1429
1437
|
},
|
|
1430
1438
|
conversationId: conversation.conversationId,
|
|
1431
|
-
|
|
1439
|
+
pendingScope,
|
|
1432
1440
|
// Desktop path: disable NL classification to avoid consuming non-decision
|
|
1433
1441
|
// messages while a tool confirmation is pending. Deterministic code-prefix
|
|
1434
1442
|
// and callback parsing remain active.
|
|
@@ -9,9 +9,9 @@ import { statSync } from "node:fs";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
11
|
import { type ChannelId, parseInterfaceId } from "../channels/types.js";
|
|
12
|
-
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
13
12
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
14
13
|
import { getConfig } from "../config/loader.js";
|
|
14
|
+
import { isMemoryV3Live } from "../config/memory-v3-gate.js";
|
|
15
15
|
import type { LLMCallSite, LLMConfig } from "../config/schemas/llm.js";
|
|
16
16
|
import {
|
|
17
17
|
NOW_SCRATCHPAD_STRIP_PREFIXES,
|
|
@@ -62,11 +62,11 @@ import type {
|
|
|
62
62
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
63
63
|
import {
|
|
64
64
|
type ActorTrustContext,
|
|
65
|
-
isUntrustedTrustClass,
|
|
66
65
|
resolveActorTrust,
|
|
67
66
|
type TrustClass,
|
|
68
67
|
} from "../runtime/actor-trust-resolver.js";
|
|
69
68
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
69
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
70
70
|
import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/acl-enforcement.js";
|
|
71
71
|
import { getSubagentManager } from "../subagent/index.js";
|
|
72
72
|
import type { SubagentState } from "../subagent/types.js";
|
|
@@ -125,8 +125,8 @@ export interface InboundActorContext {
|
|
|
125
125
|
actorSenderDisplayName?: string;
|
|
126
126
|
/** Guardian-managed display name from the contact record. */
|
|
127
127
|
actorMemberDisplayName?: string;
|
|
128
|
-
/** Trust classification:
|
|
129
|
-
trustClass:
|
|
128
|
+
/** Trust classification: see TrustClass. */
|
|
129
|
+
trustClass: TrustClass;
|
|
130
130
|
/** Guardian identity for this (assistant, channel) binding. */
|
|
131
131
|
guardianIdentity?: string;
|
|
132
132
|
/** Member status when the actor has a contact record. */
|
|
@@ -916,7 +916,7 @@ function filterSlackConversationRowsForActor(
|
|
|
916
916
|
rows: MessageRow[],
|
|
917
917
|
trustClass: TrustClass | undefined,
|
|
918
918
|
): MessageRow[] {
|
|
919
|
-
if (
|
|
919
|
+
if (resolveCapabilities(trustClass).canAccessMemory) return rows;
|
|
920
920
|
const nonSlackVisibleRows = filterMessagesForUntrustedActor(rows);
|
|
921
921
|
const nonSlackVisibleIds = new Set(nonSlackVisibleRows.map((row) => row.id));
|
|
922
922
|
return rows.filter((row) => {
|
|
@@ -1009,6 +1009,7 @@ function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
|
|
|
1009
1009
|
if (
|
|
1010
1010
|
outer.provenanceTrustClass === "guardian" ||
|
|
1011
1011
|
outer.provenanceTrustClass === "trusted_contact" ||
|
|
1012
|
+
outer.provenanceTrustClass === "unverified_contact" ||
|
|
1012
1013
|
outer.provenanceTrustClass === "unknown"
|
|
1013
1014
|
) {
|
|
1014
1015
|
provenanceTrustClass = outer.provenanceTrustClass;
|
|
@@ -1322,9 +1323,9 @@ export function loadSlackChronologicalContext(
|
|
|
1322
1323
|
options,
|
|
1323
1324
|
);
|
|
1324
1325
|
return assembleSlackChronologicalContext(rows, capabilities, {
|
|
1325
|
-
contextSummary:
|
|
1326
|
-
?
|
|
1327
|
-
:
|
|
1326
|
+
contextSummary: resolveCapabilities(options.trustClass).canAccessMemory
|
|
1327
|
+
? options.contextSummary
|
|
1328
|
+
: null,
|
|
1328
1329
|
});
|
|
1329
1330
|
}
|
|
1330
1331
|
|
|
@@ -1536,8 +1537,8 @@ export interface RuntimeInjectionBlocks {
|
|
|
1536
1537
|
*/
|
|
1537
1538
|
memoryV3InjectedBlock?: string;
|
|
1538
1539
|
/**
|
|
1539
|
-
* True when memory-v3 superseded v2 as this turn's `<memory>` source —
|
|
1540
|
-
* `memory
|
|
1540
|
+
* True when memory-v3 superseded v2 as this turn's `<memory>` source —
|
|
1541
|
+
* `memory.v3.live` is on AND the v3 injector produced a block (possibly
|
|
1541
1542
|
* empty-text on an all-repeat turn), i.e. exactly when assembly stripped
|
|
1542
1543
|
* v2's fresh tail block. The user-prompt-submit hook keys v2's
|
|
1543
1544
|
* `memoryInjectedBlock` metadata persist off this so a stripped v2 block is
|
|
@@ -2177,7 +2178,7 @@ export async function applyRuntimeInjections(
|
|
|
2177
2178
|
// no-op, keeping the v2 path bit-for-bit identical.
|
|
2178
2179
|
let runMessagesForAssembly = stripSpotlightInjections(runMessages);
|
|
2179
2180
|
|
|
2180
|
-
// v2 suppression: when
|
|
2181
|
+
// v2 suppression: when `memory.v3.live` is on AND the v3 injector
|
|
2181
2182
|
// produced a block this turn (possibly empty-text on an all-repeat turn), v3
|
|
2182
2183
|
// owns the `<memory>` layer. v2's `prepareMemory` already prepended its own
|
|
2183
2184
|
// fresh `<memory>` block to the tail user message — strip the TAIL's v2
|
|
@@ -2196,10 +2197,7 @@ export async function applyRuntimeInjections(
|
|
|
2196
2197
|
// (`produce()` → null) leaves v2's block intact — fallback rather than a
|
|
2197
2198
|
// memory-less turn. Idempotent: re-injection sites that already stripped
|
|
2198
2199
|
// see no change. Flag off → bit-for-bit identical to the v2 path.
|
|
2199
|
-
const suppressV2MemoryForV3 =
|
|
2200
|
-
"memory-v3-live",
|
|
2201
|
-
getConfig(),
|
|
2202
|
-
);
|
|
2200
|
+
const suppressV2MemoryForV3 = isMemoryV3Live(getConfig());
|
|
2203
2201
|
const v3ProducedBlock = afterMemory.some((b) => b.id === MEMORY_V3_BLOCK_ID);
|
|
2204
2202
|
const memoryV3Active = suppressV2MemoryForV3 && v3ProducedBlock;
|
|
2205
2203
|
if (memoryV3Active) {
|