@vellumai/assistant 0.3.15 → 0.3.18
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 +211 -12
- package/Dockerfile +1 -1
- package/README.md +11 -5
- package/docs/architecture/http-token-refresh.md +274 -0
- package/docs/architecture/memory.md +5 -4
- package/docs/architecture/scheduling.md +4 -88
- package/docs/runbook-trusted-contacts.md +283 -0
- package/docs/trusted-contact-access.md +247 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2 -6
- package/src/__tests__/access-request-decision.test.ts +328 -0
- package/src/__tests__/asset-materialize-tool.test.ts +7 -7
- package/src/__tests__/asset-search-tool.test.ts +15 -15
- package/src/__tests__/attachments-store.test.ts +13 -13
- package/src/__tests__/call-controller.test.ts +150 -4
- package/src/__tests__/call-conversation-messages.test.ts +2 -2
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/call-start-guardian-guard.test.ts +93 -0
- package/src/__tests__/channel-approval-routes.test.ts +108 -12
- package/src/__tests__/channel-guardian.test.ts +19 -15
- package/src/__tests__/checker.test.ts +103 -48
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +356 -0
- package/src/__tests__/conversation-pairing.test.ts +127 -27
- package/src/__tests__/conversation-store.test.ts +36 -36
- package/src/__tests__/date-context.test.ts +179 -1
- package/src/__tests__/db-migration-rollback.test.ts +4 -7
- package/src/__tests__/deterministic-verification-control-plane.test.ts +5 -5
- package/src/__tests__/emit-signal-routing-intent.test.ts +179 -0
- package/src/__tests__/gateway-only-guard.test.ts +188 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +451 -0
- package/src/__tests__/guardian-action-copy-generator.test.ts +197 -0
- package/src/__tests__/guardian-action-followup-executor.test.ts +379 -0
- package/src/__tests__/guardian-action-followup-store.test.ts +376 -0
- package/src/__tests__/guardian-action-late-reply.test.ts +425 -0
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +71 -0
- package/src/__tests__/guardian-action-store.test.ts +182 -0
- package/src/__tests__/guardian-action-sweep.test.ts +9 -9
- package/src/__tests__/guardian-dispatch.test.ts +120 -0
- package/src/__tests__/guardian-outbound-http.test.ts +194 -2
- package/src/__tests__/guardian-verification-intent-routing.test.ts +179 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +141 -0
- package/src/__tests__/handlers-telegram-config.test.ts +6 -6
- package/src/__tests__/hooks-runner.test.ts +13 -4
- package/src/__tests__/ingress-routes-http.test.ts +443 -0
- package/src/__tests__/intent-routing.test.ts +14 -0
- package/src/__tests__/ipc-snapshot.test.ts +23 -5
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-regressions.test.ts +16 -12
- package/src/__tests__/non-member-access-request.test.ts +281 -0
- package/src/__tests__/notification-broadcaster.test.ts +115 -4
- package/src/__tests__/notification-decision-strategy.test.ts +138 -1
- package/src/__tests__/notification-deep-link.test.ts +44 -1
- package/src/__tests__/notification-guardian-path.test.ts +157 -0
- package/src/__tests__/notification-routing-intent.test.ts +11 -1
- package/src/__tests__/notification-thread-candidate-validation.test.ts +215 -0
- package/src/__tests__/notification-thread-candidates.test.ts +166 -0
- package/src/__tests__/recording-intent.test.ts +1 -0
- package/src/__tests__/recording-state-machine.test.ts +328 -17
- package/src/__tests__/registry.test.ts +17 -8
- package/src/__tests__/relay-server.test.ts +105 -0
- package/src/__tests__/reminder.test.ts +13 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -4
- package/src/__tests__/scheduler-recurrence.test.ts +50 -0
- package/src/__tests__/server-history-render.test.ts +8 -8
- package/src/__tests__/session-agent-loop.test.ts +1 -0
- package/src/__tests__/session-runtime-assembly.test.ts +49 -0
- package/src/__tests__/session-skill-tools.test.ts +1 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +11 -3
- package/src/__tests__/slack-channel-config.test.ts +230 -0
- package/src/__tests__/subagent-manager-notify.test.ts +4 -4
- package/src/__tests__/swarm-session-integration.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +43 -0
- package/src/__tests__/task-management-tools.test.ts +3 -3
- package/src/__tests__/task-tools.test.ts +3 -3
- package/src/__tests__/trust-store.test.ts +38 -22
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +489 -0
- package/src/__tests__/trusted-contact-multichannel.test.ts +405 -0
- package/src/__tests__/trusted-contact-verification.test.ts +360 -0
- package/src/__tests__/update-bulletin-format.test.ts +119 -0
- package/src/__tests__/update-bulletin-state.test.ts +129 -0
- package/src/__tests__/update-bulletin.test.ts +323 -0
- package/src/__tests__/update-template-contract.test.ts +24 -0
- package/src/__tests__/voice-session-bridge.test.ts +109 -9
- package/src/agent/loop.ts +2 -2
- package/src/amazon/client.ts +2 -3
- package/src/calls/call-controller.ts +241 -39
- package/src/calls/call-conversation-messages.ts +2 -2
- package/src/calls/call-domain.ts +10 -3
- package/src/calls/call-pointer-messages.ts +17 -5
- package/src/calls/guardian-action-sweep.ts +77 -36
- package/src/calls/guardian-dispatch.ts +8 -0
- package/src/calls/relay-server.ts +51 -12
- package/src/calls/twilio-routes.ts +3 -1
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +8 -6
- package/src/cli/core-commands.ts +43 -3
- package/src/cli/map.ts +8 -5
- package/src/config/bundled-skills/phone-calls/SKILL.md +16 -1
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +4 -4
- package/src/config/bundled-skills/time-based-actions/SKILL.md +11 -1
- package/src/config/computer-use-prompt.ts +1 -0
- package/src/config/core-schema.ts +16 -0
- package/src/config/env-registry.ts +1 -0
- package/src/config/env.ts +16 -1
- package/src/config/memory-schema.ts +5 -0
- package/src/config/schema.ts +4 -0
- package/src/config/system-prompt.ts +69 -2
- package/src/config/templates/BOOTSTRAP.md +1 -1
- package/src/config/templates/IDENTITY.md +8 -4
- package/src/config/templates/SOUL.md +14 -0
- package/src/config/templates/UPDATES.md +15 -0
- package/src/config/templates/USER.md +5 -1
- package/src/config/types.ts +1 -0
- package/src/config/update-bulletin-format.ts +54 -0
- package/src/config/update-bulletin-state.ts +49 -0
- package/src/config/update-bulletin-template-path.ts +6 -0
- package/src/config/update-bulletin.ts +97 -0
- package/src/config/vellum-skills/catalog.json +6 -0
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/vellum-skills/guardian-verify-setup/SKILL.md +44 -10
- package/src/config/vellum-skills/telegram-setup/SKILL.md +4 -4
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +147 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +2 -2
- package/src/context/window-manager.ts +43 -3
- package/src/daemon/config-watcher.ts +4 -2
- package/src/daemon/connection-policy.ts +21 -1
- package/src/daemon/daemon-control.ts +219 -8
- package/src/daemon/date-context.ts +174 -1
- package/src/daemon/guardian-action-generators.ts +175 -0
- package/src/daemon/guardian-verification-intent.ts +120 -0
- package/src/daemon/handlers/apps.ts +1 -3
- package/src/daemon/handlers/config-channels.ts +2 -2
- package/src/daemon/handlers/config-heartbeat.ts +1 -1
- package/src/daemon/handlers/config-inbox.ts +55 -159
- package/src/daemon/handlers/config-ingress.ts +1 -1
- package/src/daemon/handlers/config-integrations.ts +1 -1
- package/src/daemon/handlers/config-platform.ts +1 -1
- package/src/daemon/handlers/config-scheduling.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +190 -0
- package/src/daemon/handlers/config-telegram.ts +1 -1
- package/src/daemon/handlers/config-twilio.ts +1 -1
- package/src/daemon/handlers/config-voice.ts +100 -0
- package/src/daemon/handlers/config.ts +3 -0
- package/src/daemon/handlers/identity.ts +45 -25
- package/src/daemon/handlers/misc.ts +83 -5
- package/src/daemon/handlers/navigate-settings.ts +27 -0
- package/src/daemon/handlers/recording.ts +270 -144
- package/src/daemon/handlers/sessions.ts +100 -17
- package/src/daemon/handlers/subagents.ts +3 -3
- package/src/daemon/handlers/work-items.ts +10 -7
- package/src/daemon/ipc-contract/integrations.ts +9 -1
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +1 -1
- package/src/daemon/ipc-contract/settings.ts +26 -0
- package/src/daemon/ipc-contract/shared.ts +2 -0
- package/src/daemon/ipc-contract/work-items.ts +1 -7
- package/src/daemon/ipc-contract/workspace.ts +12 -1
- package/src/daemon/ipc-contract-inventory.json +6 -1
- package/src/daemon/ipc-contract.ts +5 -1
- package/src/daemon/lifecycle.ts +314 -266
- package/src/daemon/recording-intent.ts +0 -41
- package/src/daemon/response-tier.ts +2 -2
- package/src/daemon/server.ts +31 -9
- package/src/daemon/session-agent-loop-handlers.ts +34 -9
- package/src/daemon/session-agent-loop.ts +15 -8
- package/src/daemon/session-history.ts +3 -2
- package/src/daemon/session-media-retry.ts +3 -0
- package/src/daemon/session-messaging.ts +38 -4
- package/src/daemon/session-notifiers.ts +2 -2
- package/src/daemon/session-process.ts +546 -59
- package/src/daemon/session-queue-manager.ts +2 -0
- package/src/daemon/session-runtime-assembly.ts +39 -0
- package/src/daemon/session-skill-tools.ts +13 -4
- package/src/daemon/session-tool-setup.ts +5 -6
- package/src/daemon/session.ts +19 -8
- package/src/daemon/tls-certs.ts +60 -13
- package/src/daemon/tool-side-effects.ts +13 -5
- package/src/gallery/default-gallery.ts +32 -9
- package/src/influencer/client.ts +2 -1
- package/src/memory/channel-delivery-store.ts +35 -567
- package/src/memory/channel-guardian-store.ts +63 -1317
- package/src/memory/conflict-store.ts +4 -4
- package/src/memory/conversation-attention-store.ts +0 -3
- package/src/memory/conversation-crud.ts +668 -0
- package/src/memory/conversation-queries.ts +361 -0
- package/src/memory/conversation-store.ts +44 -983
- package/src/memory/db-connection.ts +3 -0
- package/src/memory/db-init.ts +33 -0
- package/src/memory/delivery-channels.ts +175 -0
- package/src/memory/delivery-crud.ts +211 -0
- package/src/memory/delivery-status.ts +199 -0
- package/src/memory/embedding-backend.ts +70 -4
- package/src/memory/embedding-local.ts +12 -2
- package/src/memory/entity-extractor.ts +3 -8
- package/src/memory/fts-reconciler.ts +136 -0
- package/src/memory/guardian-action-store.ts +418 -5
- package/src/memory/guardian-approvals.ts +569 -0
- package/src/memory/guardian-bindings.ts +130 -0
- package/src/memory/guardian-rate-limits.ts +196 -0
- package/src/memory/guardian-verification.ts +521 -0
- package/src/memory/job-handlers/index-maintenance.ts +2 -1
- package/src/memory/job-utils.ts +8 -5
- package/src/memory/jobs-store.ts +66 -6
- package/src/memory/jobs-worker.ts +23 -1
- package/src/memory/migrations/030-guardian-action-followup.ts +21 -0
- package/src/memory/migrations/030-guardian-verification-purpose.ts +17 -0
- package/src/memory/migrations/031-conversations-thread-type-index.ts +5 -0
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +15 -0
- package/src/memory/migrations/032-notification-delivery-thread-decision.ts +20 -0
- package/src/memory/migrations/100-core-tables.ts +1 -1
- package/src/memory/migrations/101-watchers-and-logs.ts +4 -0
- package/src/memory/migrations/108-tasks-and-work-items.ts +1 -1
- package/src/memory/migrations/112-assistant-inbox.ts +1 -1
- package/src/memory/migrations/113-late-migrations.ts +1 -1
- package/src/memory/migrations/116-messages-fts.ts +13 -0
- package/src/memory/migrations/119-schema-indexes-and-columns.ts +37 -0
- package/src/memory/migrations/120-fk-cascade-rebuilds.ts +161 -0
- package/src/memory/migrations/index.ts +10 -3
- package/src/memory/migrations/validate-migration-state.ts +114 -15
- package/src/memory/qdrant-circuit-breaker.ts +105 -0
- package/src/memory/retriever.ts +46 -13
- package/src/memory/schema-migration.ts +4 -0
- package/src/memory/schema.ts +31 -8
- package/src/memory/search/semantic.ts +8 -90
- package/src/notifications/README.md +159 -18
- package/src/notifications/broadcaster.ts +69 -33
- package/src/notifications/conversation-pairing.ts +99 -21
- package/src/notifications/decision-engine.ts +176 -8
- package/src/notifications/deliveries-store.ts +39 -8
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/preferences-store.ts +7 -7
- package/src/notifications/thread-candidates.ts +269 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/checker.ts +1 -16
- package/src/permissions/defaults.ts +25 -5
- package/src/permissions/prompter.ts +17 -0
- package/src/permissions/trust-store.ts +2 -0
- package/src/providers/failover.ts +19 -0
- package/src/providers/registry.ts +46 -1
- package/src/runtime/approval-message-composer.ts +1 -1
- package/src/runtime/channel-guardian-service.ts +15 -3
- package/src/runtime/channel-retry-sweep.ts +7 -2
- package/src/runtime/guardian-action-conversation-turn.ts +85 -0
- package/src/runtime/guardian-action-followup-executor.ts +301 -0
- package/src/runtime/guardian-action-message-composer.ts +245 -0
- package/src/runtime/guardian-outbound-actions.ts +26 -6
- package/src/runtime/guardian-verification-templates.ts +15 -9
- package/src/runtime/http-errors.ts +93 -0
- package/src/runtime/http-server.ts +133 -44
- package/src/runtime/http-types.ts +53 -0
- package/src/runtime/ingress-service.ts +237 -0
- package/src/runtime/middleware/error-handler.ts +4 -3
- package/src/runtime/middleware/rate-limiter.ts +160 -0
- package/src/runtime/middleware/request-logger.ts +71 -0
- package/src/runtime/middleware/twilio-validation.ts +7 -6
- package/src/runtime/pending-interactions.ts +12 -0
- package/src/runtime/routes/access-request-decision.ts +215 -0
- package/src/runtime/routes/app-routes.ts +25 -18
- package/src/runtime/routes/approval-routes.ts +18 -47
- package/src/runtime/routes/attachment-routes.ts +15 -41
- package/src/runtime/routes/call-routes.ts +20 -20
- package/src/runtime/routes/channel-delivery-routes.ts +6 -5
- package/src/runtime/routes/contact-routes.ts +4 -9
- package/src/runtime/routes/conversation-attention-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +26 -57
- package/src/runtime/routes/debug-routes.ts +71 -0
- package/src/runtime/routes/events-routes.ts +3 -2
- package/src/runtime/routes/guardian-approval-interception.ts +221 -0
- package/src/runtime/routes/identity-routes.ts +14 -10
- package/src/runtime/routes/inbound-conversation.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +527 -62
- package/src/runtime/routes/ingress-routes.ts +174 -0
- package/src/runtime/routes/integration-routes.ts +78 -16
- package/src/runtime/routes/pairing-routes.ts +11 -10
- package/src/runtime/routes/secret-routes.ts +10 -18
- package/src/runtime/verification-rate-limiter.ts +83 -0
- package/src/schedule/schedule-store.ts +13 -1
- package/src/schedule/scheduler.ts +1 -1
- package/src/security/secret-ingress.ts +5 -2
- package/src/security/secret-scanner.ts +72 -6
- package/src/subagent/manager.ts +6 -4
- package/src/swarm/plan-validator.ts +4 -1
- package/src/tasks/task-runner.ts +3 -1
- package/src/tools/browser/api-map.ts +9 -6
- package/src/tools/calls/call-start.ts +20 -0
- package/src/tools/executor.ts +50 -568
- package/src/tools/permission-checker.ts +271 -0
- package/src/tools/registry.ts +14 -6
- package/src/tools/reminder/reminder-store.ts +7 -7
- package/src/tools/reminder/reminder.ts +6 -3
- package/src/tools/secret-detection-handler.ts +301 -0
- package/src/tools/subagent/message.ts +1 -1
- package/src/tools/system/voice-config.ts +62 -0
- package/src/tools/tasks/index.ts +3 -3
- package/src/tools/tasks/work-item-list.ts +3 -3
- package/src/tools/tasks/work-item-update.ts +4 -5
- package/src/tools/tool-approval-handler.ts +192 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/version.ts +29 -2
- package/src/watcher/watcher-store.ts +9 -9
- package/src/work-items/work-item-runner.ts +9 -6
- /package/src/memory/migrations/{026-embeddings-nullable-vector-json.ts → 026a-embeddings-nullable-vector-json.ts} +0 -0
- /package/src/memory/migrations/{027-guardian-bootstrap-token.ts → 027a-guardian-bootstrap-token.ts} +0 -0
|
@@ -1,570 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Channel inbound idempotency + delivery state tracking.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* Record an inbound channel event. Returns `duplicate: true` if this
|
|
40
|
-
* exact (channel, chat, message) combination was already seen.
|
|
41
|
-
*/
|
|
42
|
-
export function recordInbound(
|
|
43
|
-
sourceChannel: string,
|
|
44
|
-
externalChatId: string,
|
|
45
|
-
externalMessageId: string,
|
|
46
|
-
options?: RecordInboundOptions,
|
|
47
|
-
): InboundResult {
|
|
48
|
-
const db = getDb();
|
|
49
|
-
|
|
50
|
-
const existing = db
|
|
51
|
-
.select({
|
|
52
|
-
id: channelInboundEvents.id,
|
|
53
|
-
conversationId: channelInboundEvents.conversationId,
|
|
54
|
-
})
|
|
55
|
-
.from(channelInboundEvents)
|
|
56
|
-
.where(
|
|
57
|
-
and(
|
|
58
|
-
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
59
|
-
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
60
|
-
eq(channelInboundEvents.externalMessageId, externalMessageId),
|
|
61
|
-
),
|
|
62
|
-
)
|
|
63
|
-
.get();
|
|
64
|
-
|
|
65
|
-
if (existing) {
|
|
66
|
-
return {
|
|
67
|
-
accepted: true,
|
|
68
|
-
eventId: existing.id,
|
|
69
|
-
conversationId: existing.conversationId,
|
|
70
|
-
duplicate: true,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const assistantId = options?.assistantId;
|
|
75
|
-
const legacyKey = `${sourceChannel}:${externalChatId}`;
|
|
76
|
-
const scopedKey = assistantId ? `asst:${assistantId}:${sourceChannel}:${externalChatId}` : legacyKey;
|
|
77
|
-
|
|
78
|
-
// Resolve conversation mapping with assistant-scoped keying:
|
|
79
|
-
// 1. If scoped key exists, use it directly.
|
|
80
|
-
// 2. If assistantId is "self" and legacy key exists, reuse the legacy
|
|
81
|
-
// conversation and create a scoped alias to prevent future bleed.
|
|
82
|
-
// 3. Otherwise, create/get conversation from the scoped key.
|
|
83
|
-
let mapping: { conversationId: string; created: boolean };
|
|
84
|
-
const scopedMapping = assistantId ? getConversationByKey(scopedKey) : null;
|
|
85
|
-
if (scopedMapping) {
|
|
86
|
-
mapping = { conversationId: scopedMapping.conversationId, created: false };
|
|
87
|
-
} else if (assistantId === 'self') {
|
|
88
|
-
const legacyMapping = getConversationByKey(legacyKey);
|
|
89
|
-
if (legacyMapping) {
|
|
90
|
-
mapping = { conversationId: legacyMapping.conversationId, created: false };
|
|
91
|
-
setConversationKeyIfAbsent(scopedKey, legacyMapping.conversationId);
|
|
92
|
-
} else {
|
|
93
|
-
mapping = getOrCreateConversation(scopedKey);
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
mapping = getOrCreateConversation(scopedKey);
|
|
97
|
-
}
|
|
98
|
-
const now = Date.now();
|
|
99
|
-
const eventId = uuid();
|
|
100
|
-
|
|
101
|
-
db.transaction((tx) => {
|
|
102
|
-
tx.update(conversations)
|
|
103
|
-
.set({ updatedAt: now })
|
|
104
|
-
.where(eq(conversations.id, mapping.conversationId))
|
|
105
|
-
.run();
|
|
106
|
-
tx.insert(channelInboundEvents)
|
|
107
|
-
.values({
|
|
108
|
-
id: eventId,
|
|
109
|
-
sourceChannel,
|
|
110
|
-
externalChatId,
|
|
111
|
-
externalMessageId,
|
|
112
|
-
sourceMessageId: options?.sourceMessageId ?? null,
|
|
113
|
-
conversationId: mapping.conversationId,
|
|
114
|
-
deliveryStatus: 'pending',
|
|
115
|
-
createdAt: now,
|
|
116
|
-
updatedAt: now,
|
|
117
|
-
})
|
|
118
|
-
.run();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
accepted: true,
|
|
123
|
-
eventId,
|
|
124
|
-
conversationId: mapping.conversationId,
|
|
125
|
-
duplicate: false,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Link an inbound event to the user message it created, so edits can
|
|
131
|
-
* later find the correct message by source_message_id → message_id.
|
|
132
|
-
*/
|
|
133
|
-
export function linkMessage(eventId: string, messageId: string): void {
|
|
134
|
-
const db = getDb();
|
|
135
|
-
db.update(channelInboundEvents)
|
|
136
|
-
.set({ messageId, updatedAt: Date.now() })
|
|
137
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
138
|
-
.run();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Find the message ID linked to the original inbound event for a given
|
|
143
|
-
* platform-level message identifier (e.g. Telegram message_id).
|
|
144
|
-
*/
|
|
145
|
-
export function findMessageBySourceId(
|
|
146
|
-
sourceChannel: string,
|
|
147
|
-
externalChatId: string,
|
|
148
|
-
sourceMessageId: string,
|
|
149
|
-
): { messageId: string; conversationId: string } | null {
|
|
150
|
-
const db = getDb();
|
|
151
|
-
const row = db
|
|
152
|
-
.select({
|
|
153
|
-
messageId: channelInboundEvents.messageId,
|
|
154
|
-
conversationId: channelInboundEvents.conversationId,
|
|
155
|
-
})
|
|
156
|
-
.from(channelInboundEvents)
|
|
157
|
-
.where(
|
|
158
|
-
and(
|
|
159
|
-
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
160
|
-
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
161
|
-
eq(channelInboundEvents.sourceMessageId, sourceMessageId),
|
|
162
|
-
isNotNull(channelInboundEvents.messageId),
|
|
163
|
-
),
|
|
164
|
-
)
|
|
165
|
-
.get();
|
|
166
|
-
|
|
167
|
-
if (!row || !row.messageId) return null;
|
|
168
|
-
return { messageId: row.messageId, conversationId: row.conversationId };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Acknowledge delivery of an outbound message for a channel event.
|
|
173
|
-
*/
|
|
174
|
-
export function acknowledgeDelivery(
|
|
175
|
-
sourceChannel: string,
|
|
176
|
-
externalChatId: string,
|
|
177
|
-
externalMessageId: string,
|
|
178
|
-
): boolean {
|
|
179
|
-
const db = getDb();
|
|
180
|
-
const now = Date.now();
|
|
181
|
-
|
|
182
|
-
const existing = db
|
|
183
|
-
.select({ id: channelInboundEvents.id })
|
|
184
|
-
.from(channelInboundEvents)
|
|
185
|
-
.where(
|
|
186
|
-
and(
|
|
187
|
-
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
188
|
-
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
189
|
-
eq(channelInboundEvents.externalMessageId, externalMessageId),
|
|
190
|
-
),
|
|
191
|
-
)
|
|
192
|
-
.get();
|
|
193
|
-
|
|
194
|
-
if (!existing) return false;
|
|
195
|
-
|
|
196
|
-
db.update(channelInboundEvents)
|
|
197
|
-
.set({
|
|
198
|
-
deliveryStatus: 'delivered',
|
|
199
|
-
updatedAt: now,
|
|
200
|
-
})
|
|
201
|
-
.where(eq(channelInboundEvents.id, existing.id))
|
|
202
|
-
.run();
|
|
203
|
-
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ── Pending verification reply helpers ───────────────────────────────
|
|
208
|
-
//
|
|
209
|
-
// When a guardian verification succeeds but the confirmation reply fails
|
|
210
|
-
// to deliver, we persist the reply details on the inbound event so that
|
|
211
|
-
// gateway retries (which arrive as duplicates) can re-attempt delivery.
|
|
212
|
-
|
|
213
|
-
export interface PendingVerificationReply {
|
|
214
|
-
__pendingVerificationReply: true;
|
|
215
|
-
chatId: string;
|
|
216
|
-
text: string;
|
|
217
|
-
assistantId: string;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Store a pending verification reply on an inbound event. Called when
|
|
222
|
-
* `deliverChannelReply` fails after challenge consumption so the reply
|
|
223
|
-
* can be retried on subsequent duplicate deliveries.
|
|
224
|
-
*/
|
|
225
|
-
export function storePendingVerificationReply(
|
|
226
|
-
eventId: string,
|
|
227
|
-
reply: Omit<PendingVerificationReply, '__pendingVerificationReply'>,
|
|
228
|
-
): void {
|
|
229
|
-
const db = getDb();
|
|
230
|
-
const payload: PendingVerificationReply = { __pendingVerificationReply: true, ...reply };
|
|
231
|
-
db.update(channelInboundEvents)
|
|
232
|
-
.set({ rawPayload: JSON.stringify(payload), updatedAt: Date.now() })
|
|
233
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
234
|
-
.run();
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Retrieve a pending verification reply for a given event, if one exists.
|
|
239
|
-
*/
|
|
240
|
-
export function getPendingVerificationReply(
|
|
241
|
-
eventId: string,
|
|
242
|
-
): PendingVerificationReply | null {
|
|
243
|
-
const db = getDb();
|
|
244
|
-
const row = db
|
|
245
|
-
.select({ rawPayload: channelInboundEvents.rawPayload })
|
|
246
|
-
.from(channelInboundEvents)
|
|
247
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
248
|
-
.get();
|
|
249
|
-
|
|
250
|
-
if (!row?.rawPayload) return null;
|
|
251
|
-
try {
|
|
252
|
-
const parsed = JSON.parse(row.rawPayload);
|
|
253
|
-
if (parsed && parsed.__pendingVerificationReply === true) {
|
|
254
|
-
return parsed as PendingVerificationReply;
|
|
255
|
-
}
|
|
256
|
-
return null;
|
|
257
|
-
} catch {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Clear a pending verification reply after successful delivery.
|
|
264
|
-
*/
|
|
265
|
-
export function clearPendingVerificationReply(eventId: string): void {
|
|
266
|
-
const db = getDb();
|
|
267
|
-
db.update(channelInboundEvents)
|
|
268
|
-
.set({ rawPayload: null, updatedAt: Date.now() })
|
|
269
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
270
|
-
.run();
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ── Per-segment delivery progress ──────────────────────────────────
|
|
274
|
-
//
|
|
275
|
-
// When a split reply (multiple text segments from tool boundaries) fails
|
|
276
|
-
// partway through delivery, we persist how many segments were sent so
|
|
277
|
-
// the retry can resume from where it left off.
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Read the number of reply segments already delivered for an event.
|
|
281
|
-
*/
|
|
282
|
-
export function getDeliveredSegmentCount(eventId: string): number {
|
|
283
|
-
const db = getDb();
|
|
284
|
-
const row = db
|
|
285
|
-
.select({ count: channelInboundEvents.deliveredSegmentCount })
|
|
286
|
-
.from(channelInboundEvents)
|
|
287
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
288
|
-
.get();
|
|
289
|
-
return row?.count ?? 0;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Update the delivered segment count after successful delivery of one
|
|
294
|
-
* or more segments. Called incrementally as segments are sent.
|
|
295
|
-
*/
|
|
296
|
-
export function updateDeliveredSegmentCount(eventId: string, count: number): void {
|
|
297
|
-
const db = getDb();
|
|
298
|
-
db.update(channelInboundEvents)
|
|
299
|
-
.set({ deliveredSegmentCount: count, updatedAt: Date.now() })
|
|
300
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
301
|
-
.run();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// ── Dead-letter queue helpers ───────────────────────────────────────
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Store the raw request payload on an inbound event so it can be
|
|
308
|
-
* replayed later if processing fails.
|
|
309
|
-
*/
|
|
310
|
-
export function storePayload(eventId: string, payload: Record<string, unknown>): void {
|
|
311
|
-
const db = getDb();
|
|
312
|
-
db.update(channelInboundEvents)
|
|
313
|
-
.set({ rawPayload: JSON.stringify(payload), updatedAt: Date.now() })
|
|
314
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
315
|
-
.run();
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Clear a previously stored payload. Used when the ingress check
|
|
320
|
-
* detects secret-bearing content — the payload must not remain on disk.
|
|
321
|
-
*/
|
|
322
|
-
export function clearPayload(eventId: string): void {
|
|
323
|
-
const db = getDb();
|
|
324
|
-
db.update(channelInboundEvents)
|
|
325
|
-
.set({ rawPayload: null, updatedAt: Date.now() })
|
|
326
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
327
|
-
.run();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Retrieve the stored raw payload for a given conversation's most recent
|
|
332
|
-
* inbound event. Used by the escalation decide flow to recover the
|
|
333
|
-
* original message content after an approve/deny decision.
|
|
334
|
-
*/
|
|
335
|
-
export function getLatestStoredPayload(conversationId: string): Record<string, unknown> | null {
|
|
336
|
-
const db = getDb();
|
|
337
|
-
const row = db
|
|
338
|
-
.select({
|
|
339
|
-
rawPayload: channelInboundEvents.rawPayload,
|
|
340
|
-
})
|
|
341
|
-
.from(channelInboundEvents)
|
|
342
|
-
.where(
|
|
343
|
-
and(
|
|
344
|
-
eq(channelInboundEvents.conversationId, conversationId),
|
|
345
|
-
isNotNull(channelInboundEvents.rawPayload),
|
|
346
|
-
),
|
|
347
|
-
)
|
|
348
|
-
.orderBy(desc(channelInboundEvents.createdAt))
|
|
349
|
-
.get();
|
|
350
|
-
|
|
351
|
-
if (!row?.rawPayload) return null;
|
|
352
|
-
try {
|
|
353
|
-
return JSON.parse(row.rawPayload) as Record<string, unknown>;
|
|
354
|
-
} catch {
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/** Mark an event as successfully processed. */
|
|
360
|
-
export function markProcessed(eventId: string): void {
|
|
361
|
-
const db = getDb();
|
|
362
|
-
db.update(channelInboundEvents)
|
|
363
|
-
.set({ processingStatus: 'processed', updatedAt: Date.now() })
|
|
364
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
365
|
-
.run();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Record a processing failure. Classifies the error to decide whether
|
|
370
|
-
* the event should be retried (status='failed') or dead-lettered
|
|
371
|
-
* (status='dead_letter') when the error is fatal or max attempts
|
|
372
|
-
* are exhausted.
|
|
373
|
-
*/
|
|
374
|
-
export function recordProcessingFailure(eventId: string, err: unknown): void {
|
|
375
|
-
const db = getDb();
|
|
376
|
-
const now = Date.now();
|
|
377
|
-
|
|
378
|
-
const row = db
|
|
379
|
-
.select({ attempts: channelInboundEvents.processingAttempts })
|
|
380
|
-
.from(channelInboundEvents)
|
|
381
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
382
|
-
.get();
|
|
383
|
-
|
|
384
|
-
const attempts = (row?.attempts ?? 0) + 1;
|
|
385
|
-
const category = classifyError(err);
|
|
386
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
387
|
-
|
|
388
|
-
if (category === 'fatal' || attempts >= RETRY_MAX_ATTEMPTS) {
|
|
389
|
-
db.update(channelInboundEvents)
|
|
390
|
-
.set({
|
|
391
|
-
processingStatus: 'dead_letter',
|
|
392
|
-
processingAttempts: attempts,
|
|
393
|
-
lastProcessingError: errorMsg,
|
|
394
|
-
retryAfter: null,
|
|
395
|
-
updatedAt: now,
|
|
396
|
-
})
|
|
397
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
398
|
-
.run();
|
|
399
|
-
} else {
|
|
400
|
-
const delay = retryDelayForAttempt(attempts);
|
|
401
|
-
db.update(channelInboundEvents)
|
|
402
|
-
.set({
|
|
403
|
-
processingStatus: 'failed',
|
|
404
|
-
processingAttempts: attempts,
|
|
405
|
-
lastProcessingError: errorMsg,
|
|
406
|
-
retryAfter: now + delay,
|
|
407
|
-
updatedAt: now,
|
|
408
|
-
})
|
|
409
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
410
|
-
.run();
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** Fetch events eligible for automatic retry (failed + past their backoff). */
|
|
415
|
-
export function getRetryableEvents(limit = 20): Array<{
|
|
416
|
-
id: string;
|
|
417
|
-
conversationId: string;
|
|
418
|
-
processingAttempts: number;
|
|
419
|
-
rawPayload: string | null;
|
|
420
|
-
}> {
|
|
421
|
-
const db = getDb();
|
|
422
|
-
const now = Date.now();
|
|
423
|
-
return db
|
|
424
|
-
.select({
|
|
425
|
-
id: channelInboundEvents.id,
|
|
426
|
-
conversationId: channelInboundEvents.conversationId,
|
|
427
|
-
processingAttempts: channelInboundEvents.processingAttempts,
|
|
428
|
-
rawPayload: channelInboundEvents.rawPayload,
|
|
429
|
-
})
|
|
430
|
-
.from(channelInboundEvents)
|
|
431
|
-
.where(
|
|
432
|
-
and(
|
|
433
|
-
eq(channelInboundEvents.processingStatus, 'failed'),
|
|
434
|
-
lte(channelInboundEvents.retryAfter, now),
|
|
435
|
-
),
|
|
436
|
-
)
|
|
437
|
-
.limit(limit)
|
|
438
|
-
.all();
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/** Fetch dead-lettered events. */
|
|
442
|
-
export function getDeadLetterEvents(): Array<{
|
|
443
|
-
id: string;
|
|
444
|
-
sourceChannel: string;
|
|
445
|
-
externalChatId: string;
|
|
446
|
-
externalMessageId: string;
|
|
447
|
-
conversationId: string;
|
|
448
|
-
processingAttempts: number;
|
|
449
|
-
lastProcessingError: string | null;
|
|
450
|
-
createdAt: number;
|
|
451
|
-
}> {
|
|
452
|
-
const db = getDb();
|
|
453
|
-
return db
|
|
454
|
-
.select({
|
|
455
|
-
id: channelInboundEvents.id,
|
|
456
|
-
sourceChannel: channelInboundEvents.sourceChannel,
|
|
457
|
-
externalChatId: channelInboundEvents.externalChatId,
|
|
458
|
-
externalMessageId: channelInboundEvents.externalMessageId,
|
|
459
|
-
conversationId: channelInboundEvents.conversationId,
|
|
460
|
-
processingAttempts: channelInboundEvents.processingAttempts,
|
|
461
|
-
lastProcessingError: channelInboundEvents.lastProcessingError,
|
|
462
|
-
createdAt: channelInboundEvents.createdAt,
|
|
463
|
-
})
|
|
464
|
-
.from(channelInboundEvents)
|
|
465
|
-
.where(eq(channelInboundEvents.processingStatus, 'dead_letter'))
|
|
466
|
-
.all();
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// ── Deliver-once guard for terminal reply idempotency ────────────────
|
|
470
|
-
//
|
|
471
|
-
// When both the main poll (processChannelMessageWithApprovals) and the
|
|
472
|
-
// post-decision poll (schedulePostDecisionDelivery) race to deliver the
|
|
473
|
-
// final assistant reply for the same run, this guard ensures only one
|
|
474
|
-
// of them actually sends the message. The guard is run-scoped so old
|
|
475
|
-
// assistant messages from previous runs are not affected.
|
|
476
|
-
|
|
477
|
-
/** Map from runId to insertion timestamp (ms). */
|
|
478
|
-
const deliveredRuns = new Map<string, number>();
|
|
479
|
-
|
|
480
|
-
/** TTL for delivery claims — 10 minutes, well beyond the poll max-wait. */
|
|
481
|
-
const CLAIM_TTL_MS = 10 * 60 * 1000;
|
|
482
|
-
|
|
483
|
-
/** Hard cap to bound memory even under sustained high throughput within the TTL window. */
|
|
484
|
-
const MAX_DELIVERED_RUNS = 10_000;
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Atomically claim the right to deliver the final reply for a run.
|
|
488
|
-
* Returns `true` if this caller won the claim (and should proceed with
|
|
489
|
-
* delivery). Returns `false` if another caller already claimed it.
|
|
490
|
-
*
|
|
491
|
-
* This is an in-memory guard — sufficient because both racing pollers
|
|
492
|
-
* execute within the same process. The Map is never persisted; on restart
|
|
493
|
-
* there are no in-flight pollers to race.
|
|
494
|
-
*
|
|
495
|
-
* Claims are evicted after CLAIM_TTL_MS. When the hard cap is reached,
|
|
496
|
-
* only TTL-expired entries are evicted — active claims are never removed
|
|
497
|
-
* early, preserving the at-most-once delivery guarantee.
|
|
498
|
-
*/
|
|
499
|
-
export function claimRunDelivery(runId: string): boolean {
|
|
500
|
-
if (deliveredRuns.has(runId)) return false;
|
|
501
|
-
if (deliveredRuns.size >= MAX_DELIVERED_RUNS) {
|
|
502
|
-
// Only evict entries whose TTL has expired. Map iteration order
|
|
503
|
-
// matches insertion order, so oldest entries come first.
|
|
504
|
-
const now = Date.now();
|
|
505
|
-
for (const [id, insertedAt] of deliveredRuns) {
|
|
506
|
-
if (now - insertedAt >= CLAIM_TTL_MS) {
|
|
507
|
-
deliveredRuns.delete(id);
|
|
508
|
-
} else {
|
|
509
|
-
// Remaining entries are newer; stop scanning.
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
const now = Date.now();
|
|
515
|
-
deliveredRuns.set(runId, now);
|
|
516
|
-
setTimeout(() => deliveredRuns.delete(runId), CLAIM_TTL_MS);
|
|
517
|
-
return true;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Reset the deliver-once guard for a run. Used to release a claim when
|
|
522
|
-
* delivery fails (so the other racing poller can retry) and in tests
|
|
523
|
-
* for isolation between test cases.
|
|
524
|
-
*/
|
|
525
|
-
export function resetRunDeliveryClaim(runId: string): void {
|
|
526
|
-
deliveredRuns.delete(runId);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Clear all delivery claims. Used in tests for full isolation.
|
|
531
|
-
*/
|
|
532
|
-
export function resetAllRunDeliveryClaims(): void {
|
|
533
|
-
deliveredRuns.clear();
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Reset dead-lettered events back to 'failed' so the sweep can retry
|
|
538
|
-
* them. Resets attempt counter and sets an immediate retry_after.
|
|
539
|
-
*/
|
|
540
|
-
export function replayDeadLetters(eventIds: string[]): number {
|
|
541
|
-
const db = getDb();
|
|
542
|
-
const now = Date.now();
|
|
543
|
-
let count = 0;
|
|
544
|
-
for (const id of eventIds) {
|
|
545
|
-
const existing = db
|
|
546
|
-
.select({ id: channelInboundEvents.id })
|
|
547
|
-
.from(channelInboundEvents)
|
|
548
|
-
.where(
|
|
549
|
-
and(
|
|
550
|
-
eq(channelInboundEvents.id, id),
|
|
551
|
-
eq(channelInboundEvents.processingStatus, 'dead_letter'),
|
|
552
|
-
),
|
|
553
|
-
)
|
|
554
|
-
.get();
|
|
555
|
-
if (!existing) continue;
|
|
556
|
-
|
|
557
|
-
db.update(channelInboundEvents)
|
|
558
|
-
.set({
|
|
559
|
-
processingStatus: 'failed',
|
|
560
|
-
processingAttempts: 0,
|
|
561
|
-
lastProcessingError: null,
|
|
562
|
-
retryAfter: now,
|
|
563
|
-
updatedAt: now,
|
|
564
|
-
})
|
|
565
|
-
.where(eq(channelInboundEvents.id, id))
|
|
566
|
-
.run();
|
|
567
|
-
count++;
|
|
568
|
-
}
|
|
569
|
-
return count;
|
|
570
|
-
}
|
|
4
|
+
* This module re-exports from focused sub-modules for backward compatibility.
|
|
5
|
+
* New code should import directly from the relevant sub-module:
|
|
6
|
+
* - delivery-crud.ts — inbound event CRUD and payload management
|
|
7
|
+
* - delivery-status.ts — processing status tracking and dead-letter queue
|
|
8
|
+
* - delivery-channels.ts — verification replies, segment progress, delivery guards
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type { PendingVerificationReply } from './delivery-channels.js';
|
|
12
|
+
export {
|
|
13
|
+
claimRunDelivery,
|
|
14
|
+
clearPendingVerificationReply,
|
|
15
|
+
getDeliveredSegmentCount,
|
|
16
|
+
getPendingVerificationReply,
|
|
17
|
+
resetAllRunDeliveryClaims,
|
|
18
|
+
resetRunDeliveryClaim,
|
|
19
|
+
storePendingVerificationReply,
|
|
20
|
+
updateDeliveredSegmentCount,
|
|
21
|
+
} from './delivery-channels.js';
|
|
22
|
+
export type { InboundResult, RecordInboundOptions } from './delivery-crud.js';
|
|
23
|
+
export {
|
|
24
|
+
clearPayload,
|
|
25
|
+
findMessageBySourceId,
|
|
26
|
+
getLatestStoredPayload,
|
|
27
|
+
linkMessage,
|
|
28
|
+
recordInbound,
|
|
29
|
+
storePayload,
|
|
30
|
+
} from './delivery-crud.js';
|
|
31
|
+
export {
|
|
32
|
+
acknowledgeDelivery,
|
|
33
|
+
getDeadLetterEvents,
|
|
34
|
+
getRetryableEvents,
|
|
35
|
+
markProcessed,
|
|
36
|
+
recordProcessingFailure,
|
|
37
|
+
replayDeadLetters,
|
|
38
|
+
} from './delivery-status.js';
|