@vellumai/assistant 0.8.3 → 0.8.4
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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +25 -7
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +135 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +151 -56
- package/src/runtime/auth/route-policy.ts +7 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RenderedHistoryContent } from "../daemon/handlers/shared.js";
|
|
1
2
|
import { renderHistoryContent } from "../daemon/handlers/shared.js";
|
|
2
3
|
import { getAttachmentMetadataForMessage } from "../memory/attachments-store.js";
|
|
3
4
|
import {
|
|
@@ -76,6 +77,18 @@ function toDeliverableTextSegments(
|
|
|
76
77
|
return [];
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
function hasDeliverableReply(
|
|
81
|
+
rendered: RenderedHistoryContent,
|
|
82
|
+
attachments: RuntimeAttachmentMetadata[],
|
|
83
|
+
): boolean {
|
|
84
|
+
return (
|
|
85
|
+
toDeliverableTextSegments(rendered.textSegments, rendered.text).length >
|
|
86
|
+
0 ||
|
|
87
|
+
attachments.length > 0 ||
|
|
88
|
+
hasNoResponseMarker(rendered.textSegments)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
79
92
|
export async function deliverRenderedReplyViaCallback(
|
|
80
93
|
params: DeliverRenderedReplyParams,
|
|
81
94
|
): Promise<void> {
|
|
@@ -169,6 +182,13 @@ export async function deliverRenderedReplyViaCallback(
|
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
export type DeliverReplyOptions = {
|
|
185
|
+
/** Persisted assistant message row to deliver; defaults to latest assistant. */
|
|
186
|
+
messageId?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Internal conversation message id for the user row that started this
|
|
189
|
+
* delivery. When set, fallback scans never cross into older turns.
|
|
190
|
+
*/
|
|
191
|
+
sinceMessageId?: string;
|
|
172
192
|
startFromSegment?: number;
|
|
173
193
|
onSegmentDelivered?: (deliveredCount: number) => void;
|
|
174
194
|
/** Deliver as ephemeral (visible only to `user`). Fire-and-forget. */
|
|
@@ -181,6 +201,125 @@ export type DeliverReplyOptions = {
|
|
|
181
201
|
onMessageTs?: (ts: string) => void;
|
|
182
202
|
};
|
|
183
203
|
|
|
204
|
+
type PersistedMessage = ReturnType<typeof getMessages>[number];
|
|
205
|
+
|
|
206
|
+
function readPersistedAssistantReply(msg: PersistedMessage): {
|
|
207
|
+
rendered: RenderedHistoryContent;
|
|
208
|
+
replyAttachments: RuntimeAttachmentMetadata[];
|
|
209
|
+
} {
|
|
210
|
+
let parsed: unknown;
|
|
211
|
+
try {
|
|
212
|
+
parsed = JSON.parse(msg.content);
|
|
213
|
+
} catch {
|
|
214
|
+
parsed = msg.content;
|
|
215
|
+
}
|
|
216
|
+
const rendered = renderHistoryContent(parsed);
|
|
217
|
+
|
|
218
|
+
const linked = getAttachmentMetadataForMessage(msg.id);
|
|
219
|
+
const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
|
|
220
|
+
id: a.id,
|
|
221
|
+
filename: a.originalFilename,
|
|
222
|
+
mimeType: a.mimeType,
|
|
223
|
+
sizeBytes: a.sizeBytes,
|
|
224
|
+
kind: a.kind,
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
return { rendered, replyAttachments };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function isToolResultUserMessage(msg: PersistedMessage): boolean {
|
|
231
|
+
if (msg.role !== "user") return false;
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(msg.content) as unknown;
|
|
234
|
+
return (
|
|
235
|
+
Array.isArray(parsed) &&
|
|
236
|
+
parsed.length > 0 &&
|
|
237
|
+
parsed.every(
|
|
238
|
+
(block) =>
|
|
239
|
+
block !== null &&
|
|
240
|
+
typeof block === "object" &&
|
|
241
|
+
(block as Record<string, unknown>).type === "tool_result",
|
|
242
|
+
)
|
|
243
|
+
);
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function findAssistantReplyMessageIdForTurn(
|
|
250
|
+
conversationId: string,
|
|
251
|
+
userMessageId: string,
|
|
252
|
+
): string | undefined {
|
|
253
|
+
const msgs = getMessages(conversationId);
|
|
254
|
+
const userIndex = msgs.findIndex((msg) => msg.id === userMessageId);
|
|
255
|
+
if (userIndex === -1) return undefined;
|
|
256
|
+
|
|
257
|
+
let turnEndIndex = msgs.length;
|
|
258
|
+
for (let i = userIndex + 1; i < msgs.length; i++) {
|
|
259
|
+
const msg = msgs[i];
|
|
260
|
+
if (msg.role === "user" && !isToolResultUserMessage(msg)) {
|
|
261
|
+
turnEndIndex = i;
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
for (let i = turnEndIndex - 1; i > userIndex; i--) {
|
|
267
|
+
const msg = msgs[i];
|
|
268
|
+
if (msg.role === "assistant") {
|
|
269
|
+
const { rendered, replyAttachments } = readPersistedAssistantReply(msg);
|
|
270
|
+
if (hasDeliverableReply(rendered, replyAttachments)) {
|
|
271
|
+
return msg.id;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function deliverPersistedAssistantMessageViaCallback(
|
|
279
|
+
msg: PersistedMessage,
|
|
280
|
+
externalChatId: string,
|
|
281
|
+
callbackUrl: string,
|
|
282
|
+
assistantId: string | undefined,
|
|
283
|
+
options: DeliverReplyOptions | undefined,
|
|
284
|
+
): Promise<boolean> {
|
|
285
|
+
const { rendered, replyAttachments } = readPersistedAssistantReply(msg);
|
|
286
|
+
if (!hasDeliverableReply(rendered, replyAttachments)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Compose an `onMessageTs` that reconciles `slackMeta.channelTs` on the
|
|
291
|
+
// persisted assistant row once Slack returns the authoritative ts. The
|
|
292
|
+
// assistant row was written BEFORE the gateway POST in
|
|
293
|
+
// `handleMessageComplete`, so the partial `slackMeta` it carries is
|
|
294
|
+
// missing `channelTs` and would otherwise be rejected by
|
|
295
|
+
// `readSlackMetadata`, dropping the row out of chronological/thread-tag
|
|
296
|
+
// rendering. We only act on the FIRST ts (top-level segment); any
|
|
297
|
+
// subsequent split segments become independent Slack messages with
|
|
298
|
+
// their own ts and are not represented as separate DB rows.
|
|
299
|
+
const reconcileOnMessageTs = makeChannelTsReconciler(msg.id);
|
|
300
|
+
const callerOnMessageTs = options?.onMessageTs;
|
|
301
|
+
const composedOnMessageTs = (ts: string): void => {
|
|
302
|
+
reconcileOnMessageTs(ts);
|
|
303
|
+
callerOnMessageTs?.(ts);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
await deliverRenderedReplyViaCallback({
|
|
307
|
+
callbackUrl,
|
|
308
|
+
chatId: externalChatId,
|
|
309
|
+
textSegments: rendered.textSegments,
|
|
310
|
+
fallbackText: rendered.text,
|
|
311
|
+
attachments: replyAttachments,
|
|
312
|
+
assistantId,
|
|
313
|
+
startFromSegment: options?.startFromSegment,
|
|
314
|
+
onSegmentDelivered: options?.onSegmentDelivered,
|
|
315
|
+
ephemeral: options?.ephemeral,
|
|
316
|
+
user: options?.user,
|
|
317
|
+
messageTs: options?.messageTs,
|
|
318
|
+
onMessageTs: composedOnMessageTs,
|
|
319
|
+
});
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
184
323
|
export async function deliverReplyViaCallback(
|
|
185
324
|
conversationId: string,
|
|
186
325
|
externalChatId: string,
|
|
@@ -188,58 +327,54 @@ export async function deliverReplyViaCallback(
|
|
|
188
327
|
assistantId?: string,
|
|
189
328
|
options?: DeliverReplyOptions,
|
|
190
329
|
): Promise<void> {
|
|
330
|
+
if (options?.messageId) {
|
|
331
|
+
const msg = getMessageById(options.messageId, conversationId);
|
|
332
|
+
if (!msg || msg.role !== "assistant") {
|
|
333
|
+
throw new Error(
|
|
334
|
+
`Target assistant reply message not found: ${options.messageId}`,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
await deliverPersistedAssistantMessageViaCallback(
|
|
338
|
+
msg,
|
|
339
|
+
externalChatId,
|
|
340
|
+
callbackUrl,
|
|
341
|
+
assistantId,
|
|
342
|
+
options,
|
|
343
|
+
);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (options?.sinceMessageId) {
|
|
348
|
+
const replyMessageId = findAssistantReplyMessageIdForTurn(
|
|
349
|
+
conversationId,
|
|
350
|
+
options.sinceMessageId,
|
|
351
|
+
);
|
|
352
|
+
if (replyMessageId) {
|
|
353
|
+
const msg = getMessageById(replyMessageId, conversationId);
|
|
354
|
+
if (msg && msg.role === "assistant") {
|
|
355
|
+
await deliverPersistedAssistantMessageViaCallback(
|
|
356
|
+
msg,
|
|
357
|
+
externalChatId,
|
|
358
|
+
callbackUrl,
|
|
359
|
+
assistantId,
|
|
360
|
+
options,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
191
367
|
const msgs = getMessages(conversationId);
|
|
192
368
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
193
369
|
if (msgs[i].role !== "assistant") continue;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
parsed = JSON.parse(msgs[i].content);
|
|
198
|
-
} catch {
|
|
199
|
-
parsed = msgs[i].content;
|
|
200
|
-
}
|
|
201
|
-
const rendered = renderHistoryContent(parsed);
|
|
202
|
-
|
|
203
|
-
const linked = getAttachmentMetadataForMessage(msgs[i].id);
|
|
204
|
-
const replyAttachments: RuntimeAttachmentMetadata[] = linked.map((a) => ({
|
|
205
|
-
id: a.id,
|
|
206
|
-
filename: a.originalFilename,
|
|
207
|
-
mimeType: a.mimeType,
|
|
208
|
-
sizeBytes: a.sizeBytes,
|
|
209
|
-
kind: a.kind,
|
|
210
|
-
}));
|
|
211
|
-
|
|
212
|
-
// Compose an `onMessageTs` that reconciles `slackMeta.channelTs` on the
|
|
213
|
-
// persisted assistant row once Slack returns the authoritative ts. The
|
|
214
|
-
// assistant row was written BEFORE the gateway POST in
|
|
215
|
-
// `handleMessageComplete`, so the partial `slackMeta` it carries is
|
|
216
|
-
// missing `channelTs` and would otherwise be rejected by
|
|
217
|
-
// `readSlackMetadata`, dropping the row out of chronological/thread-tag
|
|
218
|
-
// rendering. We only act on the FIRST ts (top-level segment); any
|
|
219
|
-
// subsequent split segments become independent Slack messages with
|
|
220
|
-
// their own ts and are not represented as separate DB rows.
|
|
221
|
-
const reconcileOnMessageTs = makeChannelTsReconciler(msgs[i].id);
|
|
222
|
-
const callerOnMessageTs = options?.onMessageTs;
|
|
223
|
-
const composedOnMessageTs = (ts: string): void => {
|
|
224
|
-
reconcileOnMessageTs(ts);
|
|
225
|
-
callerOnMessageTs?.(ts);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
await deliverRenderedReplyViaCallback({
|
|
370
|
+
const delivered = await deliverPersistedAssistantMessageViaCallback(
|
|
371
|
+
msgs[i],
|
|
372
|
+
externalChatId,
|
|
229
373
|
callbackUrl,
|
|
230
|
-
chatId: externalChatId,
|
|
231
|
-
textSegments: rendered.textSegments,
|
|
232
|
-
fallbackText: rendered.text,
|
|
233
|
-
attachments: replyAttachments,
|
|
234
374
|
assistantId,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
user: options?.user,
|
|
239
|
-
messageTs: options?.messageTs,
|
|
240
|
-
onMessageTs: composedOnMessageTs,
|
|
241
|
-
});
|
|
242
|
-
break;
|
|
375
|
+
options,
|
|
376
|
+
);
|
|
377
|
+
if (delivered) break;
|
|
243
378
|
}
|
|
244
379
|
}
|
|
245
380
|
|
|
@@ -9,17 +9,28 @@ import {
|
|
|
9
9
|
} from "../channels/types.js";
|
|
10
10
|
import { getDiskPressureStatus } from "../daemon/disk-pressure-guard.js";
|
|
11
11
|
import { classifyDiskPressureTurnPolicy } from "../daemon/disk-pressure-policy.js";
|
|
12
|
+
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
12
13
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
13
14
|
import { updateDeliveredSegmentCount } from "../memory/delivery-channels.js";
|
|
14
|
-
import { clearPayload, linkMessage } from "../memory/delivery-crud.js";
|
|
15
15
|
import {
|
|
16
|
+
clearPayload,
|
|
17
|
+
linkMessage,
|
|
18
|
+
storeReplyMessageId,
|
|
19
|
+
} from "../memory/delivery-crud.js";
|
|
20
|
+
import {
|
|
21
|
+
getRetryableDeliveryEvents,
|
|
16
22
|
getRetryableEvents,
|
|
23
|
+
markDeliveryDelivered,
|
|
17
24
|
markProcessed,
|
|
18
25
|
markRetryableFailure,
|
|
26
|
+
recordDeliveryFailure,
|
|
19
27
|
recordProcessingFailure,
|
|
20
28
|
} from "../memory/delivery-status.js";
|
|
21
29
|
import { getLogger } from "../util/logger.js";
|
|
22
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
deliverReplyViaCallback,
|
|
32
|
+
findAssistantReplyMessageIdForTurn,
|
|
33
|
+
} from "./channel-reply-delivery.js";
|
|
23
34
|
import { deliverChannelReply } from "./gateway-client.js";
|
|
24
35
|
import type { MessageProcessor } from "./http-types.js";
|
|
25
36
|
import { resolveRoutingStateFromRuntime } from "./trust-context-resolver.js";
|
|
@@ -91,9 +102,13 @@ export async function sweepFailedEvents(
|
|
|
91
102
|
processMessage: MessageProcessor,
|
|
92
103
|
): Promise<void> {
|
|
93
104
|
const events = getRetryableEvents();
|
|
94
|
-
|
|
105
|
+
const deliveryEvents = getRetryableDeliveryEvents();
|
|
106
|
+
if (events.length === 0 && deliveryEvents.length === 0) return;
|
|
95
107
|
|
|
96
|
-
log.info(
|
|
108
|
+
log.info(
|
|
109
|
+
{ processingCount: events.length, deliveryCount: deliveryEvents.length },
|
|
110
|
+
"Retrying failed channel inbound events",
|
|
111
|
+
);
|
|
97
112
|
|
|
98
113
|
for (const event of events) {
|
|
99
114
|
if (!event.rawPayload) {
|
|
@@ -243,9 +258,20 @@ export async function sweepFailedEvents(
|
|
|
243
258
|
sourceMetadata.chatType.trim().length > 0
|
|
244
259
|
? sourceMetadata.chatType.trim()
|
|
245
260
|
: undefined;
|
|
261
|
+
let replyMessageId: string | undefined;
|
|
262
|
+
const observeAgentEvent = (msg: ServerMessage): void => {
|
|
263
|
+
if (
|
|
264
|
+
msg.type === "message_complete" &&
|
|
265
|
+
(msg.source === undefined || msg.source === "main") &&
|
|
266
|
+
typeof msg.messageId === "string"
|
|
267
|
+
) {
|
|
268
|
+
replyMessageId = msg.messageId;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
246
271
|
|
|
272
|
+
let userMessageId: string | undefined;
|
|
247
273
|
try {
|
|
248
|
-
const
|
|
274
|
+
const result = await processMessage(
|
|
249
275
|
event.conversationId,
|
|
250
276
|
content,
|
|
251
277
|
attachmentIds,
|
|
@@ -260,27 +286,39 @@ export async function sweepFailedEvents(
|
|
|
260
286
|
trustContext,
|
|
261
287
|
isInteractive:
|
|
262
288
|
resolveRoutingStateFromRuntime(trustContext).promptWaitingAllowed,
|
|
289
|
+
onEvent: observeAgentEvent,
|
|
263
290
|
},
|
|
264
291
|
sourceChannel,
|
|
265
292
|
sourceInterface,
|
|
266
293
|
);
|
|
294
|
+
userMessageId = result.messageId;
|
|
267
295
|
linkMessage(event.id, userMessageId);
|
|
268
296
|
markProcessed(event.id);
|
|
297
|
+
replyMessageId ??= result.assistantMessageId;
|
|
298
|
+
if (replyMessageId) {
|
|
299
|
+
storeReplyMessageId(event.id, replyMessageId);
|
|
300
|
+
}
|
|
269
301
|
log.info(
|
|
270
302
|
{ eventId: event.id },
|
|
271
303
|
"Successfully replayed failed channel event",
|
|
272
304
|
);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
log.error({ err, eventId: event.id }, "Retry failed for channel event");
|
|
307
|
+
recordProcessingFailure(event.id, err);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
273
310
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
311
|
+
const replyCallbackUrl =
|
|
312
|
+
typeof payload.replyCallbackUrl === "string"
|
|
313
|
+
? payload.replyCallbackUrl
|
|
314
|
+
: undefined;
|
|
315
|
+
if (replyCallbackUrl) {
|
|
316
|
+
const externalChatId =
|
|
317
|
+
typeof payload.externalChatId === "string"
|
|
318
|
+
? payload.externalChatId
|
|
277
319
|
: undefined;
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
typeof payload.externalChatId === "string"
|
|
281
|
-
? payload.externalChatId
|
|
282
|
-
: undefined;
|
|
283
|
-
if (externalChatId) {
|
|
320
|
+
if (externalChatId) {
|
|
321
|
+
try {
|
|
284
322
|
// processMessage above generated a fresh assistant response, so any
|
|
285
323
|
// previously tracked segment progress belongs to the old response and
|
|
286
324
|
// must not carry over. Reset to 0 so we deliver all segments of the
|
|
@@ -292,16 +330,103 @@ export async function sweepFailedEvents(
|
|
|
292
330
|
replyCallbackUrl,
|
|
293
331
|
assistantId,
|
|
294
332
|
{
|
|
333
|
+
messageId: replyMessageId,
|
|
334
|
+
sinceMessageId: userMessageId,
|
|
295
335
|
startFromSegment: 0,
|
|
296
336
|
onSegmentDelivered: (count) =>
|
|
297
337
|
updateDeliveredSegmentCount(event.id, count),
|
|
298
338
|
},
|
|
299
339
|
);
|
|
340
|
+
markDeliveryDelivered(event.id);
|
|
341
|
+
} catch (err) {
|
|
342
|
+
log.error(
|
|
343
|
+
{ err, eventId: event.id },
|
|
344
|
+
"Retry delivery failed for channel event",
|
|
345
|
+
);
|
|
346
|
+
recordDeliveryFailure(event.id, err);
|
|
300
347
|
}
|
|
301
348
|
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
for (const event of deliveryEvents) {
|
|
353
|
+
if (!event.rawPayload) {
|
|
354
|
+
recordDeliveryFailure(
|
|
355
|
+
event.id,
|
|
356
|
+
new Error("No raw payload stored for delivery retry"),
|
|
357
|
+
);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let payload: Record<string, unknown>;
|
|
362
|
+
try {
|
|
363
|
+
payload = JSON.parse(event.rawPayload) as Record<string, unknown>;
|
|
364
|
+
} catch {
|
|
365
|
+
recordDeliveryFailure(
|
|
366
|
+
event.id,
|
|
367
|
+
new Error("Failed to parse stored raw payload for delivery retry"),
|
|
368
|
+
);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const replyCallbackUrl =
|
|
373
|
+
typeof payload.replyCallbackUrl === "string"
|
|
374
|
+
? payload.replyCallbackUrl
|
|
375
|
+
: undefined;
|
|
376
|
+
const externalChatId =
|
|
377
|
+
typeof payload.externalChatId === "string"
|
|
378
|
+
? payload.externalChatId
|
|
379
|
+
: undefined;
|
|
380
|
+
let replyMessageId =
|
|
381
|
+
typeof payload.replyMessageId === "string"
|
|
382
|
+
? payload.replyMessageId
|
|
383
|
+
: undefined;
|
|
384
|
+
const assistantId =
|
|
385
|
+
typeof payload.assistantId === "string" ? payload.assistantId : undefined;
|
|
386
|
+
if (!replyCallbackUrl || !externalChatId) {
|
|
387
|
+
recordDeliveryFailure(
|
|
388
|
+
event.id,
|
|
389
|
+
new Error("Stored payload is missing delivery callback details"),
|
|
390
|
+
);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (!replyMessageId && event.messageId) {
|
|
394
|
+
replyMessageId = findAssistantReplyMessageIdForTurn(
|
|
395
|
+
event.conversationId,
|
|
396
|
+
event.messageId,
|
|
397
|
+
);
|
|
398
|
+
if (replyMessageId) {
|
|
399
|
+
storeReplyMessageId(event.id, replyMessageId);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (!replyMessageId) {
|
|
403
|
+
recordDeliveryFailure(
|
|
404
|
+
event.id,
|
|
405
|
+
new Error("Stored payload is missing assistant reply message id"),
|
|
406
|
+
);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
await deliverReplyViaCallback(
|
|
412
|
+
event.conversationId,
|
|
413
|
+
externalChatId,
|
|
414
|
+
replyCallbackUrl,
|
|
415
|
+
assistantId,
|
|
416
|
+
{
|
|
417
|
+
messageId: replyMessageId,
|
|
418
|
+
startFromSegment: event.deliveredSegmentCount,
|
|
419
|
+
onSegmentDelivered: (count) =>
|
|
420
|
+
updateDeliveredSegmentCount(event.id, count),
|
|
421
|
+
},
|
|
422
|
+
);
|
|
423
|
+
markDeliveryDelivered(event.id);
|
|
302
424
|
} catch (err) {
|
|
303
|
-
log.error(
|
|
304
|
-
|
|
425
|
+
log.error(
|
|
426
|
+
{ err, eventId: event.id },
|
|
427
|
+
"Retry delivery failed for processed channel event",
|
|
428
|
+
);
|
|
429
|
+
recordDeliveryFailure(event.id, err);
|
|
305
430
|
}
|
|
306
431
|
}
|
|
307
432
|
}
|
|
@@ -63,6 +63,7 @@ export interface RuntimeMessageConversationOptions {
|
|
|
63
63
|
hints?: string[];
|
|
64
64
|
uxBrief?: string;
|
|
65
65
|
chatType?: string;
|
|
66
|
+
clientTimezone?: string;
|
|
66
67
|
};
|
|
67
68
|
assistantId?: string;
|
|
68
69
|
trustContext?: TrustContext;
|
|
@@ -74,8 +75,6 @@ export interface RuntimeMessageConversationOptions {
|
|
|
74
75
|
isInteractive?: boolean;
|
|
75
76
|
/** Channel command intent metadata (e.g. Telegram /start). */
|
|
76
77
|
commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
77
|
-
/** Slack-only non-persisted notice injected into the active model turn. */
|
|
78
|
-
slackRuntimeContextNotice?: string;
|
|
79
78
|
/**
|
|
80
79
|
* Persisted user-facing content. When present, storage/UI use this value
|
|
81
80
|
* while the model-facing turn continues to use `content`.
|
|
@@ -106,7 +105,7 @@ export type MessageProcessor = (
|
|
|
106
105
|
options?: RuntimeMessageConversationOptions,
|
|
107
106
|
sourceChannel?: ChannelId,
|
|
108
107
|
sourceInterface?: InterfaceId,
|
|
109
|
-
) => Promise<{ messageId: string }>;
|
|
108
|
+
) => Promise<{ messageId: string; assistantMessageId?: string }>;
|
|
110
109
|
|
|
111
110
|
/**
|
|
112
111
|
* Dependencies for the POST /v1/messages handler.
|
|
@@ -167,7 +166,6 @@ export interface RuntimeMessagePayload {
|
|
|
167
166
|
approvalReason?: string;
|
|
168
167
|
riskThreshold?: string;
|
|
169
168
|
}>;
|
|
170
|
-
interfaces?: string[];
|
|
171
169
|
surfaces?: Array<{
|
|
172
170
|
surfaceId: string;
|
|
173
171
|
surfaceType: string;
|
|
@@ -189,8 +187,13 @@ export interface RuntimeMessagePayload {
|
|
|
189
187
|
};
|
|
190
188
|
slackMessage?: {
|
|
191
189
|
channelId: string;
|
|
190
|
+
channelName?: string;
|
|
192
191
|
channelTs: string;
|
|
193
192
|
threadTs?: string;
|
|
193
|
+
sender?: {
|
|
194
|
+
displayName?: string;
|
|
195
|
+
externalUserId?: string;
|
|
196
|
+
};
|
|
194
197
|
messageLink?: {
|
|
195
198
|
appUrl?: string;
|
|
196
199
|
webUrl?: string;
|
|
@@ -18,7 +18,14 @@
|
|
|
18
18
|
* resolve the interaction.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import type { InteractionResolutionState } from "../daemon/message-types/messages.js";
|
|
21
22
|
import type { UserDecision } from "../permissions/types.js";
|
|
23
|
+
import { getLogger } from "../util/logger.js";
|
|
24
|
+
import { broadcastMessage } from "./assistant-event-hub.js";
|
|
25
|
+
|
|
26
|
+
const log = getLogger("pending-interactions");
|
|
27
|
+
|
|
28
|
+
export type { InteractionResolutionState } from "../daemon/message-types/messages.js";
|
|
22
29
|
|
|
23
30
|
export interface ConfirmationDetails {
|
|
24
31
|
toolName: string;
|
|
@@ -98,17 +105,50 @@ export function register(
|
|
|
98
105
|
* Remove and return the pending interaction for the given requestId.
|
|
99
106
|
* Auto-clears the proxy timer and detaches the abort listener if present.
|
|
100
107
|
* Returns undefined if no interaction is registered.
|
|
108
|
+
*
|
|
109
|
+
* Emits `interaction_resolved` on the event hub when an interaction is
|
|
110
|
+
* actually removed (no-op when the entry was already consumed by another
|
|
111
|
+
* path). Callers pass `state` to communicate the lifecycle outcome
|
|
112
|
+
* — defaults to `"cancelled"`, the safest value when the call site has
|
|
113
|
+
* no extra context.
|
|
101
114
|
*/
|
|
102
|
-
export function resolve(
|
|
115
|
+
export function resolve(
|
|
116
|
+
requestId: string,
|
|
117
|
+
state: InteractionResolutionState = "cancelled",
|
|
118
|
+
): PendingInteraction | undefined {
|
|
103
119
|
const interaction = pending.get(requestId);
|
|
104
|
-
if (interaction)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
if (!interaction) return undefined;
|
|
121
|
+
pending.delete(requestId);
|
|
122
|
+
if (interaction.timer != null) clearTimeout(interaction.timer);
|
|
123
|
+
interaction.detachAbort?.();
|
|
124
|
+
emitResolved(requestId, interaction, state);
|
|
109
125
|
return interaction;
|
|
110
126
|
}
|
|
111
127
|
|
|
128
|
+
function emitResolved(
|
|
129
|
+
requestId: string,
|
|
130
|
+
interaction: PendingInteraction,
|
|
131
|
+
state: InteractionResolutionState,
|
|
132
|
+
): void {
|
|
133
|
+
log.info(
|
|
134
|
+
{
|
|
135
|
+
requestId,
|
|
136
|
+
conversationId: interaction.conversationId,
|
|
137
|
+
kind: interaction.kind,
|
|
138
|
+
state,
|
|
139
|
+
},
|
|
140
|
+
"Pending interaction resolved",
|
|
141
|
+
);
|
|
142
|
+
broadcastMessage({
|
|
143
|
+
type: "interaction_resolved",
|
|
144
|
+
requestId,
|
|
145
|
+
conversationId: interaction.conversationId,
|
|
146
|
+
conversationKey: interaction.conversationId,
|
|
147
|
+
kind: interaction.kind,
|
|
148
|
+
state,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
112
152
|
/**
|
|
113
153
|
* Return the pending interaction without removing it.
|
|
114
154
|
* Used by trust-rule endpoint which doesn't resolve the confirmation itself.
|
|
@@ -146,7 +186,10 @@ export function getByConversation(
|
|
|
146
186
|
* /v1/host-transfer-result after completing the operation, get a 404, and the
|
|
147
187
|
* proxy timer would fire with a spurious timeout error.
|
|
148
188
|
*/
|
|
149
|
-
export function removeByConversation(
|
|
189
|
+
export function removeByConversation(
|
|
190
|
+
conversationId: string,
|
|
191
|
+
state: InteractionResolutionState = "superseded",
|
|
192
|
+
): void {
|
|
150
193
|
// Snapshot keys to avoid mutation-during-iteration.
|
|
151
194
|
for (const [requestId, interaction] of [...pending]) {
|
|
152
195
|
if (
|
|
@@ -160,7 +203,7 @@ export function removeByConversation(conversationId: string): void {
|
|
|
160
203
|
interaction.kind !== "acp_confirmation"
|
|
161
204
|
) {
|
|
162
205
|
// resolve() clears the stored timer and detaches abort listeners.
|
|
163
|
-
resolve(requestId);
|
|
206
|
+
resolve(requestId, state);
|
|
164
207
|
}
|
|
165
208
|
}
|
|
166
209
|
}
|