@vellumai/assistant 0.11.4 → 0.11.5-staging.1
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 +2 -2
- package/ARCHITECTURE.md +29 -4
- package/README.md +1 -1
- package/docs/architecture/memory.md +17 -5
- package/docs/architecture/security.md +96 -152
- package/docs/guardian-request-flow.md +13 -2
- 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/index.ts +1 -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/index.ts +1 -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/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +7 -14
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +29 -31
- 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/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +193 -3
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +10 -3
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +4 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -6
- package/src/__tests__/channel-reply-delivery.test.ts +26 -13
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/client-os-metadata-persistence.test.ts +16 -0
- package/src/__tests__/compactor-retained-image-validation.test.ts +142 -0
- package/src/__tests__/container-cpu-sampler.test.ts +153 -0
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +17 -0
- package/src/__tests__/conversation-lifecycle.test.ts +20 -26
- package/src/__tests__/conversation-pairing.test.ts +186 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
- package/src/__tests__/credential-broker-server-use.test.ts +24 -5
- package/src/__tests__/credential-routes.test.ts +22 -3
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/daemon-credential-client.test.ts +88 -0
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/dm-backfill.test.ts +63 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -4
- package/src/__tests__/events-client-registration.test.ts +28 -0
- package/src/__tests__/file-write-tool.test.ts +4 -2
- package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/host-proxy-interface.test.ts +11 -1
- package/src/__tests__/host-shell-tool.test.ts +23 -4
- package/src/__tests__/image-conversion.test.ts +143 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/input-repairs.test.ts +207 -0
- package/src/__tests__/live-workspace-guard.test.ts +75 -0
- package/src/__tests__/mcp-abort-signal.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
- package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
- package/src/__tests__/mtime-cache.test.ts +2 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
- package/src/__tests__/openai-provider.test.ts +22 -0
- package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/platform.test.ts +16 -1
- package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
- package/src/__tests__/plugin-api-shim.test.ts +5 -0
- package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
- package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
- 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 +0 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/resource-pressure-guard.test.ts +428 -0
- package/src/__tests__/resource-pressure-routes.test.ts +113 -0
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
- package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
- package/src/__tests__/skill-tool-factory.test.ts +161 -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 +63 -38
- package/src/__tests__/tool-policy.test.ts +97 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
- package/src/__tests__/user-plugin-loader.test.ts +2 -0
- package/src/__tests__/validate-input.test.ts +243 -7
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
- package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
- package/src/acp/acp-claude-oauth.ts +38 -21
- package/src/acp/prepare-agent-env.ts +146 -55
- package/src/agent/loop-tool-dedup.test.ts +161 -0
- package/src/agent/loop.ts +68 -22
- package/src/api/constants/app-tools.ts +34 -0
- package/src/api/events/resource-pressure-status-changed.ts +53 -0
- package/src/api/index.ts +19 -0
- package/src/api/responses/resource-pressure-status.ts +25 -0
- package/src/approvals/guardian-channel-delivery.ts +70 -6
- package/src/approvals/guardian-request-resolvers.ts +32 -66
- package/src/channels/__tests__/message-audience.test.ts +49 -0
- package/src/channels/__tests__/types.test.ts +17 -3
- package/src/channels/message-audience.ts +40 -0
- package/src/channels/types.ts +16 -12
- package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
- package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/__tests__/inference-providers.test.ts +68 -0
- package/src/cli/commands/__tests__/keys.test.ts +89 -8
- package/src/cli/commands/__tests__/plugins.test.ts +57 -1
- package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
- package/src/cli/commands/channel-verification-sessions.ts +11 -21
- package/src/cli/commands/conversations.help.ts +34 -0
- package/src/cli/commands/conversations.ts +125 -0
- package/src/cli/commands/inference-providers.ts +9 -4
- package/src/cli/commands/inference.help.ts +9 -4
- package/src/cli/commands/keys.help.ts +13 -1
- package/src/cli/commands/keys.ts +26 -15
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/index.help.ts +41 -27
- 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-validate.ts +18 -0
- package/src/cli/commands/monitoring.ts +1 -0
- package/src/cli/commands/plugins.help.ts +28 -31
- package/src/cli/commands/plugins.ts +10 -0
- package/src/cli/commands/skills.help.ts +13 -16
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/daemon-credential-client.ts +25 -3
- package/src/cli/lib/install-from-github.ts +35 -24
- package/src/cli/lib/merge-plugin-tree.ts +22 -4
- package/src/cli/lib/plugin-surfaces.ts +27 -0
- package/src/config/feature-flag-registry.json +35 -19
- package/src/config/webhook-routing.ts +8 -0
- package/src/context/compactor.ts +31 -2
- package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
- package/src/daemon/conversation-error.ts +13 -0
- package/src/daemon/conversation-messaging.ts +22 -2
- package/src/daemon/conversation-process.ts +11 -0
- package/src/daemon/conversation-store.ts +10 -0
- package/src/daemon/conversation-surfaces.ts +21 -8
- package/src/daemon/conversation.ts +12 -10
- package/src/daemon/lifecycle.ts +6 -2
- package/src/daemon/message-types/conversations.ts +5 -7
- package/src/daemon/provider-rejection-log-fields.ts +124 -0
- package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
- package/src/daemon/resource-pressure-guard.ts +387 -0
- package/src/daemon/shutdown-handlers.ts +2 -0
- package/src/daemon/startup-error.ts +16 -0
- package/src/daemon/trust-context.ts +14 -10
- package/src/daemon/unsendable-image-notice.ts +76 -0
- package/src/hooks/registry.ts +65 -51
- package/src/ipc/__tests__/socket-path.test.ts +15 -7
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/socket-cleanup.ts +2 -11
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +128 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +116 -68
- package/src/messaging/providers/channel-transport.ts +90 -13
- package/src/messaging/providers/discord/send.ts +16 -0
- package/src/messaging/providers/discord/transport.ts +11 -1
- package/src/messaging/providers/index.ts +109 -16
- package/src/messaging/providers/slack/message-metadata.ts +45 -0
- package/src/messaging/providers/slack/render-transcript.ts +1 -4
- package/src/messaging/providers/slack/send.test.ts +6 -9
- package/src/messaging/providers/slack/send.ts +40 -28
- package/src/messaging/providers/slack/transport.ts +22 -26
- package/src/messaging/providers/telegram-bot/send.test.ts +2 -8
- package/src/messaging/providers/telegram-bot/transport.ts +3 -6
- package/src/messaging/read-provider-metadata.test.ts +157 -0
- package/src/messaging/read-provider-metadata.ts +51 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
- package/src/notifications/__tests__/edit-notification.test.ts +174 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
- package/src/notifications/adapters/macos.ts +55 -0
- package/src/notifications/adapters/slack.ts +10 -5
- package/src/notifications/assistant-reply-producer.ts +37 -4
- package/src/notifications/conversation-pairing.ts +111 -21
- package/src/notifications/edit-notification.ts +38 -8
- package/src/notifications/emit-signal.ts +12 -14
- package/src/notifications/home-feed-side-effect.ts +218 -15
- package/src/notifications/types.ts +5 -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 +28 -3
- package/src/permissions/confirmation-guardian-request.ts +5 -1
- package/src/persistence/conversation-crud.ts +23 -20
- package/src/persistence/conversation-queries.ts +14 -1
- package/src/persistence/conversation-types.ts +22 -0
- package/src/persistence/delivery-crud.ts +122 -15
- package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
- package/src/plugin-api/conversation-turn.ts +27 -0
- package/src/plugin-api/credential-scope.test.ts +15 -0
- package/src/plugin-api/credential-scope.ts +14 -0
- package/src/plugin-api/index.ts +19 -1
- package/src/plugin-api/resolve-credential.ts +15 -10
- package/src/plugin-api/store-credential.ts +148 -0
- package/src/plugin-api/system-card.ts +36 -0
- package/src/plugin-api/vision-support.test.ts +82 -0
- package/src/plugin-api/vision-support.ts +14 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
- package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
- package/src/plugins/defaults/image-recovery/detect.ts +24 -1
- package/src/plugins/defaults/main.ts +15 -0
- package/src/plugins/defaults/memory/AGENTS.md +11 -3
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -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 +1 -61
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
- 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/static-context.ts +0 -29
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -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/mtime-cache.ts +1 -1
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/plugin-tree-walk.ts +63 -4
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/connection-resolution.ts +2 -1
- package/src/providers/content-blocks.ts +22 -0
- package/src/providers/gemini/client.ts +5 -2
- package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
- package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
- package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
- package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
- package/src/providers/inference/adapter-factory.ts +33 -2
- package/src/providers/inference/auth.ts +13 -2
- package/src/providers/inference/credential-usage.ts +37 -0
- package/src/providers/inference/missing-credential-guard.ts +110 -0
- package/src/providers/inference/resolve-auth.ts +7 -7
- package/src/providers/media-resolve.ts +176 -15
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +322 -42
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +31 -10
- package/src/runtime/AGENTS.md +35 -0
- package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
- package/src/runtime/__tests__/web-presence.test.ts +234 -0
- package/src/runtime/assistant-event-hub.ts +38 -0
- package/src/runtime/channel-reply-delivery.ts +54 -31
- package/src/runtime/channel-retry-sweep.ts +6 -4
- package/src/runtime/effective-capabilities.test.ts +19 -0
- package/src/runtime/effective-capabilities.ts +25 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/http-errors.ts +1 -0
- package/src/runtime/http-server.ts +20 -5
- package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
- package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +52 -1
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +88 -35
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
- package/src/runtime/routes/approval-routes.ts +37 -4
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
- package/src/runtime/routes/canned-message-complete.ts +68 -29
- package/src/runtime/routes/client-routes.ts +113 -0
- package/src/runtime/routes/conversation-list-routes.ts +22 -7
- package/src/runtime/routes/conversation-management-routes.ts +15 -9
- package/src/runtime/routes/conversation-routes.ts +36 -22
- package/src/runtime/routes/credential-in-use.ts +85 -0
- package/src/runtime/routes/credential-routes.ts +55 -85
- package/src/runtime/routes/guardian-approval-interception.ts +22 -22
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
- package/src/runtime/routes/identity-routes.ts +5 -121
- package/src/runtime/routes/inbound-message-handler.ts +71 -27
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +77 -50
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -97
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +9 -7
- package/src/runtime/routes/monitoring-routes.ts +9 -1
- package/src/runtime/routes/plugins-routes.ts +17 -5
- package/src/runtime/routes/resource-pressure-routes.ts +22 -0
- package/src/runtime/routes/secret-routes.ts +39 -8
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/user-route-dispatcher.ts +31 -2
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/slack-reply-session.test.ts +23 -13
- package/src/runtime/slack-reply-session.ts +38 -55
- package/src/runtime/web-presence.ts +90 -0
- package/src/skills/validate-input.ts +177 -40
- package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
- package/src/subagent/consult-context.ts +7 -23
- package/src/tools/credentials/broker.ts +24 -79
- 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/credentials/tool-policy.ts +67 -0
- package/src/tools/executor.ts +53 -15
- package/src/tools/network/__tests__/web-search.test.ts +41 -1
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/network/web-search.ts +21 -3
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/schema-transforms.ts +40 -5
- package/src/tools/shared/input-repairs.ts +160 -0
- package/src/tools/skills/skill-tool-factory.ts +18 -4
- package/src/tools/subagent/spawn.ts +0 -1
- package/src/tools/tool-approval-handler.ts +16 -10
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/util/__tests__/cgroup-memory.test.ts +3 -0
- package/src/util/cgroup-memory.ts +3 -0
- package/src/util/container-cpu-sampler.ts +250 -0
- package/src/util/image-conversion.ts +178 -14
- package/src/util/platform.ts +93 -10
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* focused on orchestration.
|
|
9
9
|
*/
|
|
10
10
|
import type { AssistantEvent } from "../../../api/index.js";
|
|
11
|
+
import { resolveGuardianPromptDelivery } from "../../../approvals/guardian-channel-delivery.js";
|
|
11
12
|
import {
|
|
12
13
|
extractMessageTsFromCallbackUrl,
|
|
13
14
|
extractThreadTsFromCallbackUrl,
|
|
@@ -20,6 +21,12 @@ import {
|
|
|
20
21
|
} from "../../../contacts/guardian-delivery-reader.js";
|
|
21
22
|
import { isConversationBusyError } from "../../../daemon/conversation-messaging.js";
|
|
22
23
|
import type { TrustContext } from "../../../daemon/trust-context-types.js";
|
|
24
|
+
import {
|
|
25
|
+
sendChannelReaction,
|
|
26
|
+
sendChannelTyping,
|
|
27
|
+
setChannelThreadStatus,
|
|
28
|
+
supportsChannelTyping,
|
|
29
|
+
} from "../../../messaging/providers/index.js";
|
|
23
30
|
import {
|
|
24
31
|
getSiblingStreamedReplyTs,
|
|
25
32
|
linkMessage,
|
|
@@ -161,25 +168,15 @@ export function processChannelMessageInBackground(
|
|
|
161
168
|
// `channel-turn-admission.ts` for why channel turns defer rather than route
|
|
162
169
|
// through the SSE-oriented conversation queue.
|
|
163
170
|
void withChannelTurnAdmission(conversationId, async () => {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
? replyCallbackUrl
|
|
169
|
-
: undefined;
|
|
170
|
-
const stopTypingHeartbeat = typingCallbackUrl
|
|
171
|
-
? startTelegramTypingHeartbeat(
|
|
172
|
-
typingCallbackUrl,
|
|
173
|
-
externalChatId,
|
|
174
|
-
assistantId,
|
|
175
|
-
)
|
|
176
|
-
: undefined;
|
|
171
|
+
const stopTypingHeartbeat =
|
|
172
|
+
replyCallbackUrl && supportsChannelTyping(replyCallbackUrl)
|
|
173
|
+
? startTypingHeartbeat(replyCallbackUrl, externalChatId)
|
|
174
|
+
: undefined;
|
|
177
175
|
|
|
178
176
|
const slackThinkingStatus = createSlackThinkingStatusController({
|
|
179
177
|
sourceChannel,
|
|
180
178
|
replyCallbackUrl,
|
|
181
179
|
chatId: externalChatId,
|
|
182
|
-
assistantId,
|
|
183
180
|
startImmediately: shouldStartSlackThinkingStatusImmediately({
|
|
184
181
|
sourceChannel,
|
|
185
182
|
chatType,
|
|
@@ -193,6 +190,7 @@ export function processChannelMessageInBackground(
|
|
|
193
190
|
externalChatId,
|
|
194
191
|
trustClass: trustCtx.trustClass,
|
|
195
192
|
guardianExternalUserId: trustCtx.guardianExternalUserId,
|
|
193
|
+
guardianChatId: trustCtx.guardianChatId,
|
|
196
194
|
requesterExternalUserId: trustCtx.requesterExternalUserId,
|
|
197
195
|
replyCallbackUrl,
|
|
198
196
|
assistantId,
|
|
@@ -230,7 +228,6 @@ export function processChannelMessageInBackground(
|
|
|
230
228
|
chatType,
|
|
231
229
|
replyCallbackUrl,
|
|
232
230
|
chatId: externalChatId,
|
|
233
|
-
assistantId,
|
|
234
231
|
recipientUserId: slackInbound?.actorExternalUserId,
|
|
235
232
|
recipientTeamId: slackInbound?.actorTeamId,
|
|
236
233
|
// Durably record the streamed message `ts` the instant the stream
|
|
@@ -387,40 +384,23 @@ export function processChannelMessageInBackground(
|
|
|
387
384
|
// ---------------------------------------------------------------------------
|
|
388
385
|
|
|
389
386
|
/**
|
|
390
|
-
* How often the
|
|
387
|
+
* How often the working indicator is re-sent while a turn runs.
|
|
391
388
|
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
389
|
+
* Every channel that has one expires it after a few seconds and offers no
|
|
390
|
+
* start/stop pair to hold it open, so showing it across a multi-second turn
|
|
391
|
+
* means re-sending on a timer. The interval has to stay under the shortest
|
|
392
|
+
* expiry of any channel that implements `typing`: Telegram is about five
|
|
393
|
+
* seconds, Discord about ten. Anyone changing it should check the current
|
|
394
|
+
* windows first.
|
|
397
395
|
*
|
|
398
|
-
* No explicit stop is needed
|
|
399
|
-
* as soon as the bot sends a message.
|
|
396
|
+
* No explicit stop is needed: both clear the indicator when the bot posts.
|
|
400
397
|
*
|
|
401
398
|
* https://core.telegram.org/bots/api#sendchataction
|
|
399
|
+
* https://discord.com/developers/docs/resources/channel#trigger-typing-indicator
|
|
402
400
|
*/
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
function shouldEmitTelegramTyping(
|
|
406
|
-
sourceChannel: ChannelId,
|
|
407
|
-
replyCallbackUrl?: string,
|
|
408
|
-
): boolean {
|
|
409
|
-
if (sourceChannel !== "telegram" || !replyCallbackUrl) {
|
|
410
|
-
return false;
|
|
411
|
-
}
|
|
412
|
-
try {
|
|
413
|
-
return new URL(replyCallbackUrl).pathname.endsWith("/deliver/telegram");
|
|
414
|
-
} catch {
|
|
415
|
-
return replyCallbackUrl.endsWith("/deliver/telegram");
|
|
416
|
-
}
|
|
417
|
-
}
|
|
401
|
+
const TYPING_INTERVAL_MS = 4_000;
|
|
418
402
|
|
|
419
|
-
function
|
|
420
|
-
callbackUrl: string,
|
|
421
|
-
chatId: string,
|
|
422
|
-
assistantId?: string,
|
|
423
|
-
): () => void {
|
|
403
|
+
function startTypingHeartbeat(callbackUrl: string, chatId: string): () => void {
|
|
424
404
|
let active = true;
|
|
425
405
|
let inFlight = false;
|
|
426
406
|
|
|
@@ -429,11 +409,7 @@ function startTelegramTypingHeartbeat(
|
|
|
429
409
|
return;
|
|
430
410
|
}
|
|
431
411
|
inFlight = true;
|
|
432
|
-
void
|
|
433
|
-
chatId,
|
|
434
|
-
chatAction: "typing",
|
|
435
|
-
assistantId,
|
|
436
|
-
})
|
|
412
|
+
void sendChannelTyping(callbackUrl, chatId)
|
|
437
413
|
.catch((err) => {
|
|
438
414
|
log.debug(
|
|
439
415
|
{ err, chatId },
|
|
@@ -447,7 +423,7 @@ function startTelegramTypingHeartbeat(
|
|
|
447
423
|
|
|
448
424
|
emitTyping();
|
|
449
425
|
|
|
450
|
-
const interval = setInterval(emitTyping,
|
|
426
|
+
const interval = setInterval(emitTyping, TYPING_INTERVAL_MS);
|
|
451
427
|
(interval as { unref?: () => void }).unref?.();
|
|
452
428
|
|
|
453
429
|
return () => {
|
|
@@ -498,16 +474,9 @@ function createSlackThinkingStatusController(params: {
|
|
|
498
474
|
sourceChannel: ChannelId;
|
|
499
475
|
replyCallbackUrl?: string;
|
|
500
476
|
chatId: string;
|
|
501
|
-
assistantId?: string;
|
|
502
477
|
startImmediately?: boolean;
|
|
503
478
|
}): SlackThinkingStatusController | undefined {
|
|
504
|
-
const {
|
|
505
|
-
sourceChannel,
|
|
506
|
-
replyCallbackUrl,
|
|
507
|
-
chatId,
|
|
508
|
-
assistantId,
|
|
509
|
-
startImmediately,
|
|
510
|
-
} = params;
|
|
479
|
+
const { sourceChannel, replyCallbackUrl, chatId, startImmediately } = params;
|
|
511
480
|
if (
|
|
512
481
|
!replyCallbackUrl ||
|
|
513
482
|
!shouldEmitSlackThinkingStatus(sourceChannel, replyCallbackUrl)
|
|
@@ -532,7 +501,6 @@ function createSlackThinkingStatusController(params: {
|
|
|
532
501
|
slackThinkingStatus = setSlackThinkingStatus(
|
|
533
502
|
callbackUrl,
|
|
534
503
|
chatId,
|
|
535
|
-
assistantId,
|
|
536
504
|
currentLoadingMessages,
|
|
537
505
|
);
|
|
538
506
|
lastSentLoadingMessageKey = getLoadingMessagesKey(currentLoadingMessages);
|
|
@@ -649,7 +617,6 @@ function getTaskProgressLoadingMessage(
|
|
|
649
617
|
function setSlackThinkingStatus(
|
|
650
618
|
callbackUrl: string,
|
|
651
619
|
chatId: string,
|
|
652
|
-
assistantId?: string,
|
|
653
620
|
loadingMessages?: string[],
|
|
654
621
|
): SlackThinkingStatusHandle {
|
|
655
622
|
let cleared = false;
|
|
@@ -668,15 +635,13 @@ function setSlackThinkingStatus(
|
|
|
668
635
|
};
|
|
669
636
|
}
|
|
670
637
|
|
|
671
|
-
const addPromise =
|
|
638
|
+
const addPromise = sendChannelReaction(callbackUrl, {
|
|
672
639
|
chatId,
|
|
673
|
-
|
|
674
|
-
|
|
640
|
+
messageId: messageTs,
|
|
641
|
+
emoji: "eyes",
|
|
642
|
+
action: "add",
|
|
675
643
|
}).catch((err) => {
|
|
676
|
-
log.debug(
|
|
677
|
-
{ err, chatId, messageTs },
|
|
678
|
-
"Failed to add Slack eyes reaction",
|
|
679
|
-
);
|
|
644
|
+
log.debug({ err, chatId, messageTs }, "Failed to add eyes reaction");
|
|
680
645
|
});
|
|
681
646
|
|
|
682
647
|
const clearReaction = (): void => {
|
|
@@ -686,14 +651,15 @@ function setSlackThinkingStatus(
|
|
|
686
651
|
cleared = true;
|
|
687
652
|
clearTimeout(safetyTimer);
|
|
688
653
|
void addPromise.then(() =>
|
|
689
|
-
|
|
654
|
+
sendChannelReaction(callbackUrl, {
|
|
690
655
|
chatId,
|
|
691
|
-
|
|
692
|
-
|
|
656
|
+
messageId: messageTs,
|
|
657
|
+
emoji: "eyes",
|
|
658
|
+
action: "remove",
|
|
693
659
|
}).catch((err) => {
|
|
694
660
|
log.debug(
|
|
695
661
|
{ err, chatId, messageTs },
|
|
696
|
-
"Failed to remove
|
|
662
|
+
"Failed to remove eyes reaction",
|
|
697
663
|
);
|
|
698
664
|
}),
|
|
699
665
|
);
|
|
@@ -713,15 +679,11 @@ function setSlackThinkingStatus(
|
|
|
713
679
|
|
|
714
680
|
// Track the set promise so clear waits for it to settle first,
|
|
715
681
|
// preventing a race where clear arrives at Slack before set.
|
|
716
|
-
let statusPromise =
|
|
682
|
+
let statusPromise = setChannelThreadStatus(callbackUrl, {
|
|
717
683
|
chatId,
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
threadTs,
|
|
722
|
-
status: getRandomSlackThinkingStatus(),
|
|
723
|
-
...(loadingMessages ? { loadingMessages } : {}),
|
|
724
|
-
},
|
|
684
|
+
threadTs,
|
|
685
|
+
status: getRandomSlackThinkingStatus(),
|
|
686
|
+
...(loadingMessages ? { loadingMessages } : {}),
|
|
725
687
|
}).catch((err) => {
|
|
726
688
|
log.debug({ err, chatId, threadTs }, "Failed to set Slack thinking status");
|
|
727
689
|
});
|
|
@@ -731,17 +693,13 @@ function setSlackThinkingStatus(
|
|
|
731
693
|
return;
|
|
732
694
|
}
|
|
733
695
|
statusPromise = statusPromise.then(() =>
|
|
734
|
-
|
|
696
|
+
setChannelThreadStatus(callbackUrl, {
|
|
735
697
|
chatId,
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
...(nextLoadingMessages
|
|
742
|
-
? { loadingMessages: nextLoadingMessages }
|
|
743
|
-
: {}),
|
|
744
|
-
},
|
|
698
|
+
threadTs,
|
|
699
|
+
status: getRandomSlackThinkingStatus(),
|
|
700
|
+
...(nextLoadingMessages
|
|
701
|
+
? { loadingMessages: nextLoadingMessages }
|
|
702
|
+
: {}),
|
|
745
703
|
}).catch((err) => {
|
|
746
704
|
log.debug(
|
|
747
705
|
{ err, chatId, threadTs },
|
|
@@ -758,14 +716,10 @@ function setSlackThinkingStatus(
|
|
|
758
716
|
cleared = true;
|
|
759
717
|
clearTimeout(safetyTimer);
|
|
760
718
|
void statusPromise.then(() =>
|
|
761
|
-
|
|
719
|
+
setChannelThreadStatus(callbackUrl, {
|
|
762
720
|
chatId,
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
channel: chatId,
|
|
766
|
-
threadTs,
|
|
767
|
-
status: "",
|
|
768
|
-
},
|
|
721
|
+
threadTs,
|
|
722
|
+
status: "",
|
|
769
723
|
}).catch((err) => {
|
|
770
724
|
log.debug(
|
|
771
725
|
{ err, chatId, threadTs },
|
|
@@ -796,6 +750,7 @@ function startPendingApprovalPromptWatcher(params: {
|
|
|
796
750
|
externalChatId: string;
|
|
797
751
|
trustClass: TrustContext["trustClass"];
|
|
798
752
|
guardianExternalUserId?: string;
|
|
753
|
+
guardianChatId?: string;
|
|
799
754
|
requesterExternalUserId?: string;
|
|
800
755
|
replyCallbackUrl: string;
|
|
801
756
|
assistantId?: string;
|
|
@@ -807,6 +762,7 @@ function startPendingApprovalPromptWatcher(params: {
|
|
|
807
762
|
externalChatId,
|
|
808
763
|
trustClass,
|
|
809
764
|
guardianExternalUserId,
|
|
765
|
+
guardianChatId,
|
|
810
766
|
requesterExternalUserId,
|
|
811
767
|
replyCallbackUrl,
|
|
812
768
|
assistantId,
|
|
@@ -838,9 +794,17 @@ function startPendingApprovalPromptWatcher(params: {
|
|
|
838
794
|
const info = pending[0];
|
|
839
795
|
if (prompt && info && !deliveredRequestIds.has(info.requestId)) {
|
|
840
796
|
deliveredRequestIds.add(info.requestId);
|
|
797
|
+
// Addressed to the guardian's own chat, not the chat the turn is
|
|
798
|
+
// running in, which can be a room that reads the tool and its
|
|
799
|
+
// command preview.
|
|
800
|
+
const promptDelivery = resolveGuardianPromptDelivery({
|
|
801
|
+
turnChatId: externalChatId,
|
|
802
|
+
turnCallbackUrl: replyCallbackUrl,
|
|
803
|
+
guardianChatId,
|
|
804
|
+
});
|
|
841
805
|
const delivered = await deliverGeneratedApprovalPrompt({
|
|
842
|
-
replyCallbackUrl,
|
|
843
|
-
chatId:
|
|
806
|
+
replyCallbackUrl: promptDelivery.callbackUrl,
|
|
807
|
+
chatId: promptDelivery.chatId,
|
|
844
808
|
sourceChannel,
|
|
845
809
|
assistantId: assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
846
810
|
prompt,
|
|
@@ -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";
|
|
@@ -71,19 +74,19 @@ export async function handleEditIntercept(
|
|
|
71
74
|
content,
|
|
72
75
|
} = params;
|
|
73
76
|
|
|
74
|
-
// Dedup the edit event itself (retried edited_message webhooks)
|
|
75
|
-
|
|
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(
|
|
76
81
|
sourceChannel,
|
|
77
82
|
conversationExternalId,
|
|
78
83
|
externalMessageId,
|
|
79
|
-
{ sourceMessageId, sourceThreadId },
|
|
80
84
|
);
|
|
81
|
-
|
|
82
|
-
if (editResult.duplicate) {
|
|
85
|
+
if (alreadyRecorded) {
|
|
83
86
|
return {
|
|
84
87
|
accepted: true,
|
|
85
88
|
duplicate: true,
|
|
86
|
-
eventId:
|
|
89
|
+
eventId: alreadyRecorded.eventId,
|
|
87
90
|
};
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -117,81 +120,100 @@ export async function handleEditIntercept(
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
if (original) {
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{
|
|
135
|
-
assistantId,
|
|
136
|
-
sourceChannel,
|
|
137
|
-
sourceMessageId,
|
|
138
|
-
messageId: original.messageId,
|
|
139
|
-
},
|
|
140
|
-
"Edit text unchanged; skipping update",
|
|
141
|
-
);
|
|
142
|
-
return {
|
|
143
|
-
accepted: true,
|
|
144
|
-
duplicate: false,
|
|
145
|
-
noop: true,
|
|
146
|
-
eventId: editResult.eventId,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
if (sourceChannel === "slack") {
|
|
150
|
-
// Slack edits stamp `slackMeta.editedAt` so the chronological
|
|
151
|
-
// transcript renderer can surface the edited marker. The merge
|
|
152
|
-
// tolerates rows that lack slackMeta enrichment by synthesizing
|
|
153
|
-
// the minimum-required fields from the lookup data.
|
|
154
|
-
applySlackEditMetadata({
|
|
155
|
-
messageId: original.messageId,
|
|
156
|
-
conversationExternalId,
|
|
157
|
-
sourceMessageId,
|
|
158
|
-
sourceThreadId,
|
|
159
|
-
newContent,
|
|
160
|
-
});
|
|
161
|
-
} else {
|
|
162
|
-
updateMessageContent(original.messageId, newContent);
|
|
163
|
-
}
|
|
164
|
-
// The edit changed searchable text (the no-op guard above already returned
|
|
165
|
-
// for identical content) and this path bypasses the `addMessage` persist
|
|
166
|
-
// path, so reindex the message into the lexical index — the idempotent
|
|
167
|
-
// upsert replaces the stale Qdrant point with the edited content.
|
|
168
|
-
enqueueLexicalIndexForMessage(original.messageId);
|
|
169
|
-
log.info(
|
|
170
|
-
{ assistantId, sourceMessageId, messageId: original.messageId },
|
|
171
|
-
"Updated message content from edited_message",
|
|
172
|
-
);
|
|
173
|
-
} else {
|
|
174
|
-
// For Slack, treat missing-target edits as `debug` (the row may have
|
|
175
|
-
// been compacted, never stored, or pre-date this upgrade); for other
|
|
176
|
-
// channels, retain the louder `warn` since their edit pipelines
|
|
177
|
-
// 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
|
+
};
|
|
178
137
|
if (sourceChannel === "slack") {
|
|
179
|
-
log.debug(
|
|
180
|
-
{
|
|
181
|
-
assistantId,
|
|
182
|
-
sourceChannel,
|
|
183
|
-
channelId: conversationExternalId,
|
|
184
|
-
externalMessageId: sourceMessageId,
|
|
185
|
-
},
|
|
186
|
-
"Slack edit target not found, ignoring",
|
|
187
|
-
);
|
|
138
|
+
log.debug(missingTarget, "Slack edit target not found, ignoring");
|
|
188
139
|
} else {
|
|
189
140
|
log.warn(
|
|
190
|
-
|
|
141
|
+
missingTarget,
|
|
191
142
|
"Could not find original message for edit after retries, ignoring",
|
|
192
143
|
);
|
|
193
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);
|
|
194
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
|
+
);
|
|
195
217
|
|
|
196
218
|
return {
|
|
197
219
|
accepted: true,
|
|
@@ -217,6 +239,8 @@ export async function handleEditIntercept(
|
|
|
217
239
|
*/
|
|
218
240
|
function applySlackEditMetadata(params: {
|
|
219
241
|
messageId: string;
|
|
242
|
+
/** The stored row, already read by the caller's no-op guard. */
|
|
243
|
+
existingRow: MessageRow | null;
|
|
220
244
|
conversationExternalId: string;
|
|
221
245
|
sourceMessageId: string;
|
|
222
246
|
sourceThreadId?: string;
|
|
@@ -224,13 +248,13 @@ function applySlackEditMetadata(params: {
|
|
|
224
248
|
}): void {
|
|
225
249
|
const {
|
|
226
250
|
messageId,
|
|
251
|
+
existingRow: row,
|
|
227
252
|
conversationExternalId,
|
|
228
253
|
sourceMessageId,
|
|
229
254
|
sourceThreadId,
|
|
230
255
|
newContent,
|
|
231
256
|
} = params;
|
|
232
257
|
|
|
233
|
-
const row = getMessageById(messageId);
|
|
234
258
|
const outerMetadata: Record<string, unknown> =
|
|
235
259
|
row?.metadata != null ? safeParseRecord(row.metadata) : {};
|
|
236
260
|
const existingSlackMeta =
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
listGuardianRequestsOrEmpty,
|
|
14
14
|
listPendingRequestsByDestinationOrEmpty,
|
|
15
15
|
} from "../../../channels/gateway-guardian-requests.js";
|
|
16
|
+
import { audienceForReader } from "../../../channels/message-audience.js";
|
|
16
17
|
import type { ChannelId } from "../../../channels/types.js";
|
|
17
18
|
import { getLogger } from "../../../util/logger.js";
|
|
18
19
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
|
|
@@ -44,8 +45,14 @@ export interface GuardianReplyInterceptParams {
|
|
|
44
45
|
canonicalSenderId: string | null;
|
|
45
46
|
sourceChannel: ChannelId;
|
|
46
47
|
conversationExternalId: string;
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Conversation the message arrived in. Reactions resolve their target by the
|
|
50
|
+
* reacted card's own address and are routed before any conversation is
|
|
51
|
+
* known, so they pass none.
|
|
52
|
+
*/
|
|
53
|
+
conversationId?: string;
|
|
54
|
+
/** Inbound event id, echoed on the consumed response when the caller has one. */
|
|
55
|
+
eventId?: string;
|
|
49
56
|
replyCallbackUrl: string | undefined;
|
|
50
57
|
trustClass: string;
|
|
51
58
|
guardianPrincipalId: string | null | undefined;
|
|
@@ -190,11 +197,13 @@ export async function handleGuardianReplyIntercept(
|
|
|
190
197
|
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
191
198
|
};
|
|
192
199
|
// On Slack, send guardian management replies (disambiguation, pending
|
|
193
|
-
// request lists, etc.)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
200
|
+
// request lists, etc.) so only the guardian sees them where a room is
|
|
201
|
+
// shared.
|
|
202
|
+
routerReplyPayload.audience = audienceForReader(
|
|
203
|
+
sourceChannel,
|
|
204
|
+
conversationExternalId,
|
|
205
|
+
canonicalSenderId ?? rawSenderId,
|
|
206
|
+
);
|
|
198
207
|
try {
|
|
199
208
|
await deliverChannelReply(replyCallbackUrl, routerReplyPayload);
|
|
200
209
|
} catch (err) {
|
|
@@ -209,7 +218,7 @@ export async function handleGuardianReplyIntercept(
|
|
|
209
218
|
response: {
|
|
210
219
|
accepted: true,
|
|
211
220
|
duplicate: false,
|
|
212
|
-
eventId,
|
|
221
|
+
...(eventId ? { eventId } : {}),
|
|
213
222
|
canonicalRouter: routerResult.type,
|
|
214
223
|
requestId: routerResult.requestId,
|
|
215
224
|
},
|