@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
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/AGENTS.md +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -90,7 +90,6 @@ export interface AclEnforcementParams {
|
|
|
90
90
|
rawSenderId: string | undefined;
|
|
91
91
|
sourceChannel: ChannelId;
|
|
92
92
|
conversationExternalId: string;
|
|
93
|
-
canonicalAssistantId: string;
|
|
94
93
|
trimmedContent: string;
|
|
95
94
|
sourceMetadata: SourceMetadata | undefined;
|
|
96
95
|
actorDisplayName: string | undefined;
|
|
@@ -190,7 +189,6 @@ export async function enforceIngressAcl(
|
|
|
190
189
|
rawSenderId,
|
|
191
190
|
sourceChannel,
|
|
192
191
|
conversationExternalId,
|
|
193
|
-
canonicalAssistantId,
|
|
194
192
|
trimmedContent,
|
|
195
193
|
sourceMetadata,
|
|
196
194
|
actorDisplayName,
|
|
@@ -426,7 +424,6 @@ export async function enforceIngressAcl(
|
|
|
426
424
|
// Still notify the guardian about the access attempt
|
|
427
425
|
try {
|
|
428
426
|
await notifyGuardianOfAccessRequest({
|
|
429
|
-
canonicalAssistantId,
|
|
430
427
|
sourceChannel,
|
|
431
428
|
conversationExternalId,
|
|
432
429
|
actorExternalId: canonicalSenderId ?? rawSenderId,
|
|
@@ -509,7 +506,6 @@ export async function enforceIngressAcl(
|
|
|
509
506
|
if (emailVerifyResult.initiated) {
|
|
510
507
|
try {
|
|
511
508
|
await notifyGuardianOfAccessRequest({
|
|
512
|
-
canonicalAssistantId,
|
|
513
509
|
sourceChannel,
|
|
514
510
|
conversationExternalId,
|
|
515
511
|
actorExternalId: canonicalSenderId ?? rawSenderId,
|
|
@@ -554,14 +550,12 @@ export async function enforceIngressAcl(
|
|
|
554
550
|
let handshakeInProgress = false;
|
|
555
551
|
if (isCallbackInteraction) {
|
|
556
552
|
handshakeInProgress = await isApprovalHandshakeInProgress({
|
|
557
|
-
canonicalAssistantId,
|
|
558
553
|
sourceChannel,
|
|
559
554
|
actorExternalId: (canonicalSenderId ?? rawSenderId)!,
|
|
560
555
|
});
|
|
561
556
|
} else {
|
|
562
557
|
try {
|
|
563
558
|
const accessResult = await notifyGuardianOfAccessRequest({
|
|
564
|
-
canonicalAssistantId,
|
|
565
559
|
sourceChannel,
|
|
566
560
|
conversationExternalId,
|
|
567
561
|
actorExternalId: canonicalSenderId ?? rawSenderId,
|
|
@@ -730,7 +724,6 @@ export async function enforceIngressAcl(
|
|
|
730
724
|
if (slackVerifyResult.initiated) {
|
|
731
725
|
try {
|
|
732
726
|
await notifyGuardianOfAccessRequest({
|
|
733
|
-
canonicalAssistantId,
|
|
734
727
|
sourceChannel,
|
|
735
728
|
conversationExternalId,
|
|
736
729
|
actorExternalId: canonicalSenderId ?? rawSenderId,
|
|
@@ -803,14 +796,12 @@ export async function enforceIngressAcl(
|
|
|
803
796
|
if (!terminallyKeptOut) {
|
|
804
797
|
if (isCallbackInteraction) {
|
|
805
798
|
handshakeInProgress = await isApprovalHandshakeInProgress({
|
|
806
|
-
canonicalAssistantId,
|
|
807
799
|
sourceChannel,
|
|
808
800
|
actorExternalId: (canonicalSenderId ?? rawSenderId)!,
|
|
809
801
|
});
|
|
810
802
|
} else {
|
|
811
803
|
try {
|
|
812
804
|
const accessResult = await notifyGuardianOfAccessRequest({
|
|
813
|
-
canonicalAssistantId,
|
|
814
805
|
sourceChannel,
|
|
815
806
|
conversationExternalId,
|
|
816
807
|
actorExternalId: canonicalSenderId ?? rawSenderId,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
ADMISSION_FLOOR,
|
|
28
28
|
type AdmissionPolicy,
|
|
29
29
|
isAdmissionPolicyExemptChannel,
|
|
30
|
+
TRUST_CLASS_RANK,
|
|
30
31
|
} from "@vellumai/gateway-client";
|
|
31
32
|
|
|
32
33
|
import type { ChannelId } from "../../../channels/types.js";
|
|
@@ -73,23 +74,6 @@ export type AdmissionPolicyResult =
|
|
|
73
74
|
// Public API
|
|
74
75
|
// ---------------------------------------------------------------------------
|
|
75
76
|
|
|
76
|
-
/**
|
|
77
|
-
* Trust-class ordinal compared against {@link ADMISSION_FLOOR} to make the
|
|
78
|
-
* admission decision (`rank >= floor`). Higher rank = more trusted.
|
|
79
|
-
* Blocked/revoked never reach this comparison — they short-circuit to deny on
|
|
80
|
-
* member status above — so they carry no rank here.
|
|
81
|
-
*
|
|
82
|
-
* The two halves of the floor check: this table is keyed by `TrustClass`,
|
|
83
|
-
* `ADMISSION_FLOOR` (from `@vellumai/gateway-client`) by `AdmissionPolicy`.
|
|
84
|
-
* They are only ever read together, here, so they live next to their use.
|
|
85
|
-
*/
|
|
86
|
-
const TRUST_CLASS_RANK: Record<TrustClass, number> = {
|
|
87
|
-
guardian: 4,
|
|
88
|
-
trusted_contact: 3,
|
|
89
|
-
unverified_contact: 2,
|
|
90
|
-
unknown: 1,
|
|
91
|
-
};
|
|
92
|
-
|
|
93
77
|
/**
|
|
94
78
|
* Policies under which completing verification could lift the sender past
|
|
95
79
|
* the floor. Used to decide whether to fire the upgrade UX on deny.
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import type { ChannelId } from "../../../channels/types.js";
|
|
25
25
|
import { sendTelegramReply } from "../../../messaging/providers/telegram-bot/send.js";
|
|
26
26
|
import { getLogger } from "../../../util/logger.js";
|
|
27
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
|
|
27
28
|
import { RESEND_COOLDOWN_MS } from "../../verification-outbound-actions.js";
|
|
28
29
|
import {
|
|
29
30
|
composeVerificationText,
|
|
@@ -40,7 +41,6 @@ export interface BootstrapInterceptParams {
|
|
|
40
41
|
isDuplicate: boolean;
|
|
41
42
|
commandIntent: Record<string, unknown> | undefined;
|
|
42
43
|
rawSenderId: string | undefined;
|
|
43
|
-
canonicalAssistantId: string;
|
|
44
44
|
sourceChannel: ChannelId;
|
|
45
45
|
conversationExternalId: string;
|
|
46
46
|
eventId: string;
|
|
@@ -96,7 +96,6 @@ export async function handleBootstrapIntercept(
|
|
|
96
96
|
isDuplicate,
|
|
97
97
|
commandIntent,
|
|
98
98
|
rawSenderId,
|
|
99
|
-
canonicalAssistantId,
|
|
100
99
|
sourceChannel,
|
|
101
100
|
conversationExternalId,
|
|
102
101
|
eventId,
|
|
@@ -208,7 +207,7 @@ export async function handleBootstrapIntercept(
|
|
|
208
207
|
deliverBootstrapVerificationTelegram(
|
|
209
208
|
conversationExternalId,
|
|
210
209
|
telegramBody,
|
|
211
|
-
|
|
210
|
+
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
212
211
|
);
|
|
213
212
|
|
|
214
213
|
// Update delivery tracking (best-effort — the code is already on its way)
|
|
@@ -11,20 +11,23 @@
|
|
|
11
11
|
* the message's metadata via a single transactional content+metadata update,
|
|
12
12
|
* so downstream renderers can surface the edited marker.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* An edit belongs to the conversation of the message it changes, so the stage
|
|
15
|
+
* resolves that message before it records anything, and an edit with no
|
|
16
|
+
* resolvable target is dropped.
|
|
16
17
|
*/
|
|
17
18
|
import type { ChannelId } from "../../../channels/types.js";
|
|
18
19
|
import {
|
|
19
20
|
mergeSlackMetadata,
|
|
20
21
|
readSlackMetadata,
|
|
21
22
|
} from "../../../messaging/providers/slack/message-metadata.js";
|
|
23
|
+
import type { MessageRow } from "../../../persistence/conversation-crud.js";
|
|
22
24
|
import {
|
|
23
25
|
getMessageById,
|
|
24
26
|
updateMessageContent,
|
|
25
27
|
updateMessageContentAndMetadata,
|
|
26
28
|
} from "../../../persistence/conversation-crud.js";
|
|
27
29
|
import {
|
|
30
|
+
findInboundEvent,
|
|
28
31
|
findMessageBySourceId,
|
|
29
32
|
recordInbound,
|
|
30
33
|
} from "../../../persistence/delivery-crud.js";
|
|
@@ -45,7 +48,6 @@ export interface EditInterceptParams {
|
|
|
45
48
|
externalMessageId: string;
|
|
46
49
|
sourceMessageId: string;
|
|
47
50
|
sourceThreadId?: string;
|
|
48
|
-
canonicalAssistantId: string;
|
|
49
51
|
assistantId: string;
|
|
50
52
|
content: string | undefined;
|
|
51
53
|
/** Channel ID for channel-level interaction tracking. */
|
|
@@ -68,24 +70,23 @@ export async function handleEditIntercept(
|
|
|
68
70
|
externalMessageId,
|
|
69
71
|
sourceMessageId,
|
|
70
72
|
sourceThreadId,
|
|
71
|
-
canonicalAssistantId,
|
|
72
73
|
assistantId,
|
|
73
74
|
content,
|
|
74
75
|
} = params;
|
|
75
76
|
|
|
76
|
-
// Dedup the edit event itself (retried edited_message webhooks)
|
|
77
|
-
|
|
77
|
+
// Dedup the edit event itself (retried edited_message webhooks) before the
|
|
78
|
+
// lookup below, so a redelivery answers immediately instead of waiting out
|
|
79
|
+
// the retry window a second time.
|
|
80
|
+
const alreadyRecorded = findInboundEvent(
|
|
78
81
|
sourceChannel,
|
|
79
82
|
conversationExternalId,
|
|
80
83
|
externalMessageId,
|
|
81
|
-
{ sourceMessageId, sourceThreadId, assistantId: canonicalAssistantId },
|
|
82
84
|
);
|
|
83
|
-
|
|
84
|
-
if (editResult.duplicate) {
|
|
85
|
+
if (alreadyRecorded) {
|
|
85
86
|
return {
|
|
86
87
|
accepted: true,
|
|
87
88
|
duplicate: true,
|
|
88
|
-
eventId:
|
|
89
|
+
eventId: alreadyRecorded.eventId,
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -119,81 +120,100 @@ export async function handleEditIntercept(
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
if (original) {
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
assistantId,
|
|
138
|
-
sourceChannel,
|
|
139
|
-
sourceMessageId,
|
|
140
|
-
messageId: original.messageId,
|
|
141
|
-
},
|
|
142
|
-
"Edit text unchanged; skipping update",
|
|
143
|
-
);
|
|
144
|
-
return {
|
|
145
|
-
accepted: true,
|
|
146
|
-
duplicate: false,
|
|
147
|
-
noop: true,
|
|
148
|
-
eventId: editResult.eventId,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
if (sourceChannel === "slack") {
|
|
152
|
-
// Slack edits stamp `slackMeta.editedAt` so the chronological
|
|
153
|
-
// transcript renderer can surface the edited marker. The merge
|
|
154
|
-
// tolerates rows that lack slackMeta enrichment by synthesizing
|
|
155
|
-
// the minimum-required fields from the lookup data.
|
|
156
|
-
applySlackEditMetadata({
|
|
157
|
-
messageId: original.messageId,
|
|
158
|
-
conversationExternalId,
|
|
159
|
-
sourceMessageId,
|
|
160
|
-
sourceThreadId,
|
|
161
|
-
newContent,
|
|
162
|
-
});
|
|
163
|
-
} else {
|
|
164
|
-
updateMessageContent(original.messageId, newContent);
|
|
165
|
-
}
|
|
166
|
-
// The edit changed searchable text (the no-op guard above already returned
|
|
167
|
-
// for identical content) and this path bypasses the `addMessage` persist
|
|
168
|
-
// path, so reindex the message into the lexical index — the idempotent
|
|
169
|
-
// upsert replaces the stale Qdrant point with the edited content.
|
|
170
|
-
enqueueLexicalIndexForMessage(original.messageId);
|
|
171
|
-
log.info(
|
|
172
|
-
{ assistantId, sourceMessageId, messageId: original.messageId },
|
|
173
|
-
"Updated message content from edited_message",
|
|
174
|
-
);
|
|
175
|
-
} else {
|
|
176
|
-
// For Slack, treat missing-target edits as `debug` (the row may have
|
|
177
|
-
// been compacted, never stored, or pre-date this upgrade); for other
|
|
178
|
-
// channels, retain the louder `warn` since their edit pipelines
|
|
179
|
-
// historically expect the row to exist.
|
|
123
|
+
if (!original) {
|
|
124
|
+
// Nothing to edit, and nothing to record it against: recording it anyway
|
|
125
|
+
// would resolve a conversation from the edit's own address and create one
|
|
126
|
+
// there.
|
|
127
|
+
//
|
|
128
|
+
// Slack keeps this at `debug`, since the row may have been compacted or
|
|
129
|
+
// never stored. Other channels keep the louder `warn`, since their edit
|
|
130
|
+
// pipelines expect the row to exist.
|
|
131
|
+
const missingTarget = {
|
|
132
|
+
assistantId,
|
|
133
|
+
sourceChannel,
|
|
134
|
+
conversationExternalId,
|
|
135
|
+
sourceMessageId,
|
|
136
|
+
};
|
|
180
137
|
if (sourceChannel === "slack") {
|
|
181
|
-
log.debug(
|
|
182
|
-
{
|
|
183
|
-
assistantId,
|
|
184
|
-
sourceChannel,
|
|
185
|
-
channelId: conversationExternalId,
|
|
186
|
-
externalMessageId: sourceMessageId,
|
|
187
|
-
},
|
|
188
|
-
"Slack edit target not found, ignoring",
|
|
189
|
-
);
|
|
138
|
+
log.debug(missingTarget, "Slack edit target not found, ignoring");
|
|
190
139
|
} else {
|
|
191
140
|
log.warn(
|
|
192
|
-
|
|
141
|
+
missingTarget,
|
|
193
142
|
"Could not find original message for edit after retries, ignoring",
|
|
194
143
|
);
|
|
195
144
|
}
|
|
145
|
+
return { accepted: true, duplicate: false, edited: false };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// The edit belongs to the conversation of the message it changes. Resolving
|
|
149
|
+
// one from the edit's own address would key it on the edited message's
|
|
150
|
+
// timestamp and create a conversation there.
|
|
151
|
+
const editResult = recordInbound(
|
|
152
|
+
sourceChannel,
|
|
153
|
+
conversationExternalId,
|
|
154
|
+
externalMessageId,
|
|
155
|
+
{ sourceMessageId, conversationId: original.conversationId },
|
|
156
|
+
);
|
|
157
|
+
if (editResult.duplicate) {
|
|
158
|
+
// Two deliveries of one edit raced through the probe above while neither
|
|
159
|
+
// had recorded yet. The first owns the write; applying it twice would
|
|
160
|
+
// re-enqueue lexical indexing for no change.
|
|
161
|
+
return { accepted: true, duplicate: true, eventId: editResult.eventId };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const newContent = content ?? "";
|
|
165
|
+
// Short-circuit no-op edits: Slack fires `message_changed` for link
|
|
166
|
+
// unfurls and other decorations where the text is identical to the
|
|
167
|
+
// previous revision. Skipping the DB write here covers that case and
|
|
168
|
+
// also drops trivially-redundant edit webhooks. We only have the
|
|
169
|
+
// authoritative previous text once the original row is located, so
|
|
170
|
+
// this check lives after the lookup.
|
|
171
|
+
const existingRow = getMessageById(original.messageId);
|
|
172
|
+
if (
|
|
173
|
+
existingRow &&
|
|
174
|
+
stringifyMessageContent(existingRow.content) === newContent
|
|
175
|
+
) {
|
|
176
|
+
log.debug(
|
|
177
|
+
{
|
|
178
|
+
assistantId,
|
|
179
|
+
sourceChannel,
|
|
180
|
+
sourceMessageId,
|
|
181
|
+
messageId: original.messageId,
|
|
182
|
+
},
|
|
183
|
+
"Edit text unchanged; skipping update",
|
|
184
|
+
);
|
|
185
|
+
return {
|
|
186
|
+
accepted: true,
|
|
187
|
+
duplicate: false,
|
|
188
|
+
noop: true,
|
|
189
|
+
eventId: editResult.eventId,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (sourceChannel === "slack") {
|
|
193
|
+
// Slack edits stamp `slackMeta.editedAt` so the chronological
|
|
194
|
+
// transcript renderer can surface the edited marker. The merge
|
|
195
|
+
// tolerates rows that lack slackMeta enrichment by synthesizing
|
|
196
|
+
// the minimum-required fields from the lookup data.
|
|
197
|
+
applySlackEditMetadata({
|
|
198
|
+
messageId: original.messageId,
|
|
199
|
+
existingRow,
|
|
200
|
+
conversationExternalId,
|
|
201
|
+
sourceMessageId,
|
|
202
|
+
sourceThreadId,
|
|
203
|
+
newContent,
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
updateMessageContent(original.messageId, newContent);
|
|
196
207
|
}
|
|
208
|
+
// The edit changed searchable text (the no-op guard above already returned
|
|
209
|
+
// for identical content) and this path bypasses the `addMessage` persist
|
|
210
|
+
// path, so reindex the message into the lexical index — the idempotent
|
|
211
|
+
// upsert replaces the stale Qdrant point with the edited content.
|
|
212
|
+
enqueueLexicalIndexForMessage(original.messageId);
|
|
213
|
+
log.info(
|
|
214
|
+
{ assistantId, sourceMessageId, messageId: original.messageId },
|
|
215
|
+
"Updated message content from edited_message",
|
|
216
|
+
);
|
|
197
217
|
|
|
198
218
|
return {
|
|
199
219
|
accepted: true,
|
|
@@ -219,6 +239,8 @@ export async function handleEditIntercept(
|
|
|
219
239
|
*/
|
|
220
240
|
function applySlackEditMetadata(params: {
|
|
221
241
|
messageId: string;
|
|
242
|
+
/** The stored row, already read by the caller's no-op guard. */
|
|
243
|
+
existingRow: MessageRow | null;
|
|
222
244
|
conversationExternalId: string;
|
|
223
245
|
sourceMessageId: string;
|
|
224
246
|
sourceThreadId?: string;
|
|
@@ -226,13 +248,13 @@ function applySlackEditMetadata(params: {
|
|
|
226
248
|
}): void {
|
|
227
249
|
const {
|
|
228
250
|
messageId,
|
|
251
|
+
existingRow: row,
|
|
229
252
|
conversationExternalId,
|
|
230
253
|
sourceMessageId,
|
|
231
254
|
sourceThreadId,
|
|
232
255
|
newContent,
|
|
233
256
|
} = params;
|
|
234
257
|
|
|
235
|
-
const row = getMessageById(messageId);
|
|
236
258
|
const outerMetadata: Record<string, unknown> =
|
|
237
259
|
row?.metadata != null ? safeParseRecord(row.metadata) : {};
|
|
238
260
|
const existingSlackMeta =
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "../../../channels/gateway-guardian-requests.js";
|
|
16
16
|
import type { ChannelId } from "../../../channels/types.js";
|
|
17
17
|
import { getLogger } from "../../../util/logger.js";
|
|
18
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
|
|
18
19
|
import { deliverChannelReply } from "../../gateway-client.js";
|
|
19
20
|
import {
|
|
20
21
|
type GuardianPendingScope,
|
|
@@ -41,11 +42,16 @@ export interface GuardianReplyInterceptParams {
|
|
|
41
42
|
reactedMessageTs?: string;
|
|
42
43
|
rawSenderId: string | undefined;
|
|
43
44
|
canonicalSenderId: string | null;
|
|
44
|
-
canonicalAssistantId: string;
|
|
45
45
|
sourceChannel: ChannelId;
|
|
46
46
|
conversationExternalId: string;
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Conversation the message arrived in. Reactions resolve their target by the
|
|
49
|
+
* reacted card's own address and are routed before any conversation is
|
|
50
|
+
* known, so they pass none.
|
|
51
|
+
*/
|
|
52
|
+
conversationId?: string;
|
|
53
|
+
/** Inbound event id, echoed on the consumed response when the caller has one. */
|
|
54
|
+
eventId?: string;
|
|
49
55
|
replyCallbackUrl: string | undefined;
|
|
50
56
|
trustClass: string;
|
|
51
57
|
guardianPrincipalId: string | null | undefined;
|
|
@@ -77,7 +83,6 @@ export async function handleGuardianReplyIntercept(
|
|
|
77
83
|
reactedMessageTs,
|
|
78
84
|
rawSenderId,
|
|
79
85
|
canonicalSenderId,
|
|
80
|
-
canonicalAssistantId,
|
|
81
86
|
sourceChannel,
|
|
82
87
|
conversationExternalId,
|
|
83
88
|
conversationId,
|
|
@@ -178,7 +183,7 @@ export async function handleGuardianReplyIntercept(
|
|
|
178
183
|
channelDeliveryContext: {
|
|
179
184
|
replyCallbackUrl,
|
|
180
185
|
guardianChatId: conversationExternalId,
|
|
181
|
-
assistantId:
|
|
186
|
+
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
182
187
|
},
|
|
183
188
|
});
|
|
184
189
|
|
|
@@ -188,7 +193,7 @@ export async function handleGuardianReplyIntercept(
|
|
|
188
193
|
const routerReplyPayload: Parameters<typeof deliverChannelReply>[1] = {
|
|
189
194
|
chatId: conversationExternalId,
|
|
190
195
|
text: routerResult.replyText,
|
|
191
|
-
assistantId:
|
|
196
|
+
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
192
197
|
};
|
|
193
198
|
// On Slack, send guardian management replies (disambiguation, pending
|
|
194
199
|
// request lists, etc.) as ephemeral so only the guardian sees them.
|
|
@@ -210,7 +215,7 @@ export async function handleGuardianReplyIntercept(
|
|
|
210
215
|
response: {
|
|
211
216
|
accepted: true,
|
|
212
217
|
duplicate: false,
|
|
213
|
-
eventId,
|
|
218
|
+
...(eventId ? { eventId } : {}),
|
|
214
219
|
canonicalRouter: routerResult.type,
|
|
215
220
|
requestId: routerResult.requestId,
|
|
216
221
|
},
|
|
@@ -69,17 +69,28 @@ mock.module("../../../contacts/contact-store.js", () => ({
|
|
|
69
69
|
// Downstream side-effect stubs
|
|
70
70
|
// ---------------------------------------------------------------------------
|
|
71
71
|
|
|
72
|
-
let recordInboundCalls =
|
|
72
|
+
let recordInboundCalls: Array<{ conversationId?: string }> = [];
|
|
73
|
+
let recordedEvent: { eventId: string; conversationId: string } | null = null;
|
|
74
|
+
let outboundTargetConversationId: string | null = null;
|
|
75
|
+
let storedTarget: { messageId: string; conversationId: string } | null = null;
|
|
73
76
|
mock.module("../../../persistence/delivery-crud.js", () => ({
|
|
74
|
-
recordInbound: (
|
|
75
|
-
|
|
77
|
+
recordInbound: (
|
|
78
|
+
_channel: string,
|
|
79
|
+
_chat: string,
|
|
80
|
+
_externalMessageId: string,
|
|
81
|
+
options?: { conversationId?: string },
|
|
82
|
+
) => {
|
|
83
|
+
recordInboundCalls.push({ conversationId: options?.conversationId });
|
|
76
84
|
return {
|
|
77
85
|
eventId: "evt-1",
|
|
78
|
-
conversationId: "conv-
|
|
86
|
+
conversationId: options?.conversationId ?? "conv-minted",
|
|
79
87
|
accepted: true,
|
|
80
88
|
duplicate: false,
|
|
81
89
|
};
|
|
82
90
|
},
|
|
91
|
+
findMessageBySourceId: () => storedTarget,
|
|
92
|
+
findSlackConversationByMessageTs: () => outboundTargetConversationId,
|
|
93
|
+
findInboundEvent: () => recordedEvent,
|
|
83
94
|
clearPayload: () => {},
|
|
84
95
|
linkMessage: () => {},
|
|
85
96
|
}));
|
|
@@ -172,7 +183,6 @@ function buildParams(overrides: {
|
|
|
172
183
|
sourceInterface: "slack" as const,
|
|
173
184
|
conversationExternalId: SLACK_CHANNEL_ID,
|
|
174
185
|
externalMessageId: `${SLACK_CHANNEL_ID}:1700000000.1:${msgCounter}`,
|
|
175
|
-
canonicalAssistantId: "assistant-1",
|
|
176
186
|
rawSenderId: overrides.rawSenderId,
|
|
177
187
|
canonicalSenderId: overrides.rawSenderId,
|
|
178
188
|
actorDisplayName: "Reactor",
|
|
@@ -185,7 +195,6 @@ function buildParams(overrides: {
|
|
|
185
195
|
? { trustVerdict: overrides.trustVerdict }
|
|
186
196
|
: {}),
|
|
187
197
|
} as never,
|
|
188
|
-
slackChannelName: "general",
|
|
189
198
|
approvalConversationGenerator: undefined,
|
|
190
199
|
};
|
|
191
200
|
}
|
|
@@ -194,7 +203,7 @@ function expectDropped(result: Record<string, unknown>): void {
|
|
|
194
203
|
expect(result.reaction).toBe("dropped_unknown_actor");
|
|
195
204
|
// Dropped before any write or routing: no dedup record, no transcript row,
|
|
196
205
|
// no approval-pipeline dispatch.
|
|
197
|
-
expect(recordInboundCalls).toBe(0);
|
|
206
|
+
expect(recordInboundCalls.length).toBe(0);
|
|
198
207
|
expect(addMessageCalls).toBe(0);
|
|
199
208
|
expect(guardianReplyCalls.length).toBe(0);
|
|
200
209
|
}
|
|
@@ -205,10 +214,13 @@ describe("reaction intercept consumes the stamped verdict directly", () => {
|
|
|
205
214
|
guardianDeliveryReads = 0;
|
|
206
215
|
setMemberVerdictCalls = 0;
|
|
207
216
|
contactLookups = 0;
|
|
208
|
-
recordInboundCalls =
|
|
217
|
+
recordInboundCalls = [];
|
|
218
|
+
recordedEvent = null;
|
|
219
|
+
outboundTargetConversationId = null;
|
|
209
220
|
addMessageCalls = 0;
|
|
210
221
|
guardianReplyCalls = [];
|
|
211
222
|
guardianReplyResponse = undefined;
|
|
223
|
+
storedTarget = { messageId: "msg-target", conversationId: "conv-target" };
|
|
212
224
|
});
|
|
213
225
|
|
|
214
226
|
test("guardian verdict routes the reaction into the approval decision pipeline", async () => {
|
|
@@ -245,6 +257,77 @@ describe("reaction intercept consumes the stamped verdict directly", () => {
|
|
|
245
257
|
expect(result.accepted).toBe(true);
|
|
246
258
|
expect(result.reaction).toBeUndefined();
|
|
247
259
|
expect(addMessageCalls).toBe(1);
|
|
260
|
+
// Recorded into the reacted message's conversation, never a fresh one.
|
|
261
|
+
expect(recordInboundCalls).toEqual([{ conversationId: "conv-target" }]);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("a reaction on the assistant's own post resolves through slackMeta", async () => {
|
|
265
|
+
// Outbound posts open no inbound event, so the ts is only on the row.
|
|
266
|
+
storedTarget = null;
|
|
267
|
+
outboundTargetConversationId = "conv-assistant-post";
|
|
268
|
+
|
|
269
|
+
const result = await handleSlackReactionIntercept(
|
|
270
|
+
buildParams({
|
|
271
|
+
rawSenderId: MEMBER_USER_ID,
|
|
272
|
+
trustVerdict: MEMBER_VERDICT,
|
|
273
|
+
}),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(recordInboundCalls).toEqual([
|
|
277
|
+
{ conversationId: "conv-assistant-post" },
|
|
278
|
+
]);
|
|
279
|
+
expect(addMessageCalls).toBe(1);
|
|
280
|
+
expect(result).toMatchObject({ accepted: true, duplicate: false });
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("a reaction on a message that is not stored is dropped without minting", async () => {
|
|
284
|
+
storedTarget = null;
|
|
285
|
+
|
|
286
|
+
const result = await handleSlackReactionIntercept(
|
|
287
|
+
buildParams({
|
|
288
|
+
rawSenderId: MEMBER_USER_ID,
|
|
289
|
+
trustVerdict: MEMBER_VERDICT,
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
expect(result.reaction).toBe("dropped_unknown_target");
|
|
294
|
+
expect(recordInboundCalls.length).toBe(0);
|
|
295
|
+
expect(addMessageCalls).toBe(0);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("a redelivered reaction never reaches the guardian rail twice", async () => {
|
|
299
|
+
recordedEvent = { eventId: "evt-1", conversationId: "conv-target" };
|
|
300
|
+
|
|
301
|
+
const result = await handleSlackReactionIntercept(
|
|
302
|
+
buildParams({
|
|
303
|
+
rawSenderId: GUARDIAN_USER_ID,
|
|
304
|
+
trustVerdict: GUARDIAN_VERDICT,
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
expect(result).toEqual({
|
|
309
|
+
accepted: true,
|
|
310
|
+
duplicate: true,
|
|
311
|
+
eventId: "evt-1",
|
|
312
|
+
});
|
|
313
|
+
expect(guardianReplyCalls.length).toBe(0);
|
|
314
|
+
expect(addMessageCalls).toBe(0);
|
|
315
|
+
expect(recordInboundCalls.length).toBe(0);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("a guardian card reaction still applies when the card is not a stored message", async () => {
|
|
319
|
+
storedTarget = null;
|
|
320
|
+
guardianReplyResponse = { accepted: true, canonicalRouter: "applied" };
|
|
321
|
+
|
|
322
|
+
const result = await handleSlackReactionIntercept(
|
|
323
|
+
buildParams({
|
|
324
|
+
rawSenderId: GUARDIAN_USER_ID,
|
|
325
|
+
trustVerdict: GUARDIAN_VERDICT,
|
|
326
|
+
}),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
expect(result).toEqual(guardianReplyResponse);
|
|
330
|
+
expect(recordInboundCalls.length).toBe(0);
|
|
248
331
|
});
|
|
249
332
|
|
|
250
333
|
test("unknown verdict is dropped before any write", async () => {
|