@vellumai/assistant 0.11.8 → 0.11.9-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/ARCHITECTURE.md +2 -2
- package/Dockerfile +8 -48
- package/docker-entrypoint.sh +5 -1
- package/docker-kata-apt-env.sh +3 -0
- package/docker-kata-apt-shims.sh +127 -0
- package/docker-kata-apt-wrapper.sh +45 -0
- package/docker-kata-chroot-exec.sh +35 -0
- package/docker-kata-pip.sh +8 -2
- package/docs/architecture/memory.md +8 -4
- package/docs/guardian-request-flow.md +18 -14
- package/docs/trusted-contact-access.md +1 -7
- package/node_modules/@vellumai/avatar-catalog/src/catalog.ts +7 -1
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -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/platform-credential.ts +41 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -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/platform-credential.ts +41 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +0 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +110 -5
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +5 -33
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +3 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +100 -9
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -2
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +9 -0
- package/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/openapi.yaml +193 -4
- package/package.json +2 -2
- package/src/__tests__/access-request-card-view.test.ts +6 -5
- package/src/__tests__/access-request-seed-content-blocks.test.ts +5 -2
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +43 -82
- package/src/__tests__/always-loaded-tools-guard.test.ts +8 -2
- package/src/__tests__/attachment-stored-path-annotation.test.ts +80 -0
- package/src/__tests__/attachments-store.test.ts +27 -0
- package/src/__tests__/attachments.test.ts +43 -0
- package/src/__tests__/canned-reply-release.test.ts +48 -5
- package/src/__tests__/channel-delivery-store.test.ts +129 -18
- package/src/__tests__/channel-reply-delivery.test.ts +553 -93
- package/src/__tests__/channel-retry-sweep.test.ts +199 -0
- package/src/__tests__/client-os-metadata-persistence.test.ts +32 -3
- package/src/__tests__/conversation-error.test.ts +15 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +118 -0
- package/src/__tests__/conversation-pairing.test.ts +141 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +28 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +125 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +79 -0
- package/src/__tests__/conversation-store-ephemeral.test.ts +323 -10
- package/src/__tests__/conversation-sync-tags.test.ts +2 -37
- package/src/__tests__/credential-health-service.test.ts +98 -10
- package/src/__tests__/delete-propagation.test.ts +469 -0
- package/src/__tests__/dm-persistence.test.ts +16 -0
- package/src/__tests__/docker-kata-apt-shims.test.ts +135 -0
- package/src/__tests__/forbidden-legacy-symbols.test.ts +12 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +0 -22
- package/src/__tests__/guardian-gateway-sim.ts +0 -23
- package/src/__tests__/guardian-question-mode.test.ts +108 -0
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +0 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +26 -12
- package/src/__tests__/host-proxy-interface.test.ts +10 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +16 -0
- package/src/__tests__/injector-v3-suppression.test.ts +43 -9
- package/src/__tests__/list-messages-page-latest.test.ts +127 -0
- package/src/__tests__/list-messages-system-card.test.ts +103 -0
- package/src/__tests__/media-resolve-image-validation.test.ts +77 -0
- package/src/__tests__/notification-decision-fallback.test.ts +199 -77
- package/src/__tests__/notification-decision-strategy.test.ts +198 -213
- package/src/__tests__/notification-discord-adapter.test.ts +25 -0
- package/src/__tests__/notification-slack-adapter.test.ts +133 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +36 -0
- package/src/__tests__/openai-provider.test.ts +5 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +85 -72
- package/src/__tests__/persist-user-message-set-processing-failure.test.ts +83 -65
- package/src/__tests__/platform-client-verify-credential.test.ts +100 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -1
- package/src/__tests__/process-message-display-content.test.ts +20 -9
- package/src/__tests__/processing-acquire-fenced-guard.test.ts +56 -0
- package/src/__tests__/provider-meta-persistence.test.ts +67 -0
- package/src/__tests__/reaction-persistence.test.ts +22 -198
- package/src/__tests__/run-conversation-turn-persistence.test.ts +5 -2
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +16 -0
- package/src/__tests__/skill-load-tool.test.ts +27 -0
- package/src/__tests__/skills.test.ts +34 -1
- package/src/__tests__/strip-memory-injections.test.ts +3 -4
- package/src/__tests__/terminal-tools.test.ts +9 -0
- package/src/__tests__/thread-backfill.test.ts +5 -3
- package/src/__tests__/unified-turn-context-location.test.ts +76 -0
- package/src/__tests__/voice-session-bridge.test.ts +18 -0
- package/src/__tests__/watch-retro-report-payload.test.ts +185 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +82 -0
- package/src/__tests__/workspace-migration-151-repair-renamed-fireworks-deepseek-pro-model-id.test.ts +235 -0
- package/src/__tests__/workspace-migration-152-repair-retired-fireworks-minimax-m2p7-model-id.test.ts +233 -0
- package/src/agent/attachments.ts +13 -2
- package/src/agent/loop.ts +3 -19
- package/src/api/README.md +9 -5
- package/src/api/index.ts +9 -8
- package/src/api/package.json +1 -0
- package/src/api/responses/conversation-message.ts +8 -0
- package/src/api/responses/home.ts +7 -18
- package/src/api/surfaces.ts +114 -7
- package/src/approvals/AGENTS.md +1 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +1 -23
- package/src/channels/__tests__/types.test.ts +22 -1
- package/src/channels/gateway-guardian-requests.ts +0 -22
- package/src/channels/types.ts +8 -6
- package/src/cli/__tests__/catalog-search-help.test.ts +12 -0
- package/src/cli/commands/channels/__tests__/channels.test.ts +26 -0
- package/src/cli/commands/channels/index.help.ts +15 -1
- package/src/cli/commands/channels/index.ts +6 -2
- package/src/cli/commands/db/__tests__/status.test.ts +22 -0
- package/src/cli/commands/db/index.help.ts +1 -1
- package/src/cli/commands/db/status.ts +172 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +29 -0
- package/src/cli/commands/platform/connect.ts +14 -5
- package/src/cli/commands/plugins.help.ts +6 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -8
- package/src/cli/lib/__tests__/install-from-platform.test.ts +72 -0
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +33 -0
- package/src/cli/lib/bundled-marketplace.json +14 -0
- package/src/cli/lib/install-from-github.ts +9 -5
- package/src/cli/lib/install-from-platform.ts +12 -1
- package/src/config/__tests__/assistant-initiated-threads-gate.test.ts +61 -0
- package/src/config/assistant-initiated-threads-gate.ts +50 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -3
- package/src/config/bundled-skills/schedule/SKILL.md +25 -11
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +3 -1
- package/src/config/call-site-defaults.ts +4 -1
- package/src/config/feature-flag-registry.json +21 -4
- package/src/config/schemas/memory-v3.ts +4 -3
- package/src/context/strip-injections.ts +17 -56
- package/src/conversations/__tests__/message-consolidation.test.ts +39 -0
- package/src/conversations/message-consolidation.ts +4 -3
- package/src/credential-health/credential-health-service.ts +130 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +31 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +55 -59
- package/src/daemon/conversation-error.ts +2 -0
- package/src/daemon/conversation-messaging.ts +195 -47
- package/src/daemon/conversation-process.ts +20 -5
- package/src/daemon/conversation-runtime-assembly.ts +48 -39
- package/src/daemon/conversation-store.ts +159 -11
- package/src/daemon/conversation-tool-setup.ts +9 -2
- package/src/daemon/conversation.ts +290 -19
- package/src/daemon/dictation-text-processing.ts +2 -7
- package/src/daemon/handlers/config-channels.ts +33 -3
- package/src/daemon/handlers/shared.ts +9 -0
- package/src/daemon/port-oversized-content.test.ts +117 -0
- package/src/daemon/port-oversized-content.ts +110 -0
- package/src/daemon/process-message.ts +87 -16
- package/src/daemon/reaction-record.test.ts +23 -11
- package/src/daemon/reaction-record.ts +6 -9
- package/src/documents/document-store.ts +1 -5
- package/src/home/conversation-starter-validation.ts +1 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +910 -50
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +31 -25
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +70 -1
- package/src/live-voice/live-voice-photo.ts +544 -61
- package/src/live-voice/live-voice-session.ts +6 -2
- package/src/live-voice/protocol.ts +20 -1
- package/src/messaging/provider-message-metadata.ts +40 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +10 -2
- package/src/messaging/providers/channel-transport.ts +28 -0
- package/src/messaging/providers/discord/send.ts +3 -2
- package/src/messaging/providers/slack/api.ts +11 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +133 -0
- package/src/messaging/providers/slack/message-metadata.ts +141 -1
- package/src/messaging/providers/slack/send.test.ts +58 -0
- package/src/messaging/providers/slack/send.ts +4 -4
- package/src/messaging/providers/slack/transport.ts +28 -2
- package/src/messaging/providers/telegram-bot/send.test.ts +159 -0
- package/src/messaging/providers/telegram-bot/send.ts +93 -0
- package/src/messaging/providers/telegram-bot/transport.ts +71 -0
- package/src/messaging/reaction-envelopes.test.ts +73 -0
- package/src/messaging/reaction-envelopes.ts +33 -5
- package/src/messaging/read-provider-metadata.ts +4 -3
- package/src/monitoring/recovery/__tests__/stranded-delivery-events.test.ts +208 -0
- package/src/monitoring/recovery/db.ts +35 -0
- package/src/monitoring/recovery/orphaned-channel-events.ts +3 -22
- package/src/monitoring/recovery/run-recovery.ts +6 -2
- package/src/monitoring/recovery/stale-processing.ts +3 -20
- package/src/monitoring/recovery/stranded-delivery-events.ts +68 -0
- package/src/notifications/AGENTS.md +2 -2
- package/src/notifications/README.md +2 -2
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +11 -9
- package/src/notifications/__tests__/broadcaster.test.ts +35 -4
- package/src/notifications/__tests__/notification-utils.test.ts +31 -0
- package/src/notifications/access-request-copy.ts +126 -114
- package/src/notifications/adapters/discord.ts +9 -5
- package/src/notifications/adapters/shared.ts +17 -4
- package/src/notifications/adapters/slack.ts +14 -4
- package/src/notifications/adapters/telegram.ts +8 -4
- package/src/notifications/approval-card-data.ts +5 -2
- package/src/notifications/assistant-reply-producer.ts +7 -7
- package/src/notifications/broadcaster.ts +57 -25
- package/src/notifications/conversation-pairing.ts +68 -2
- package/src/notifications/copy-composer.ts +13 -32
- package/src/notifications/decision-engine.ts +56 -237
- package/src/notifications/guardian-delivery-recorder.ts +5 -9
- package/src/notifications/guardian-feed-projection.ts +0 -15
- package/src/notifications/guardian-question-mode.ts +147 -6
- package/src/notifications/notification-utils.ts +117 -1
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -1
- package/src/persistence/conversation-crud.ts +93 -14
- package/src/persistence/conversation-queries.ts +144 -17
- package/src/persistence/conversation-types.ts +39 -5
- package/src/persistence/delivery-crud.ts +224 -28
- package/src/persistence/delivery-status.ts +21 -2
- package/src/persistence/migrations/374-channel-inbound-message-id-index.ts +26 -0
- package/src/persistence/migrations/375-create-channel-outbound-posts.ts +52 -0
- package/src/persistence/migrations/__tests__/375-create-channel-outbound-posts.test.ts +84 -0
- package/src/persistence/schema/conversations.ts +71 -2
- package/src/persistence/schema-contract.test.ts +78 -0
- package/src/persistence/schema-contract.ts +96 -0
- package/src/persistence/steps.ts +4 -0
- package/src/platform/client.ts +55 -0
- package/src/plugins/__tests__/mcp-servers.test.ts +46 -0
- package/src/plugins/defaults/injector-order.ts +2 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +2 -2
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +13 -15
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +10 -0
- package/src/plugins/defaults/memory/injectors.ts +1 -1
- package/src/plugins/defaults/memory/memory-marker.ts +17 -7
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +1 -5
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +54 -41
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +3 -3
- package/src/plugins/defaults/memory/v3/injector.ts +14 -15
- package/src/plugins/defaults/memory/v3/types.ts +15 -4
- package/src/plugins/defaults/turn-context/injectors.ts +3 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +24 -0
- package/src/plugins/mcp-servers.ts +14 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +50 -0
- package/src/providers/__tests__/registry-native-web-search.test.ts +49 -2
- package/src/providers/anthropic/client.ts +21 -24
- package/src/providers/call-site-routing.ts +5 -4
- package/src/providers/connection-resolution.ts +29 -1
- package/src/providers/content-block-size.test.ts +82 -0
- package/src/providers/content-block-size.ts +99 -0
- package/src/providers/file-block-text.test.ts +64 -0
- package/src/providers/file-block-text.ts +28 -0
- package/src/providers/gemini/client.ts +12 -7
- package/src/providers/inference/auth.ts +6 -6
- package/src/providers/media-resolve.ts +14 -0
- package/src/providers/model-catalog.ts +31 -34
- package/src/providers/openai/chat-completions-provider.ts +9 -25
- package/src/providers/openai/responses-provider.ts +11 -18
- package/src/providers/registry.ts +10 -7
- package/src/providers/routing-identity.ts +2 -1
- package/src/providers/types.ts +1 -4
- package/src/providers/vellum-model-routing.ts +3 -2
- package/src/runtime/AGENTS.md +40 -15
- package/src/runtime/approval-message-composer.ts +1 -6
- package/src/runtime/assistant-event-hub.ts +2 -39
- package/src/runtime/channel-approval-types.ts +1 -5
- package/src/runtime/channel-reply-delivery.ts +182 -115
- package/src/runtime/{slack-reply-session.test.ts → channel-reply-session.test.ts} +294 -156
- package/src/runtime/{slack-reply-session.ts → channel-reply-session.ts} +107 -86
- package/src/runtime/channel-retry-sweep.ts +102 -1
- package/src/runtime/finalize-event-delivery.ts +4 -4
- package/src/runtime/guardian-action-message-composer.ts +1 -4
- package/src/runtime/guardian-reply-router.ts +4 -75
- package/src/runtime/http-router.ts +1 -5
- package/src/runtime/question-request-guardian-bridge.ts +2 -3
- package/src/runtime/routes/__tests__/conversation-list-assistant-section.test.ts +359 -0
- package/src/runtime/routes/__tests__/dictation-command-mode.test.ts +120 -0
- package/src/runtime/routes/__tests__/sight-frame-routes.test.ts +487 -0
- package/src/runtime/routes/acp-routes.ts +1 -1
- package/src/runtime/routes/canned-reply-release.ts +19 -9
- package/src/runtime/routes/channel-route-shared.ts +0 -39
- package/src/runtime/routes/channel-verification-routes.ts +4 -1
- package/src/runtime/routes/conversation-list-routes.ts +44 -3
- package/src/runtime/routes/conversation-management-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +138 -59
- package/src/runtime/routes/diagnostics-routes.ts +111 -60
- package/src/runtime/routes/guardian-action-routes.ts +10 -14
- package/src/runtime/routes/guardian-approval-interception.ts +0 -5
- package/src/runtime/routes/inbound-message-handler.ts +170 -48
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +4 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +169 -8
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +82 -14
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +23 -48
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +454 -94
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +309 -102
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/platform-routes.ts +99 -10
- package/src/runtime/routes/sight-frame-routes.ts +136 -0
- package/src/runtime/sync/resource-sync-events.ts +14 -37
- package/src/runtime/sync/sync-publisher.test.ts +5 -4
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/client-os.ts +11 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/terminal/safe-env.ts +4 -0
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +4 -3
- package/src/tools/watch/watch-retro-report.ts +205 -0
- package/src/watch/__tests__/watch-retro.test.ts +268 -40
- package/src/watch/watch-retro.ts +190 -77
- package/src/workspace/migrations/151-repair-renamed-fireworks-deepseek-pro-model-id.ts +195 -0
- package/src/workspace/migrations/152-repair-retired-fireworks-minimax-m2p7-model-id.ts +198 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +4 -0
- package/docker-kata-pip-chroot.sh +0 -22
- package/src/__tests__/slack-reaction-approvals.test.ts +0 -97
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +0 -307
- package/src/api/events/conversation-list-invalidated.ts +0 -38
|
@@ -3,16 +3,25 @@ import type { MessageAudience } from "@vellumai/gateway-client";
|
|
|
3
3
|
import { stripVellumLinks } from "../daemon/assistant-attachments.js";
|
|
4
4
|
import type { RenderedHistoryContent } from "../daemon/handlers/shared.js";
|
|
5
5
|
import { renderHistoryContent } from "../daemon/handlers/shared.js";
|
|
6
|
-
import {
|
|
6
|
+
import type { ProviderMessageMetadata } from "../messaging/provider-message-metadata.js";
|
|
7
|
+
import {
|
|
8
|
+
everyPostDeleted,
|
|
9
|
+
readProviderMessageMetadata,
|
|
10
|
+
} from "../messaging/provider-message-metadata.js";
|
|
7
11
|
import { editChannelMessage } from "../messaging/providers/index.js";
|
|
8
|
-
import {
|
|
12
|
+
import { providerMetadataOfPreSendSlackEnvelope } from "../messaging/providers/slack/message-metadata.js";
|
|
9
13
|
import { getAttachmentMetadataForMessage } from "../persistence/attachments-store.js";
|
|
10
14
|
import {
|
|
11
15
|
getMessageById,
|
|
12
16
|
getMessages,
|
|
17
|
+
parseMessageMetadata,
|
|
13
18
|
updateMessageMetadata,
|
|
14
19
|
} from "../persistence/conversation-crud.js";
|
|
20
|
+
import { isReactionMessageMetadata } from "../persistence/conversation-types.js";
|
|
21
|
+
import { recordOutboundPost } from "../persistence/delivery-crud.js";
|
|
22
|
+
import { safeParseRecord } from "../util/json.js";
|
|
15
23
|
import { getLogger } from "../util/logger.js";
|
|
24
|
+
import { withSqliteRetry } from "../util/sqlite-retry.js";
|
|
16
25
|
import type { ChannelDeliveryResult } from "./gateway-client.js";
|
|
17
26
|
import { deliverChannelReply } from "./gateway-client.js";
|
|
18
27
|
import type { RuntimeAttachmentMetadata } from "./http-types.js";
|
|
@@ -48,8 +57,10 @@ type DeliverRenderedReplyParams = {
|
|
|
48
57
|
* identified by this ts instead of posting a new one (Slack-specific). */
|
|
49
58
|
messageTs?: string;
|
|
50
59
|
/** Called with the ts of the delivered/updated message so callers
|
|
51
|
-
* can use it for subsequent updates.
|
|
52
|
-
|
|
60
|
+
* can use it for subsequent updates. Awaited when it returns a promise,
|
|
61
|
+
* so an async handler (the sent-message-id reconciler) completes its
|
|
62
|
+
* durable write before the next segment posts. */
|
|
63
|
+
onMessageTs?: (ts: string) => void | Promise<void>;
|
|
53
64
|
};
|
|
54
65
|
|
|
55
66
|
function sleep(ms: number): Promise<void> {
|
|
@@ -172,7 +183,7 @@ export async function deliverRenderedReplyViaCallback(
|
|
|
172
183
|
},
|
|
173
184
|
);
|
|
174
185
|
if (result.ts) {
|
|
175
|
-
onMessageTs?.(result.ts);
|
|
186
|
+
await onMessageTs?.(result.ts);
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
return;
|
|
@@ -191,10 +202,10 @@ export async function deliverRenderedReplyViaCallback(
|
|
|
191
202
|
);
|
|
192
203
|
const deliveredTs = result.ts ?? messageTs;
|
|
193
204
|
if (deliveredTs) {
|
|
194
|
-
onMessageTs?.(deliveredTs);
|
|
205
|
+
await onMessageTs?.(deliveredTs);
|
|
195
206
|
}
|
|
196
207
|
} else if (messageTs) {
|
|
197
|
-
onMessageTs?.(messageTs);
|
|
208
|
+
await onMessageTs?.(messageTs);
|
|
198
209
|
}
|
|
199
210
|
return;
|
|
200
211
|
}
|
|
@@ -255,7 +266,7 @@ export async function deliverRenderedReplyViaCallback(
|
|
|
255
266
|
|
|
256
267
|
if (result.ts) {
|
|
257
268
|
currentMessageTs = result.ts;
|
|
258
|
-
onMessageTs?.(result.ts);
|
|
269
|
+
await onMessageTs?.(result.ts);
|
|
259
270
|
}
|
|
260
271
|
|
|
261
272
|
onSegmentDelivered?.(i + 1);
|
|
@@ -282,16 +293,55 @@ export type DeliverReplyOptions = {
|
|
|
282
293
|
audience?: MessageAudience;
|
|
283
294
|
/** Update an existing message instead of posting a new one. */
|
|
284
295
|
messageTs?: string;
|
|
285
|
-
/** Called with the ts of the delivered/updated message.
|
|
286
|
-
|
|
296
|
+
/** Called with the ts of the delivered/updated message. Awaited when it
|
|
297
|
+
* returns a promise. */
|
|
298
|
+
onMessageTs?: (ts: string) => void | Promise<void>;
|
|
287
299
|
};
|
|
288
300
|
|
|
289
301
|
type PersistedMessage = ReturnType<typeof getMessages>[number];
|
|
290
302
|
|
|
303
|
+
/**
|
|
304
|
+
* A row read that contributes nothing to deliver. Spelled out rather than
|
|
305
|
+
* rendered from empty content so the read stays independent of the renderer;
|
|
306
|
+
* the annotation is what keeps it complete as the shape grows.
|
|
307
|
+
*/
|
|
308
|
+
const NO_REPLY_CONTENT: RenderedHistoryContent = {
|
|
309
|
+
text: "",
|
|
310
|
+
toolCalls: [],
|
|
311
|
+
toolCallsBeforeText: false,
|
|
312
|
+
textSegments: [],
|
|
313
|
+
contentOrder: [],
|
|
314
|
+
surfaces: [],
|
|
315
|
+
thinkingSegments: [],
|
|
316
|
+
attachments: [],
|
|
317
|
+
contentBlocks: [],
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Read a persisted assistant row as a candidate channel reply.
|
|
322
|
+
*
|
|
323
|
+
* A reaction row reads as empty on purpose. Its `"[reaction]"` body is a
|
|
324
|
+
* storage sentinel for an emoji the react tool already delivered to the
|
|
325
|
+
* channel, not speech the turn owes anyone, and the row is drained at the
|
|
326
|
+
* turn boundary, so it is the newest assistant row of any turn that
|
|
327
|
+
* reacted. Read literally, its non-empty text counts as a real deliverable
|
|
328
|
+
* reply and outranks the turn's actual reply (or its `<no_response/>`
|
|
329
|
+
* silence) in every newest-first scan below, posting the raw sentinel to the
|
|
330
|
+
* channel as visible text. Reading it as empty is what makes a reaction-only
|
|
331
|
+
* turn silent, and it is the one seam every delivery path shares: the turn
|
|
332
|
+
* scan, the unbounded fallback sweep, and the targeted `messageId` path,
|
|
333
|
+
* which needs it too because the sweep durably stores whatever the scan
|
|
334
|
+
* returns. The history renderers project these rows as a structured reaction
|
|
335
|
+
* fact for the same reason; delivery owes the channel nothing for them.
|
|
336
|
+
*/
|
|
291
337
|
function readPersistedAssistantReply(msg: PersistedMessage): {
|
|
292
338
|
rendered: RenderedHistoryContent;
|
|
293
339
|
replyAttachments: RuntimeAttachmentMetadata[];
|
|
294
340
|
} {
|
|
341
|
+
if (isReactionMessageMetadata(parseMessageMetadata(msg.metadata))) {
|
|
342
|
+
return { rendered: NO_REPLY_CONTENT, replyAttachments: [] };
|
|
343
|
+
}
|
|
344
|
+
|
|
295
345
|
const parsed: unknown = msg.content;
|
|
296
346
|
const rendered = renderHistoryContent(parsed);
|
|
297
347
|
|
|
@@ -387,17 +437,15 @@ async function deliverPersistedAssistantMessageViaCallback(
|
|
|
387
437
|
// Compose an `onMessageTs` that reconciles the persisted assistant row's
|
|
388
438
|
// provider message ids as the transport reports the authoritative ones.
|
|
389
439
|
// The assistant row is written BEFORE the gateway POST, so its pre-send
|
|
390
|
-
// envelope
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
// id per posted provider message, all reconciled onto this one row; see
|
|
395
|
-
// `makeSentMessageIdReconciler` for the per-envelope rules.
|
|
440
|
+
// envelope lacks the `messageId` a later reaction or delete naming it
|
|
441
|
+
// resolves by. A reply split into several segments reports one id per
|
|
442
|
+
// posted provider message, all reconciled onto this one row; see
|
|
443
|
+
// `makeSentMessageIdReconciler`.
|
|
396
444
|
const reconcileOnMessageTs = makeSentMessageIdReconciler(msg.id);
|
|
397
445
|
const callerOnMessageTs = options?.onMessageTs;
|
|
398
|
-
const composedOnMessageTs = (ts: string): void => {
|
|
399
|
-
reconcileOnMessageTs(ts);
|
|
400
|
-
callerOnMessageTs?.(ts);
|
|
446
|
+
const composedOnMessageTs = async (ts: string): Promise<void> => {
|
|
447
|
+
await reconcileOnMessageTs(ts);
|
|
448
|
+
await callerOnMessageTs?.(ts);
|
|
401
449
|
};
|
|
402
450
|
|
|
403
451
|
await deliverRenderedReplyViaCallback({
|
|
@@ -493,133 +541,152 @@ export async function deliverReplyViaCallback(
|
|
|
493
541
|
|
|
494
542
|
/**
|
|
495
543
|
* Build an `onMessageTs` handler that reconciles the persisted assistant
|
|
496
|
-
* row's provider message ids from the transport's authoritative ones
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
544
|
+
* row's provider message ids from the transport's authoritative ones. Every
|
|
545
|
+
* channel's reply row carries the neutral envelope (`readReplyEnvelope`
|
|
546
|
+
* names the one pending row that may not yet), so there is one rule:
|
|
547
|
+
* the first reported id becomes `providerMeta.messageId`, the further posts
|
|
548
|
+
* of a reply split at tool boundaries or length limits accumulate under
|
|
549
|
+
* `additionalMessageIds`, and every id lands in the `channel_outbound_posts`
|
|
550
|
+
* index, which is what lets a later reaction or delete naming any of the
|
|
551
|
+
* assistant's posts resolve back to this row.
|
|
502
552
|
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
* - No-op when the row was persisted with neither envelope (e.g. vellum
|
|
511
|
-
* outbound).
|
|
512
|
-
* - Failures are logged and swallowed so a transient DB error cannot break
|
|
513
|
-
* the outbound delivery itself.
|
|
553
|
+
* An id the row already names is a redelivery and is skipped. A row
|
|
554
|
+
* persisted with no envelope (e.g. vellum outbound) is left untouched. Every
|
|
555
|
+
* write retries transient SQLite contention (`withSqliteRetry`), because
|
|
556
|
+
* this is the only durable record of the sent message's id and nothing
|
|
557
|
+
* revisits it once the event is marked delivered. Remaining failures are
|
|
558
|
+
* logged and swallowed so a DB error cannot break the outbound delivery
|
|
559
|
+
* itself.
|
|
514
560
|
*/
|
|
515
|
-
function makeSentMessageIdReconciler(
|
|
516
|
-
|
|
517
|
-
|
|
561
|
+
function makeSentMessageIdReconciler(
|
|
562
|
+
messageId: string,
|
|
563
|
+
): (ts: string) => Promise<void> {
|
|
564
|
+
return async (ts: string): Promise<void> => {
|
|
518
565
|
if (!ts) {
|
|
519
566
|
return;
|
|
520
567
|
}
|
|
521
568
|
try {
|
|
522
|
-
// Re-read the row's current metadata so a concurrent edit-propagation
|
|
523
|
-
// write (e.g. `editedAt`) is not clobbered. `updateMessageMetadata`
|
|
524
|
-
// shallow-merges into the top-level envelope, and the per-channel
|
|
525
|
-
// sub-object is merged manually below so we can preserve fields on
|
|
526
|
-
// the partial pre-send envelope (`mergeSlackMetadata` would call
|
|
527
|
-
// `readSlackMetadata` which rejects the partial form for lacking
|
|
528
|
-
// channelTs, exactly the state we are reconciling).
|
|
529
569
|
const row = getMessageById(messageId);
|
|
530
570
|
if (row === null || row.metadata === null) {
|
|
531
571
|
return;
|
|
532
572
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
const slackMetaRaw =
|
|
540
|
-
typeof envelope.slackMeta === "string" ? envelope.slackMeta : null;
|
|
541
|
-
if (slackMetaRaw === null) {
|
|
542
|
-
reconcileProviderMessageId(messageId, envelope, ts);
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
if (slackApplied) {
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
slackApplied = true;
|
|
549
|
-
// If the existing slackMeta already parses cleanly via the strict
|
|
550
|
-
// reader, channelTs is already present (a prior reconciliation ran,
|
|
551
|
-
// or backfill stamped the field) — nothing to do.
|
|
552
|
-
if (readSlackMetadata(slackMetaRaw) !== null) {
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
// Lenient parse of the partial slackMeta so we can preserve every
|
|
556
|
-
// field already written by `handleMessageComplete` (source,
|
|
557
|
-
// eventKind, channelId, threadTs, ...) while patching channelTs in.
|
|
558
|
-
let existingSlackMeta: Record<string, unknown>;
|
|
559
|
-
try {
|
|
560
|
-
const parsed = JSON.parse(slackMetaRaw) as unknown;
|
|
561
|
-
if (
|
|
562
|
-
parsed === null ||
|
|
563
|
-
typeof parsed !== "object" ||
|
|
564
|
-
Array.isArray(parsed)
|
|
565
|
-
) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
existingSlackMeta = parsed as Record<string, unknown>;
|
|
569
|
-
} catch {
|
|
573
|
+
const providerMeta = readReplyEnvelope(safeParseRecord(row.metadata));
|
|
574
|
+
if (
|
|
575
|
+
providerMeta === null ||
|
|
576
|
+
providerIdPatchFor(providerMeta, ts) === null
|
|
577
|
+
) {
|
|
570
578
|
return;
|
|
571
579
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
580
|
+
// The index first: it is the resolution contract, and the insert is
|
|
581
|
+
// conflict-ignoring, so a redelivered id is a no-op there too.
|
|
582
|
+
await withSqliteRetry(
|
|
583
|
+
() =>
|
|
584
|
+
recordOutboundPost({
|
|
585
|
+
sourceChannel: providerMeta.source,
|
|
586
|
+
externalChatId: providerMeta.conversationExternalId,
|
|
587
|
+
providerMessageId: ts,
|
|
588
|
+
messageId,
|
|
589
|
+
conversationId: row.conversationId,
|
|
590
|
+
}),
|
|
591
|
+
{ op: "record_outbound_post", context: { messageId } },
|
|
592
|
+
);
|
|
593
|
+
// The envelope is read and written in one synchronous step, re-read on
|
|
594
|
+
// every attempt, and the delete stage stamps its rows the same way, so
|
|
595
|
+
// neither overwrites the other's patch: bun:sqlite is synchronous and
|
|
596
|
+
// nothing yields between the read and the write.
|
|
597
|
+
await withSqliteRetry(() => stampSentMessageId(messageId, ts), {
|
|
598
|
+
op: "reconcile_sent_message_id",
|
|
599
|
+
context: { messageId },
|
|
579
600
|
});
|
|
580
|
-
updateMessageMetadata(messageId, { slackMeta: mergedSlackMeta });
|
|
581
601
|
} catch (err) {
|
|
582
602
|
log.warn(
|
|
583
603
|
{ err, messageId },
|
|
584
|
-
"Failed to reconcile
|
|
604
|
+
"Failed to reconcile the sent message id on the outbound assistant row",
|
|
585
605
|
);
|
|
586
606
|
}
|
|
587
607
|
};
|
|
588
608
|
}
|
|
589
609
|
|
|
590
610
|
/**
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
611
|
+
* Record a reported id on the row's envelope. A post just reported is on the
|
|
612
|
+
* channel, so the row-level deletion marker is recomputed: it stays only
|
|
613
|
+
* when every post the row now names, this one included, is already recorded
|
|
614
|
+
* as deleted (a deletion can land between the index write and this stamp,
|
|
615
|
+
* and it can name this very post); a marker left by an earlier state in
|
|
616
|
+
* which every known post was gone is cleared. Synchronous by design; see
|
|
617
|
+
* the caller.
|
|
597
618
|
*/
|
|
598
|
-
function
|
|
599
|
-
messageId
|
|
600
|
-
|
|
601
|
-
ts: string,
|
|
602
|
-
): void {
|
|
603
|
-
const providerMeta = readProviderMessageMetadata(envelope.providerMeta);
|
|
604
|
-
if (providerMeta === null) {
|
|
619
|
+
function stampSentMessageId(messageId: string, ts: string): void {
|
|
620
|
+
const row = getMessageById(messageId);
|
|
621
|
+
if (row === null || row.metadata === null) {
|
|
605
622
|
return;
|
|
606
623
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
});
|
|
624
|
+
const envelope = safeParseRecord(row.metadata);
|
|
625
|
+
const providerMeta = readReplyEnvelope(envelope);
|
|
626
|
+
if (providerMeta === null) {
|
|
611
627
|
return;
|
|
612
628
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
providerMeta.additionalMessageIds?.includes(ts)
|
|
616
|
-
) {
|
|
629
|
+
const patch = providerIdPatchFor(providerMeta, ts);
|
|
630
|
+
if (patch === null) {
|
|
617
631
|
return;
|
|
618
632
|
}
|
|
633
|
+
const posts = [
|
|
634
|
+
...(providerMeta.messageId !== undefined ? [providerMeta.messageId] : []),
|
|
635
|
+
...(providerMeta.additionalMessageIds ?? []),
|
|
636
|
+
ts,
|
|
637
|
+
];
|
|
638
|
+
const { deletedAt, ...rest } = providerMeta;
|
|
619
639
|
updateMessageMetadata(messageId, {
|
|
620
640
|
providerMeta: JSON.stringify({
|
|
621
|
-
...
|
|
622
|
-
|
|
641
|
+
...rest,
|
|
642
|
+
...patch,
|
|
643
|
+
...(deletedAt !== undefined &&
|
|
644
|
+
everyPostDeleted(posts, providerMeta.deletedMessageIds)
|
|
645
|
+
? { deletedAt }
|
|
646
|
+
: {}),
|
|
623
647
|
}),
|
|
648
|
+
// A row reserved with Slack's pre-send envelope converges on the neutral
|
|
649
|
+
// one here: `updateMessageMetadata` serializes the merged envelope with
|
|
650
|
+
// `JSON.stringify`, which drops a key set to undefined.
|
|
651
|
+
...(envelope.slackMeta !== undefined ? { slackMeta: undefined } : {}),
|
|
624
652
|
});
|
|
625
653
|
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* The envelope a reply row stamps its sent ids onto: the neutral one every
|
|
657
|
+
* reply the daemon reserves carries, or, for a reply still pending from a
|
|
658
|
+
* daemon that reserved Slack replies under Slack's own envelope, the neutral
|
|
659
|
+
* reading of that pre-send envelope (transitional; see
|
|
660
|
+
* `providerMetadataOfPreSendSlackEnvelope`).
|
|
661
|
+
*/
|
|
662
|
+
function readReplyEnvelope(
|
|
663
|
+
envelope: Record<string, unknown>,
|
|
664
|
+
): ProviderMessageMetadata | null {
|
|
665
|
+
return (
|
|
666
|
+
readProviderMessageMetadata(envelope.providerMeta) ??
|
|
667
|
+
providerMetadataOfPreSendSlackEnvelope(envelope)
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Where a newly reported id belongs on the neutral envelope: `messageId` when
|
|
673
|
+
* the row names none yet, otherwise appended to `additionalMessageIds`.
|
|
674
|
+
* `null` when the envelope already names it, which is a redelivery.
|
|
675
|
+
*/
|
|
676
|
+
function providerIdPatchFor(
|
|
677
|
+
providerMeta: ProviderMessageMetadata,
|
|
678
|
+
ts: string,
|
|
679
|
+
): Partial<ProviderMessageMetadata> | null {
|
|
680
|
+
if (providerMeta.messageId === undefined) {
|
|
681
|
+
return { messageId: ts };
|
|
682
|
+
}
|
|
683
|
+
if (
|
|
684
|
+
providerMeta.messageId === ts ||
|
|
685
|
+
providerMeta.additionalMessageIds?.includes(ts)
|
|
686
|
+
) {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
additionalMessageIds: [...(providerMeta.additionalMessageIds ?? []), ts],
|
|
691
|
+
};
|
|
692
|
+
}
|