@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
|
@@ -291,6 +291,32 @@ export function markSurfaceCompleted(
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Complete a `ui_surface` card and notify live clients, addressed only by
|
|
296
|
+
* conversation + surface id.
|
|
297
|
+
*
|
|
298
|
+
* Unlike {@link completeSurfaceFromAction}, this needs no live `Conversation`
|
|
299
|
+
* instance, so it can run from flows that don't own one — projecting a
|
|
300
|
+
* terminal guardian-request status onto its in-app approval card when the
|
|
301
|
+
* request was resolved on another surface (or by the expiry sweep). Persists
|
|
302
|
+
* the completion (reload-safe) and broadcasts `ui_surface_complete` so every
|
|
303
|
+
* connected client of this guardian converges. No-ops when the surface block
|
|
304
|
+
* isn't found in the conversation.
|
|
305
|
+
*/
|
|
306
|
+
export function completeSurfaceAndNotify(
|
|
307
|
+
conversationId: string,
|
|
308
|
+
surfaceId: string,
|
|
309
|
+
summary: string,
|
|
310
|
+
): void {
|
|
311
|
+
markSurfaceCompleted({ conversationId }, surfaceId, summary);
|
|
312
|
+
broadcastMessage({
|
|
313
|
+
type: "ui_surface_complete",
|
|
314
|
+
conversationId,
|
|
315
|
+
surfaceId,
|
|
316
|
+
summary,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
294
320
|
/**
|
|
295
321
|
* Remove a `ui_surface` content block from history so a passively dismissed
|
|
296
322
|
* surface does not survive a reload. The live client drops a dismissed surface
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
} from "./doordash-steps.js";
|
|
56
56
|
import type { ServerMessage, UiSurfaceShow } from "./message-protocol.js";
|
|
57
57
|
import { runPostExecutionSideEffects } from "./tool-side-effects.js";
|
|
58
|
-
import { resolveTrustClass } from "./trust-context.js";
|
|
58
|
+
import { FALLBACK_TURN_TRUST, resolveTrustClass } from "./trust-context.js";
|
|
59
59
|
|
|
60
60
|
const log = getLogger("conversation-tool-setup");
|
|
61
61
|
|
|
@@ -181,28 +181,32 @@ export function createToolExecutor(
|
|
|
181
181
|
markDoordashStepInProgress(ctx, executionInput);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
//
|
|
185
|
-
//
|
|
184
|
+
// Per-turn trust snapshot: prefer the snapshot captured at turn start so
|
|
185
|
+
// a concurrent owner meta command (/status, /clean) that mutates the live
|
|
186
|
+
// trustContext cannot elevate the in-flight turn to guardian.
|
|
187
|
+
const turnTrust =
|
|
188
|
+
ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
|
|
189
|
+
|
|
186
190
|
const toolContext: ToolContext = {
|
|
187
191
|
workingDir: ctx.workingDir,
|
|
188
192
|
conversationId: ctx.conversationId,
|
|
189
193
|
assistantId: ctx.assistantId,
|
|
190
194
|
requestId: ctx.currentRequestId,
|
|
191
195
|
taskRunId: ctx.taskRunId,
|
|
192
|
-
trustClass: resolveTrustClass(
|
|
193
|
-
executionChannel:
|
|
194
|
-
sourceActorPrincipalId:
|
|
196
|
+
trustClass: resolveTrustClass(turnTrust),
|
|
197
|
+
executionChannel: turnTrust.sourceChannel,
|
|
198
|
+
sourceActorPrincipalId: turnTrust.guardianPrincipalId,
|
|
195
199
|
callSessionId: ctx.callSessionId,
|
|
196
200
|
triggeredBySurfaceAction:
|
|
197
201
|
ctx.surfaceActionRequestIds?.has(ctx.currentRequestId ?? "") ?? false,
|
|
198
202
|
approvedViaPrompt: ctx.approvedViaPromptThisTurn || undefined,
|
|
199
203
|
batchAuthorizedByTask: false,
|
|
200
|
-
requesterExternalUserId:
|
|
201
|
-
requesterChatId:
|
|
202
|
-
requesterIdentifier:
|
|
203
|
-
requesterDisplayName:
|
|
204
|
+
requesterExternalUserId: turnTrust.requesterExternalUserId,
|
|
205
|
+
requesterChatId: turnTrust.requesterChatId,
|
|
206
|
+
requesterIdentifier: turnTrust.requesterIdentifier,
|
|
207
|
+
requesterDisplayName: turnTrust.requesterDisplayName,
|
|
204
208
|
channelPermissionChannelId:
|
|
205
|
-
|
|
209
|
+
turnTrust.sourceChannel === "slack"
|
|
206
210
|
? getBindingByConversation(ctx.conversationId)?.externalChatId
|
|
207
211
|
: undefined,
|
|
208
212
|
onOutput,
|
|
@@ -214,6 +218,7 @@ export function createToolExecutor(
|
|
|
214
218
|
isPlatformHosted: getIsPlatform(),
|
|
215
219
|
transportInterface: ctx.transportInterface,
|
|
216
220
|
overrideProfile: ctx.currentTurnOverrideProfile,
|
|
221
|
+
invokingCallSite: ctx.currentCallSite ?? "mainAgent",
|
|
217
222
|
attribution: resolveConversationAttribution(ctx),
|
|
218
223
|
onToolLifecycleEvent: handleToolLifecycleEvent,
|
|
219
224
|
sendToClient: (msg) => {
|
|
@@ -80,12 +80,10 @@ import {
|
|
|
80
80
|
} from "../prompts/system-prompt.js";
|
|
81
81
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
82
82
|
import type { Provider } from "../providers/types.js";
|
|
83
|
-
import {
|
|
84
|
-
isUntrustedTrustClass,
|
|
85
|
-
type TrustClass,
|
|
86
|
-
} from "../runtime/actor-trust-resolver.js";
|
|
83
|
+
import { type TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
87
84
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
88
85
|
import type { AuthContext } from "../runtime/auth/types.js";
|
|
86
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
89
87
|
import type { InteractiveUiResult } from "../runtime/interactive-ui.js";
|
|
90
88
|
import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
|
|
91
89
|
import {
|
|
@@ -107,7 +105,7 @@ import {
|
|
|
107
105
|
runAgentLoopImpl,
|
|
108
106
|
} from "./conversation-agent-loop.js";
|
|
109
107
|
import type { HistoryConversationContext } from "./conversation-history.js";
|
|
110
|
-
import { undo as undoImpl } from "./conversation-history.js";
|
|
108
|
+
import { isToolResultBlock, undo as undoImpl } from "./conversation-history.js";
|
|
111
109
|
import {
|
|
112
110
|
abortConversation,
|
|
113
111
|
disposeConversation,
|
|
@@ -175,6 +173,36 @@ import { TraceEmitter } from "./trace-emitter.js";
|
|
|
175
173
|
|
|
176
174
|
const log = getLogger("conversation");
|
|
177
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Whether a persisted message starts a new conversation turn. A turn is
|
|
178
|
+
* delimited by a "real" user message; a user message whose content is entirely
|
|
179
|
+
* tool_result blocks is a continuation within the current turn, and assistant
|
|
180
|
+
* messages never start one. Mirrors the turn-boundary definition used by
|
|
181
|
+
* `getAssistantMessageIdsInTurn`/`getTurnTimeBounds` and the agent loop's
|
|
182
|
+
* per-turn `turnCount++`, so counting these reconstructs `turnCount` on load.
|
|
183
|
+
*/
|
|
184
|
+
function startsNewTurn(role: string, content: string): boolean {
|
|
185
|
+
if (role !== "user") return false;
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(content);
|
|
188
|
+
if (
|
|
189
|
+
Array.isArray(parsed) &&
|
|
190
|
+
parsed.length > 0 &&
|
|
191
|
+
parsed.every(
|
|
192
|
+
(block: unknown) =>
|
|
193
|
+
block != null &&
|
|
194
|
+
typeof block === "object" &&
|
|
195
|
+
isToolResultBlock(block as Record<string, unknown>),
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
// Non-JSON content is a plain user message — a turn boundary.
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
178
206
|
export interface CleanResult {
|
|
179
207
|
previousEstimatedInputTokens: number;
|
|
180
208
|
estimatedInputTokens: number;
|
|
@@ -366,6 +394,13 @@ export class Conversation {
|
|
|
366
394
|
workspaceDir: string,
|
|
367
395
|
) => Pick<WorkspaceGitService, "ensureInitialized">;
|
|
368
396
|
/** @internal */ commitTurnChanges?: typeof commitTurnChanges;
|
|
397
|
+
/**
|
|
398
|
+
* Abort-watchdog timeout (ms) for the agent loop's bounded-unwind backstop.
|
|
399
|
+
* Overridable in tests to fire the watchdog quickly; defaults to the
|
|
400
|
+
* production constant in the agent loop when unset.
|
|
401
|
+
* @internal
|
|
402
|
+
*/
|
|
403
|
+
abortWatchdogMs?: number;
|
|
369
404
|
/**
|
|
370
405
|
* The conversation's immutable creation type (`interactive`, `background`,
|
|
371
406
|
* `scheduled`, …) as stored on the DB row. Cached on load (and set directly
|
|
@@ -824,10 +859,25 @@ export class Conversation {
|
|
|
824
859
|
|
|
825
860
|
async loadFromDb(): Promise<void> {
|
|
826
861
|
const trustClass = this.trustContext?.trustClass;
|
|
862
|
+
const canAccessMemory = resolveCapabilities(trustClass).canAccessMemory;
|
|
827
863
|
const allDbMessages = getMessages(this.conversationId);
|
|
828
|
-
const dbMessages =
|
|
829
|
-
?
|
|
830
|
-
: allDbMessages;
|
|
864
|
+
const dbMessages = canAccessMemory
|
|
865
|
+
? allDbMessages
|
|
866
|
+
: filterMessagesForUntrustedActor(allDbMessages);
|
|
867
|
+
|
|
868
|
+
// Rehydrate the in-memory turn counter from persisted history. `turnCount`
|
|
869
|
+
// is otherwise a fresh-zero field, so a reloaded conversation (eviction,
|
|
870
|
+
// restart, fork) would restart its turn numbering at 0 and the next turn
|
|
871
|
+
// would reuse `turnIndex` 0 — colliding with the conversation's first turn.
|
|
872
|
+
// The memory-v3 selector memoizes per (conversationId, turnIndex) for the
|
|
873
|
+
// life of the daemon process, so a collided turnIndex serves a stale
|
|
874
|
+
// selection and skips retrieval. One turn per real (turn-starting) user
|
|
875
|
+
// message, matching the agent loop's per-turn `turnCount++`. Counted from
|
|
876
|
+
// the full unsliced history so it survives compaction and is independent of
|
|
877
|
+
// the viewer's trust class.
|
|
878
|
+
this.turnCount = allDbMessages.filter((m) =>
|
|
879
|
+
startsNewTurn(m.role, m.content),
|
|
880
|
+
).length;
|
|
831
881
|
|
|
832
882
|
const conv = getConversation(this.conversationId);
|
|
833
883
|
this.conversationType = conv?.conversationType ?? undefined;
|
|
@@ -855,12 +905,12 @@ export class Conversation {
|
|
|
855
905
|
// than exist. Slack chronological context is a separate consumer that
|
|
856
906
|
// applies its own trust filtering downstream, so it reads the raw
|
|
857
907
|
// mirrored count rather than this in-context boundary.
|
|
858
|
-
const inContextCompactedCount =
|
|
859
|
-
?
|
|
860
|
-
:
|
|
861
|
-
const contextSummaryForHistory =
|
|
862
|
-
? null
|
|
863
|
-
:
|
|
908
|
+
const inContextCompactedCount = canAccessMemory
|
|
909
|
+
? Math.min(this.contextCompactedMessageCount, dbMessages.length)
|
|
910
|
+
: 0;
|
|
911
|
+
const contextSummaryForHistory = canAccessMemory
|
|
912
|
+
? this.contextSummary?.trim() || null
|
|
913
|
+
: null;
|
|
864
914
|
|
|
865
915
|
// Every injection-strip event (`/clean` or compaction) updates
|
|
866
916
|
// `historyStrippedAt`. Messages older than this should skip metadata
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { InterfaceId } from "../channels/types.js";
|
|
2
2
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
3
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
3
4
|
import type { DiskPressureStatus } from "./disk-pressure-guard.js";
|
|
4
5
|
import type { ConversationType } from "./message-types/shared.js";
|
|
5
6
|
import type { TrustContext } from "./trust-context.js";
|
|
@@ -74,11 +75,11 @@ export function classifyDiskPressureTurnPolicy(
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
const trustClass = metadata.trustContext?.trustClass;
|
|
77
|
-
if (trustClass
|
|
78
|
+
if (resolveCapabilities(trustClass).canActUnderDiskPressureCleanup) {
|
|
78
79
|
return { action: "allow-cleanup-mode", reason: "guardian" };
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
if (trustClass === "trusted_contact") {
|
|
82
|
+
if (trustClass === "trusted_contact" || trustClass === "unverified_contact") {
|
|
82
83
|
return { action: "block", reason: "trusted-contact" };
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -156,6 +157,7 @@ function isExplicitLocalOwnerCleanupTurn(
|
|
|
156
157
|
}
|
|
157
158
|
return (
|
|
158
159
|
metadata.trustContext == null ||
|
|
159
|
-
metadata.trustContext.trustClass
|
|
160
|
+
resolveCapabilities(metadata.trustContext.trustClass)
|
|
161
|
+
.canActUnderDiskPressureCleanup
|
|
160
162
|
);
|
|
161
163
|
}
|
|
@@ -121,7 +121,6 @@ describe("completeA2AInvite", () => {
|
|
|
121
121
|
const invite = findById(created.inviteId!);
|
|
122
122
|
const contact = getContact(invite!.contactId);
|
|
123
123
|
expect(contact!.channels[0]!.address).toBe("upper-case-id-123");
|
|
124
|
-
expect(contact!.channels[0]!.externalUserId).toBe("UPPER-Case-ID-123");
|
|
125
124
|
});
|
|
126
125
|
|
|
127
126
|
test("assistantContactMetadata has correct assistantId and gatewayUrl", () => {
|
|
@@ -120,7 +120,6 @@ describe("redeemA2AInvite", () => {
|
|
|
120
120
|
|
|
121
121
|
const contact = getContact(result.contactId!);
|
|
122
122
|
expect(contact!.channels[0]!.address).toBe("upper-case-sender-id");
|
|
123
|
-
expect(contact!.channels[0]!.externalUserId).toBe("UPPER-Case-SENDER-ID");
|
|
124
123
|
});
|
|
125
124
|
|
|
126
125
|
test("does not make outbound fetch calls", () => {
|
|
@@ -221,7 +221,6 @@ export function completeA2AInvite(params: {
|
|
|
221
221
|
{
|
|
222
222
|
type: "a2a",
|
|
223
223
|
address: params.acceptor.assistantId.toLowerCase(),
|
|
224
|
-
externalUserId: params.acceptor.assistantId,
|
|
225
224
|
status: "active",
|
|
226
225
|
policy: "allow",
|
|
227
226
|
},
|
|
@@ -289,7 +288,6 @@ export function redeemA2AInvite(params: {
|
|
|
289
288
|
{
|
|
290
289
|
type: "a2a",
|
|
291
290
|
address: params.sender.assistantId.toLowerCase(),
|
|
292
|
-
externalUserId: params.sender.assistantId,
|
|
293
291
|
status: "active",
|
|
294
292
|
policy: "allow",
|
|
295
293
|
},
|
|
@@ -332,11 +332,9 @@ export async function verifyTrustedContact(
|
|
|
332
332
|
channel: verificationChannel,
|
|
333
333
|
expectedChatId: channel.externalChatId,
|
|
334
334
|
expectedExternalUserId:
|
|
335
|
-
channel.
|
|
336
|
-
(channel.address !== channel.externalChatId
|
|
335
|
+
channel.address !== channel.externalChatId
|
|
337
336
|
? channel.address
|
|
338
|
-
: undefined
|
|
339
|
-
undefined,
|
|
337
|
+
: undefined,
|
|
340
338
|
identityBindingStatus: "bound",
|
|
341
339
|
destinationAddress: effectiveDestination,
|
|
342
340
|
verificationPurpose: "trusted_contact",
|
|
@@ -409,17 +407,14 @@ export async function verifyTrustedContact(
|
|
|
409
407
|
|
|
410
408
|
// --- Slack verification ---
|
|
411
409
|
if (verificationChannel === "slack") {
|
|
412
|
-
const slackUserId =
|
|
413
|
-
channel.externalUserId ?? channel.address ?? destination;
|
|
410
|
+
const slackUserId = channel.address;
|
|
414
411
|
|
|
415
412
|
const sessionResult = createOutboundSession({
|
|
416
413
|
channel: verificationChannel,
|
|
417
414
|
expectedExternalUserId:
|
|
418
|
-
channel.
|
|
419
|
-
(channel.address !== channel.externalChatId
|
|
415
|
+
channel.address !== channel.externalChatId
|
|
420
416
|
? channel.address
|
|
421
|
-
: undefined
|
|
422
|
-
undefined,
|
|
417
|
+
: undefined,
|
|
423
418
|
expectedChatId: channel.externalChatId ?? undefined,
|
|
424
419
|
identityBindingStatus: "bound",
|
|
425
420
|
destinationAddress: slackUserId,
|
|
@@ -28,6 +28,9 @@ import { log as _log } from "./shared.js";
|
|
|
28
28
|
|
|
29
29
|
// -- Result type --
|
|
30
30
|
|
|
31
|
+
export const SlackThreadMode = z.enum(["mention_only", "mention_then_thread"]);
|
|
32
|
+
export type SlackThreadMode = z.infer<typeof SlackThreadMode>;
|
|
33
|
+
|
|
31
34
|
export const SlackChannelConfigResultSchema = z.object({
|
|
32
35
|
success: z.boolean(),
|
|
33
36
|
hasBotToken: z.boolean(),
|
|
@@ -39,6 +42,7 @@ export const SlackChannelConfigResultSchema = z.object({
|
|
|
39
42
|
teamUrl: z.string().optional(),
|
|
40
43
|
botUserId: z.string().optional(),
|
|
41
44
|
botUsername: z.string().optional(),
|
|
45
|
+
threadMode: SlackThreadMode,
|
|
42
46
|
error: z.string().optional(),
|
|
43
47
|
warning: z.string().optional(),
|
|
44
48
|
});
|
|
@@ -129,6 +133,8 @@ export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult>
|
|
|
129
133
|
ensureUserTokenInjectionTemplates();
|
|
130
134
|
}
|
|
131
135
|
|
|
136
|
+
const { threadMode } = getConfig().slack;
|
|
137
|
+
|
|
132
138
|
return {
|
|
133
139
|
success: true,
|
|
134
140
|
hasBotToken,
|
|
@@ -140,6 +146,7 @@ export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult>
|
|
|
140
146
|
...(teamUrl ? { teamUrl } : {}),
|
|
141
147
|
...(botUserId ? { botUserId } : {}),
|
|
142
148
|
...(botUsername ? { botUsername } : {}),
|
|
149
|
+
threadMode,
|
|
143
150
|
};
|
|
144
151
|
}
|
|
145
152
|
|
|
@@ -166,6 +173,7 @@ async function currentErrorSnapshot(
|
|
|
166
173
|
!!(errConn && errConn.status === "active") &&
|
|
167
174
|
errHasBotToken &&
|
|
168
175
|
errHasAppToken,
|
|
176
|
+
threadMode: getConfig().slack.threadMode,
|
|
169
177
|
error,
|
|
170
178
|
};
|
|
171
179
|
}
|
|
@@ -429,6 +437,7 @@ export async function setSlackChannelConfig(
|
|
|
429
437
|
hasUserToken,
|
|
430
438
|
connected: hasBotToken && hasAppToken,
|
|
431
439
|
...metadata,
|
|
440
|
+
threadMode: getConfig().slack.threadMode,
|
|
432
441
|
...(warning ? { warning } : {}),
|
|
433
442
|
};
|
|
434
443
|
}
|
|
@@ -466,6 +475,7 @@ export async function clearSlackUserToken(): Promise<SlackChannelConfigResult> {
|
|
|
466
475
|
hasUserToken,
|
|
467
476
|
connected:
|
|
468
477
|
!!(conn && conn.status === "active") && hasBotToken && hasAppToken,
|
|
478
|
+
threadMode: getConfig().slack.threadMode,
|
|
469
479
|
error:
|
|
470
480
|
result === "not-found"
|
|
471
481
|
? "Slack user token not found in secure storage"
|
|
@@ -497,6 +507,7 @@ export async function clearSlackUserToken(): Promise<SlackChannelConfigResult> {
|
|
|
497
507
|
...(teamUrl ? { teamUrl } : {}),
|
|
498
508
|
...(botUserId ? { botUserId } : {}),
|
|
499
509
|
...(botUsername ? { botUsername } : {}),
|
|
510
|
+
threadMode: getConfig().slack.threadMode,
|
|
500
511
|
};
|
|
501
512
|
}
|
|
502
513
|
|
|
@@ -530,6 +541,7 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
530
541
|
hasUserToken,
|
|
531
542
|
connected:
|
|
532
543
|
!!(conn && conn.status === "active") && hasBotToken && hasAppToken,
|
|
544
|
+
threadMode: getConfig().slack.threadMode,
|
|
533
545
|
error: "Failed to delete Slack channel credentials from secure storage",
|
|
534
546
|
};
|
|
535
547
|
}
|
|
@@ -556,5 +568,13 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
556
568
|
hasAppToken: false,
|
|
557
569
|
hasUserToken: false,
|
|
558
570
|
connected: false,
|
|
571
|
+
threadMode: "mention_only",
|
|
559
572
|
};
|
|
560
573
|
}
|
|
574
|
+
|
|
575
|
+
export function patchSlackChannelConfig(threadMode: SlackThreadMode): void {
|
|
576
|
+
const raw = loadRawConfig();
|
|
577
|
+
setNestedValue(raw, "slack.threadMode", threadMode);
|
|
578
|
+
saveRawConfig(raw);
|
|
579
|
+
invalidateConfigCache();
|
|
580
|
+
}
|
|
@@ -3,14 +3,21 @@ import { v4 as uuid } from "uuid";
|
|
|
3
3
|
import { clearAll, getConversation } from "../../memory/conversation-crud.js";
|
|
4
4
|
import { resolveConversationId } from "../../memory/conversation-key-store.js";
|
|
5
5
|
import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
|
|
6
|
+
import { resolveCapabilities } from "../../runtime/capabilities.js";
|
|
6
7
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
7
8
|
import { createAbortReason } from "../../util/abort-reasons.js";
|
|
9
|
+
import { UserError } from "../../util/errors.js";
|
|
8
10
|
import { truncate } from "../../util/truncate.js";
|
|
9
11
|
import { regenerate } from "../conversation-history.js";
|
|
12
|
+
import {
|
|
13
|
+
buildSlashContext,
|
|
14
|
+
formatCleanResult,
|
|
15
|
+
} from "../conversation-process.js";
|
|
10
16
|
import {
|
|
11
17
|
conversationEntries,
|
|
12
18
|
findConversation,
|
|
13
19
|
} from "../conversation-registry.js";
|
|
20
|
+
import { resolveSlash } from "../conversation-slash.js";
|
|
14
21
|
import {
|
|
15
22
|
clearAllActiveConversations,
|
|
16
23
|
getOrCreateConversation,
|
|
@@ -18,6 +25,7 @@ import {
|
|
|
18
25
|
} from "../conversation-store.js";
|
|
19
26
|
import type { ConfirmationResponse } from "../message-protocol.js";
|
|
20
27
|
import { normalizeConversationType } from "../message-protocol.js";
|
|
28
|
+
import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../trust-context.js";
|
|
21
29
|
import { log } from "./shared.js";
|
|
22
30
|
|
|
23
31
|
export function handleConfirmationResponse(msg: ConfirmationResponse): void {
|
|
@@ -98,6 +106,12 @@ export function cancelGeneration(conversationId: string): boolean {
|
|
|
98
106
|
// being cancelled, so enqueuing synthetic messages would trigger
|
|
99
107
|
// unwanted model activity after the user pressed stop.
|
|
100
108
|
getSubagentManager().abortAllForParent(conversationId);
|
|
109
|
+
// The processing flag is cleared by the in-flight turn's `finally`, not here.
|
|
110
|
+
// Abort propagates into the provider call and tool execution (and is backed
|
|
111
|
+
// by the agent loop's abort watchdog), so the turn reaches its `finally`
|
|
112
|
+
// within a bounded time and tears down its own state there — which publishes
|
|
113
|
+
// the metadata sync invalidation that drives clients to the authoritative
|
|
114
|
+
// idle state.
|
|
101
115
|
return true;
|
|
102
116
|
}
|
|
103
117
|
|
|
@@ -118,6 +132,99 @@ export async function undoLastMessage(
|
|
|
118
132
|
const removedCount = conversation.undo();
|
|
119
133
|
return { removedCount };
|
|
120
134
|
}
|
|
135
|
+
|
|
136
|
+
export interface MetaSlashCommandResult {
|
|
137
|
+
kind: "clean" | "info";
|
|
138
|
+
/** User-facing text to render (clean stats card or info listing). */
|
|
139
|
+
text: string;
|
|
140
|
+
/** Present for `/clean`: the post-strip context-window usage. */
|
|
141
|
+
contextUsage?: {
|
|
142
|
+
tokens: number;
|
|
143
|
+
maxTokens: number | null;
|
|
144
|
+
fillRatio: number | null;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Resolve a local meta slash command (`/clean`, `/status`, `/commands`,
|
|
150
|
+
* `/models`) for a conversation without running a turn: no user/assistant
|
|
151
|
+
* messages are persisted and no streaming/turn events are emitted. `/clean`
|
|
152
|
+
* additionally strips runtime injections via `forceClean`.
|
|
153
|
+
*
|
|
154
|
+
* Returns null if the conversation cannot be resolved. Throws `UserError` for
|
|
155
|
+
* commands that are not local meta commands (`/compact`, passthrough) — those
|
|
156
|
+
* must be sent as a normal message so they run a real turn.
|
|
157
|
+
*/
|
|
158
|
+
export async function resolveMetaSlashCommand(
|
|
159
|
+
conversationId: string,
|
|
160
|
+
command: string,
|
|
161
|
+
): Promise<MetaSlashCommandResult | null> {
|
|
162
|
+
const resolvedId = resolveConversationId(conversationId);
|
|
163
|
+
if (!resolvedId) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const conversation = await getOrCreateConversation(resolvedId);
|
|
167
|
+
touchConversation(resolvedId);
|
|
168
|
+
|
|
169
|
+
// Meta commands reload (and, for `/clean`, strip) the in-memory history
|
|
170
|
+
// array. Running that against an in-flight turn would corrupt the messages
|
|
171
|
+
// the active agent loop is iterating over, and mutating trustContext would
|
|
172
|
+
// elevate that turn's actor trust to guardian for subsequent tool calls.
|
|
173
|
+
if (conversation.isProcessing()) {
|
|
174
|
+
throw new UserError(
|
|
175
|
+
`\`${command.trim()}\` cannot run while the assistant is responding.`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Owner self-maintenance operates on the full (guardian) history. Without a
|
|
180
|
+
// trusted context, `loadFromDb` filters to non-guardian provenance — so a
|
|
181
|
+
// guardian-authored conversation would report 0 preserved / 0 messages.
|
|
182
|
+
// Temporarily apply the guardian context for hydration and restore it
|
|
183
|
+
// afterward so the elevated class never leaks into a later turn's snapshot.
|
|
184
|
+
const priorTrustContext = conversation.trustContext;
|
|
185
|
+
if (!resolveCapabilities(priorTrustContext?.trustClass).canAccessMemory) {
|
|
186
|
+
conversation.setTrustContext(INTERNAL_GUARDIAN_TRUST_CONTEXT);
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
await conversation.ensureActorScopedHistory();
|
|
190
|
+
|
|
191
|
+
const slashResult = await resolveSlash(
|
|
192
|
+
command,
|
|
193
|
+
buildSlashContext(command, conversation),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (slashResult.kind === "clean") {
|
|
197
|
+
const result = await conversation.forceClean();
|
|
198
|
+
return {
|
|
199
|
+
kind: "clean",
|
|
200
|
+
text: formatCleanResult(result),
|
|
201
|
+
contextUsage: {
|
|
202
|
+
tokens: result.estimatedInputTokens,
|
|
203
|
+
maxTokens: result.maxInputTokens,
|
|
204
|
+
fillRatio:
|
|
205
|
+
result.maxInputTokens > 0
|
|
206
|
+
? result.estimatedInputTokens / result.maxInputTokens
|
|
207
|
+
: null,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (slashResult.kind === "unknown") {
|
|
213
|
+
return { kind: "info", text: slashResult.message };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// `compact` / `passthrough` are real turns, not local meta commands.
|
|
217
|
+
throw new UserError(`\`${command.trim()}\` is not a local meta command.`);
|
|
218
|
+
} finally {
|
|
219
|
+
// Only undo the temporary guardian context this handler installed. If a
|
|
220
|
+
// new turn started at an `await` boundary and legitimately updated
|
|
221
|
+
// trustContext, the reference will differ and we leave it alone.
|
|
222
|
+
if (conversation.trustContext === INTERNAL_GUARDIAN_TRUST_CONTEXT) {
|
|
223
|
+
conversation.setTrustContext(priorTrustContext ?? null);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
121
228
|
/**
|
|
122
229
|
* Regenerate the last assistant response for a conversation. The caller provides
|
|
123
230
|
* a `sendEvent` callback for delivering streaming events via HTTP/SSE.
|
|
@@ -9,6 +9,7 @@ import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
|
9
9
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
10
10
|
import { AssistantError, ErrorCode } from "../util/errors.js";
|
|
11
11
|
import { getLogger } from "../util/logger.js";
|
|
12
|
+
import { sleep } from "../util/retry.js";
|
|
12
13
|
import type { HostBrowserRequest } from "./message-types/host-browser.js";
|
|
13
14
|
|
|
14
15
|
/** Distributive omit that preserves union variant fields. */
|
|
@@ -24,6 +25,16 @@ export type HostBrowserInput = DistributiveOmit<
|
|
|
24
25
|
|
|
25
26
|
const log = getLogger("host-browser-proxy");
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Grace window for a flapping extension SSE connection. The Chrome
|
|
30
|
+
* extension's stream can briefly drop and reconnect (MV3 service-worker
|
|
31
|
+
* churn); without a short wait, a dispatch landing in a "down" blip
|
|
32
|
+
* hard-fails even though the extension is effectively connected. Well
|
|
33
|
+
* under the request-layer timeout (default 30s).
|
|
34
|
+
*/
|
|
35
|
+
const EXTENSION_RECONNECT_GRACE_MS = 3_000;
|
|
36
|
+
const EXTENSION_RECONNECT_POLL_MS = 250;
|
|
37
|
+
|
|
27
38
|
/**
|
|
28
39
|
* Extension-only pseudo-CDP methods (Vellum.listTabs, Vellum.selectTab,
|
|
29
40
|
* Vellum.closeTab, Vellum.createTab, Vellum.findTab, Vellum.attach,
|
|
@@ -196,6 +207,36 @@ export class HostBrowserProxy {
|
|
|
196
207
|
);
|
|
197
208
|
}
|
|
198
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Poll until the extension client that dispatch will route to is
|
|
212
|
+
* connected, or `timeoutMs` elapses. Absorbs brief SSE reconnect blips
|
|
213
|
+
* so extension-pinned dispatch doesn't hard-fail on a momentary gap.
|
|
214
|
+
*
|
|
215
|
+
* When `targetClientId` is supplied, waits for that *exact* client —
|
|
216
|
+
* `request()` resolves the target by id, so waiting for any sibling
|
|
217
|
+
* extension (multi-install setups) would return early and still fail.
|
|
218
|
+
* Otherwise waits for any extension owned by the actor, matching the
|
|
219
|
+
* auto-resolution preference. Returns the final availability; callers
|
|
220
|
+
* proceed to dispatch regardless (which still surfaces the typed "no
|
|
221
|
+
* Chrome Extension connected" error if absent).
|
|
222
|
+
*/
|
|
223
|
+
async waitForExtensionClient(
|
|
224
|
+
sourceActorPrincipalId?: string,
|
|
225
|
+
targetClientId?: string,
|
|
226
|
+
timeoutMs = EXTENSION_RECONNECT_GRACE_MS,
|
|
227
|
+
): Promise<boolean> {
|
|
228
|
+
const connected = () =>
|
|
229
|
+
targetClientId != null
|
|
230
|
+
? assistantEventHub.getClientById(targetClientId) !== undefined
|
|
231
|
+
: this.hasExtensionClient(sourceActorPrincipalId);
|
|
232
|
+
const deadline = Date.now() + timeoutMs;
|
|
233
|
+
for (;;) {
|
|
234
|
+
if (connected()) return true;
|
|
235
|
+
if (Date.now() >= deadline) return false;
|
|
236
|
+
await sleep(EXTENSION_RECONNECT_POLL_MS);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
199
240
|
/**
|
|
200
241
|
* Send a host_browser request to the connected extension/macOS bridge.
|
|
201
242
|
*
|