@agent-native/core 0.107.1 → 0.107.2
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/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/artifact-response.ts +32 -10
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +107 -3
- package/corpus/core/src/integrations/pending-tasks-store.ts +98 -1
- package/corpus/core/src/integrations/plugin.ts +129 -4
- package/corpus/core/src/integrations/webhook-handler.ts +339 -36
- package/corpus/core/src/server/agent-chat-plugin.ts +93 -8
- package/dist/a2a/artifact-response.d.ts +5 -1
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +18 -12
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/integrations/a2a-continuation-processor.js +62 -5
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts +3 -0
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +75 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +81 -5
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts +23 -2
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +257 -35
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts +6 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +71 -6
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
+
import { type A2AArtifactIdentity } from "../a2a/artifact-response.js";
|
|
2
3
|
import type { AgentEngine } from "../agent/engine/types.js";
|
|
3
4
|
import { type ActionEntry } from "../agent/production-agent.js";
|
|
4
5
|
import { type PendingTask } from "./pending-tasks-store.js";
|
|
5
|
-
import type { PlatformAdapter, IncomingMessage } from "./types.js";
|
|
6
|
+
import type { PlatformAdapter, IncomingMessage, OutgoingMessage, PlatformDeliveryReceipt } from "./types.js";
|
|
7
|
+
export type IntegrationResponseDeliveryTaskPayload = {
|
|
8
|
+
kind: "response-delivery";
|
|
9
|
+
incoming: IncomingMessage;
|
|
10
|
+
message: OutgoingMessage;
|
|
11
|
+
placeholderRef?: string;
|
|
12
|
+
internalThreadId?: string;
|
|
13
|
+
userMessageId?: string;
|
|
14
|
+
assistantMessageId?: string;
|
|
15
|
+
deliveryReceipt?: PlatformDeliveryReceipt;
|
|
16
|
+
deliveredAt?: string;
|
|
17
|
+
artifacts?: A2AArtifactIdentity[];
|
|
18
|
+
};
|
|
19
|
+
export type ProcessIntegrationTaskResult = {
|
|
20
|
+
status: "completed";
|
|
21
|
+
} | {
|
|
22
|
+
status: "delivery-pending";
|
|
23
|
+
payload: IntegrationResponseDeliveryTaskPayload;
|
|
24
|
+
errorMessage: string;
|
|
25
|
+
};
|
|
6
26
|
export interface WebhookHandlerOptions {
|
|
7
27
|
adapter: PlatformAdapter;
|
|
8
28
|
/** Resolved system prompt string */
|
|
@@ -85,5 +105,6 @@ export declare function resolveBaseUrl(event: H3Event): string;
|
|
|
85
105
|
* processor endpoint in `plugin.ts`. This is a fresh function execution, so
|
|
86
106
|
* it gets its own timeout budget independent of the inbound webhook handler.
|
|
87
107
|
*/
|
|
88
|
-
export declare function processIntegrationTask(task: PendingTask, options: WebhookHandlerOptions): Promise<
|
|
108
|
+
export declare function processIntegrationTask(task: PendingTask, options: WebhookHandlerOptions): Promise<ProcessIntegrationTaskResult>;
|
|
109
|
+
export declare function recordIntegrationResponseDelivery(payload: IntegrationResponseDeliveryTaskPayload, receipt: PlatformDeliveryReceipt): Promise<void>;
|
|
89
110
|
//# sourceMappingURL=webhook-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ import { attachToolSearch } from "../agent/tool-search.js";
|
|
|
12
12
|
import { createThread, getThread } from "../chat-threads/store.js";
|
|
13
13
|
import { updateThreadData } from "../chat-threads/store.js";
|
|
14
14
|
import { isLocalDatabase } from "../db/client.js";
|
|
15
|
-
import { resolveOrgIdForEmail } from "../org/context.js";
|
|
15
|
+
import { getOrgA2ASecret, resolveOrgIdForEmail } from "../org/context.js";
|
|
16
16
|
import { withConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
17
17
|
import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
|
|
18
18
|
import { canUseDeployCredentialFallbackForRequest, readDeployCredentialEnv, } from "../server/credential-provider.js";
|
|
@@ -22,7 +22,7 @@ import { A2A_CONTINUATION_QUEUED_MARKER } from "./a2a-continuation-marker.js";
|
|
|
22
22
|
import { clearIntegrationAwaitingInput, setIntegrationAwaitingInput, } from "./awaiting-input-store.js";
|
|
23
23
|
import { loadIntegrationMemoryPrompt } from "./integration-memory.js";
|
|
24
24
|
import { signInternalToken } from "./internal-token.js";
|
|
25
|
-
import { insertPendingTask, isDuplicateEventError, } from "./pending-tasks-store.js";
|
|
25
|
+
import { insertPendingTask, isDuplicateEventError, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
|
|
26
26
|
import { integrationScopeSubjectKey } from "./scope-store.js";
|
|
27
27
|
import { getThreadMapping, saveThreadMapping } from "./thread-mapping-store.js";
|
|
28
28
|
import { listIntegrationUsageBudgets, releaseIntegrationUsageBudget, reserveIntegrationUsageBudget, settleIntegrationUsageBudget, } from "./usage-budget-store.js";
|
|
@@ -59,6 +59,13 @@ function buildEventDedupKey(incoming) {
|
|
|
59
59
|
: String(incoming.timestamp);
|
|
60
60
|
return `${incoming.platform}:${incoming.externalThreadId}:${eventReference}`;
|
|
61
61
|
}
|
|
62
|
+
function buildDeliveryHistoryMessageIds(incoming) {
|
|
63
|
+
const eventKey = buildEventDedupKey(incoming);
|
|
64
|
+
return {
|
|
65
|
+
userMessageId: `integration-${eventKey}-user`,
|
|
66
|
+
assistantMessageId: `integration-${eventKey}-assistant`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
62
69
|
function explicitEngineName(engineOption) {
|
|
63
70
|
if (!engineOption)
|
|
64
71
|
return undefined;
|
|
@@ -361,7 +368,7 @@ export function resolveBaseUrl(event) {
|
|
|
361
368
|
export async function processIntegrationTask(task, options) {
|
|
362
369
|
const parsed = JSON.parse(task.payload);
|
|
363
370
|
await recordInboundIntegrationAudit(task, parsed.incoming);
|
|
364
|
-
|
|
371
|
+
return processIncomingMessage(parsed.incoming, options, {
|
|
365
372
|
taskId: task.id,
|
|
366
373
|
attempts: task.attempts,
|
|
367
374
|
placeholderRef: parsed.placeholderRef,
|
|
@@ -458,10 +465,41 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
458
465
|
});
|
|
459
466
|
if (!budgetReservations.allowed) {
|
|
460
467
|
const outgoing = adapter.formatAgentResponse("This channel or requester has reached its configured AI usage budget. An admin can review the budget in Messaging settings.");
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
468
|
+
let deliveryPayload = {
|
|
469
|
+
kind: "response-delivery",
|
|
470
|
+
incoming,
|
|
471
|
+
message: outgoing,
|
|
472
|
+
...(opts.placeholderRef ? { placeholderRef: opts.placeholderRef } : {}),
|
|
473
|
+
};
|
|
474
|
+
try {
|
|
475
|
+
if (opts.taskId) {
|
|
476
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(deliveryPayload));
|
|
477
|
+
}
|
|
478
|
+
const receipt = await adapter.sendResponse(outgoing, incoming, {
|
|
479
|
+
placeholderRef: opts.placeholderRef,
|
|
480
|
+
});
|
|
481
|
+
if (receipt?.status !== "delivered") {
|
|
482
|
+
throw new Error(`${incoming.platform} response completed without delivery proof`);
|
|
483
|
+
}
|
|
484
|
+
deliveryPayload = {
|
|
485
|
+
...deliveryPayload,
|
|
486
|
+
deliveryReceipt: receipt,
|
|
487
|
+
deliveredAt: new Date().toISOString(),
|
|
488
|
+
};
|
|
489
|
+
if (opts.taskId) {
|
|
490
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(deliveryPayload));
|
|
491
|
+
}
|
|
492
|
+
return { status: "completed" };
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
return {
|
|
496
|
+
status: "delivery-pending",
|
|
497
|
+
payload: deliveryPayload,
|
|
498
|
+
errorMessage: error instanceof Error
|
|
499
|
+
? error.message.slice(0, 1000)
|
|
500
|
+
: `${incoming.platform} response delivery failed`,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
465
503
|
}
|
|
466
504
|
let threadId;
|
|
467
505
|
let thread;
|
|
@@ -530,11 +568,13 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
530
568
|
// A2A delegation. Without this, getRequestOrgId() returns undefined and
|
|
531
569
|
// call-agent can't look up the org's a2a_secret or org_domain.
|
|
532
570
|
let orgId;
|
|
571
|
+
let artifactSecrets;
|
|
533
572
|
let runnableActions;
|
|
534
573
|
let tools;
|
|
535
574
|
let availableTools;
|
|
536
575
|
try {
|
|
537
576
|
orgId = opts.orgId ?? (await resolveOrgIdForEmail(ownerEmail));
|
|
577
|
+
artifactSecrets = await resolveIntegrationArtifactSecrets(orgId);
|
|
538
578
|
// Attach tool-search on a shallow copy so framework additions merged in
|
|
539
579
|
// by `createIntegrationsPlugin` (integration memory, `call-agent`) can be
|
|
540
580
|
// deferred behind it without mutating the plugin's long-lived registry.
|
|
@@ -555,7 +595,7 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
555
595
|
let budgetsSettled = false;
|
|
556
596
|
// Wait for the run to complete inside this fresh function execution.
|
|
557
597
|
// We use a Promise so the processor endpoint can await the full lifecycle.
|
|
558
|
-
|
|
598
|
+
return new Promise((resolve) => {
|
|
559
599
|
startRun(runId, threadId, async (send, signal) => {
|
|
560
600
|
await runWithRequestContext({
|
|
561
601
|
userEmail: ownerEmail,
|
|
@@ -638,6 +678,10 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
638
678
|
}, async (completedRun) => {
|
|
639
679
|
let keepSlackInputWindow = false;
|
|
640
680
|
let queuedA2AContinuation = false;
|
|
681
|
+
let outgoingForDelivery;
|
|
682
|
+
let stagedDeliveryPayload;
|
|
683
|
+
let threadCheckpoint;
|
|
684
|
+
let outcome = { status: "completed" };
|
|
641
685
|
try {
|
|
642
686
|
queuedA2AContinuation = hasQueuedA2AContinuation(completedRun);
|
|
643
687
|
const slackInputRequest = incoming.platform === "slack"
|
|
@@ -720,6 +764,23 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
720
764
|
const outgoing = adapter.formatAgentResponse(responseText, {
|
|
721
765
|
threadDeepLinkUrl,
|
|
722
766
|
});
|
|
767
|
+
outgoingForDelivery = outgoing;
|
|
768
|
+
stagedDeliveryPayload = {
|
|
769
|
+
kind: "response-delivery",
|
|
770
|
+
incoming,
|
|
771
|
+
message: outgoing,
|
|
772
|
+
...(opts.placeholderRef
|
|
773
|
+
? { placeholderRef: opts.placeholderRef }
|
|
774
|
+
: {}),
|
|
775
|
+
internalThreadId: threadId,
|
|
776
|
+
...buildDeliveryHistoryMessageIds(incoming),
|
|
777
|
+
artifacts: extractA2AArtifactIdentities(toolResults, {
|
|
778
|
+
persistedArtifactSecrets: artifactSecrets,
|
|
779
|
+
}),
|
|
780
|
+
};
|
|
781
|
+
if (opts.taskId) {
|
|
782
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(stagedDeliveryPayload));
|
|
783
|
+
}
|
|
723
784
|
let deliveryReceipt;
|
|
724
785
|
if (queuedA2AContinuation && progress?.ref) {
|
|
725
786
|
// Post substantive parent results as a normal thread reply while
|
|
@@ -744,22 +805,28 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
744
805
|
placeholderRef: opts.placeholderRef,
|
|
745
806
|
});
|
|
746
807
|
}
|
|
747
|
-
|
|
748
|
-
if (!delivered) {
|
|
808
|
+
if (deliveryReceipt?.status !== "delivered") {
|
|
749
809
|
throw new Error(`${incoming.platform} response completed without delivery proof`);
|
|
750
810
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
: {}),
|
|
760
|
-
};
|
|
811
|
+
const deliveredAt = new Date().toISOString();
|
|
812
|
+
stagedDeliveryPayload = {
|
|
813
|
+
...stagedDeliveryPayload,
|
|
814
|
+
deliveryReceipt,
|
|
815
|
+
deliveredAt,
|
|
816
|
+
};
|
|
817
|
+
if (opts.taskId) {
|
|
818
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(stagedDeliveryPayload));
|
|
761
819
|
}
|
|
762
|
-
|
|
820
|
+
deliveredResponse = {
|
|
821
|
+
platform: incoming.platform,
|
|
822
|
+
status: "delivered",
|
|
823
|
+
text: outgoing.text,
|
|
824
|
+
deliveredAt,
|
|
825
|
+
...(deliveryReceipt.messageRefs?.length
|
|
826
|
+
? { messageRefs: deliveryReceipt.messageRefs }
|
|
827
|
+
: {}),
|
|
828
|
+
};
|
|
829
|
+
if (slackInputRequest && incoming.senderId) {
|
|
763
830
|
await setIntegrationAwaitingInput({
|
|
764
831
|
platform: "slack",
|
|
765
832
|
externalThreadId: incoming.externalThreadId,
|
|
@@ -798,7 +865,22 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
798
865
|
}
|
|
799
866
|
}
|
|
800
867
|
// Persist thread data
|
|
801
|
-
await persistThreadData(threadId, incoming.text, completedRun, thread, deliveredResponse, toolResults);
|
|
868
|
+
threadCheckpoint = await persistThreadData(threadId, incoming.text, completedRun, thread, deliveredResponse, toolResults, stagedDeliveryPayload, artifactSecrets);
|
|
869
|
+
if (outgoingForDelivery && stagedDeliveryPayload) {
|
|
870
|
+
if (!threadCheckpoint) {
|
|
871
|
+
throw new Error("Integration response history checkpoint failed");
|
|
872
|
+
}
|
|
873
|
+
stagedDeliveryPayload = {
|
|
874
|
+
...stagedDeliveryPayload,
|
|
875
|
+
userMessageId: threadCheckpoint.userMessageId,
|
|
876
|
+
...(threadCheckpoint.assistantMessageId
|
|
877
|
+
? { assistantMessageId: threadCheckpoint.assistantMessageId }
|
|
878
|
+
: {}),
|
|
879
|
+
};
|
|
880
|
+
if (opts.taskId) {
|
|
881
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(stagedDeliveryPayload));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
802
884
|
await recordIntegrationUsage({
|
|
803
885
|
usage,
|
|
804
886
|
ownerEmail,
|
|
@@ -814,9 +896,52 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
814
896
|
}
|
|
815
897
|
catch (err) {
|
|
816
898
|
console.error(`[integrations] Error sending response to ${incoming.platform}:`, err);
|
|
899
|
+
if (outgoingForDelivery) {
|
|
900
|
+
const errorMessage = err instanceof Error
|
|
901
|
+
? err.message.slice(0, 1000)
|
|
902
|
+
: `${incoming.platform} response delivery failed`;
|
|
903
|
+
if (!stagedDeliveryPayload?.deliveryReceipt) {
|
|
904
|
+
threadCheckpoint = await persistThreadData(threadId, incoming.text, completedRun, thread, undefined, collectToolResultSummaries(completedRun), stagedDeliveryPayload, artifactSecrets);
|
|
905
|
+
}
|
|
906
|
+
if (usage) {
|
|
907
|
+
await recordIntegrationUsage({
|
|
908
|
+
usage,
|
|
909
|
+
ownerEmail,
|
|
910
|
+
appId: options.appId,
|
|
911
|
+
runId,
|
|
912
|
+
threadId,
|
|
913
|
+
taskId: opts.taskId,
|
|
914
|
+
orgId: orgId ?? undefined,
|
|
915
|
+
incoming,
|
|
916
|
+
}).catch(() => { });
|
|
917
|
+
try {
|
|
918
|
+
await settleApplicableIntegrationBudgets(budgetReservations.reservations, usage);
|
|
919
|
+
budgetsSettled = true;
|
|
920
|
+
}
|
|
921
|
+
catch { }
|
|
922
|
+
}
|
|
923
|
+
outcome = {
|
|
924
|
+
status: "delivery-pending",
|
|
925
|
+
payload: {
|
|
926
|
+
...(stagedDeliveryPayload ?? {
|
|
927
|
+
kind: "response-delivery",
|
|
928
|
+
incoming,
|
|
929
|
+
message: outgoingForDelivery,
|
|
930
|
+
internalThreadId: threadId,
|
|
931
|
+
}),
|
|
932
|
+
...(threadCheckpoint?.userMessageId
|
|
933
|
+
? { userMessageId: threadCheckpoint.userMessageId }
|
|
934
|
+
: {}),
|
|
935
|
+
...(threadCheckpoint?.assistantMessageId
|
|
936
|
+
? { assistantMessageId: threadCheckpoint.assistantMessageId }
|
|
937
|
+
: {}),
|
|
938
|
+
},
|
|
939
|
+
errorMessage,
|
|
940
|
+
};
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
817
943
|
// A queued continuation owns the final platform response. Later
|
|
818
|
-
// bookkeeping failures
|
|
819
|
-
// must not close its resumable native stream with a false failure.
|
|
944
|
+
// bookkeeping failures must not close its stream with a false failure.
|
|
820
945
|
if (queuedA2AContinuation)
|
|
821
946
|
return;
|
|
822
947
|
// Last-ditch: try to post a brief apology so the thread isn't silent.
|
|
@@ -838,7 +963,7 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
838
963
|
if (!budgetsSettled) {
|
|
839
964
|
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
840
965
|
}
|
|
841
|
-
resolve();
|
|
966
|
+
resolve(outcome);
|
|
842
967
|
}
|
|
843
968
|
},
|
|
844
969
|
// Integration workers are ordinary self-dispatched serverless requests,
|
|
@@ -1130,7 +1255,7 @@ function hasSubstantiveA2APartialAnswer(text) {
|
|
|
1130
1255
|
* Persist the user message and agent response to the thread data,
|
|
1131
1256
|
* so the conversation history is available in the web UI too.
|
|
1132
1257
|
*/
|
|
1133
|
-
async function persistThreadData(threadId, userText, completedRun, thread, deliveredResponse, toolResults = []) {
|
|
1258
|
+
async function persistThreadData(threadId, userText, completedRun, thread, deliveredResponse, toolResults = [], messageIds, artifactSecrets = []) {
|
|
1134
1259
|
try {
|
|
1135
1260
|
let repo;
|
|
1136
1261
|
try {
|
|
@@ -1143,32 +1268,129 @@ async function persistThreadData(threadId, userText, completedRun, thread, deliv
|
|
|
1143
1268
|
repo.messages = [];
|
|
1144
1269
|
// Add user message
|
|
1145
1270
|
const userMsg = {
|
|
1146
|
-
id: `msg-${Date.now()}-user`,
|
|
1271
|
+
id: messageIds?.userMessageId ?? `msg-${Date.now()}-user`,
|
|
1147
1272
|
role: "user",
|
|
1148
1273
|
content: [{ type: "text", text: userText }],
|
|
1149
1274
|
createdAt: new Date().toISOString(),
|
|
1150
1275
|
};
|
|
1151
1276
|
// Build assistant message from run events
|
|
1152
|
-
const
|
|
1277
|
+
const builtAssistantMsg = buildAssistantMessage(completedRun.events ?? [], completedRun.runId);
|
|
1278
|
+
if (builtAssistantMsg && messageIds?.assistantMessageId) {
|
|
1279
|
+
builtAssistantMsg.id = messageIds.assistantMessageId;
|
|
1280
|
+
}
|
|
1281
|
+
const existingAssistantMsg = builtAssistantMsg
|
|
1282
|
+
? repo.messages.find((message) => message?.id === builtAssistantMsg.id)
|
|
1283
|
+
: undefined;
|
|
1284
|
+
const assistantMsg = existingAssistantMsg ?? builtAssistantMsg;
|
|
1153
1285
|
if (assistantMsg) {
|
|
1154
1286
|
assistantMsg.metadata.integrationDeliveryAttempted = true;
|
|
1287
|
+
const artifactIdentities = extractA2AArtifactIdentities(toolResults, {
|
|
1288
|
+
persistedArtifactSecrets: artifactSecrets,
|
|
1289
|
+
});
|
|
1290
|
+
if (artifactIdentities.length > 0) {
|
|
1291
|
+
assistantMsg.metadata.integrationArtifacts = artifactIdentities;
|
|
1292
|
+
}
|
|
1155
1293
|
if (deliveredResponse) {
|
|
1156
1294
|
assistantMsg.metadata.integrationDelivery = deliveredResponse;
|
|
1157
|
-
const artifactIdentities = extractA2AArtifactIdentities(toolResults);
|
|
1158
|
-
if (artifactIdentities.length > 0) {
|
|
1159
|
-
assistantMsg.metadata.integrationArtifacts = artifactIdentities;
|
|
1160
|
-
}
|
|
1161
1295
|
}
|
|
1162
1296
|
}
|
|
1163
|
-
repo.messages.
|
|
1164
|
-
|
|
1165
|
-
|
|
1297
|
+
if (!repo.messages.some((message) => message?.id === userMsg.id)) {
|
|
1298
|
+
repo.messages.push(userMsg);
|
|
1299
|
+
}
|
|
1300
|
+
if (builtAssistantMsg && !existingAssistantMsg) {
|
|
1301
|
+
repo.messages.push(builtAssistantMsg);
|
|
1166
1302
|
}
|
|
1167
1303
|
const meta = extractThreadMeta(repo);
|
|
1168
1304
|
await updateThreadData(threadId, JSON.stringify(repo), meta.title || thread?.title || "Integration Chat", meta.preview || thread?.preview || "", repo.messages.length);
|
|
1305
|
+
return {
|
|
1306
|
+
userMessageId: userMsg.id,
|
|
1307
|
+
...(assistantMsg?.id ? { assistantMessageId: assistantMsg.id } : {}),
|
|
1308
|
+
};
|
|
1169
1309
|
}
|
|
1170
1310
|
catch {
|
|
1171
1311
|
// Best-effort persistence
|
|
1312
|
+
return undefined;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
async function resolveIntegrationArtifactSecrets(orgId) {
|
|
1316
|
+
const secrets = [];
|
|
1317
|
+
const add = (secret) => {
|
|
1318
|
+
const value = secret?.trim();
|
|
1319
|
+
if (value && !secrets.includes(value))
|
|
1320
|
+
secrets.push(value);
|
|
1321
|
+
};
|
|
1322
|
+
add(process.env.A2A_SECRET);
|
|
1323
|
+
if (orgId) {
|
|
1324
|
+
try {
|
|
1325
|
+
add(await getOrgA2ASecret(orgId));
|
|
1326
|
+
}
|
|
1327
|
+
catch { }
|
|
1172
1328
|
}
|
|
1329
|
+
return secrets;
|
|
1330
|
+
}
|
|
1331
|
+
export async function recordIntegrationResponseDelivery(payload, receipt) {
|
|
1332
|
+
if (!payload.internalThreadId)
|
|
1333
|
+
return;
|
|
1334
|
+
const thread = await getThread(payload.internalThreadId);
|
|
1335
|
+
if (!thread)
|
|
1336
|
+
throw new Error("Integration delivery thread was not found");
|
|
1337
|
+
let repo;
|
|
1338
|
+
try {
|
|
1339
|
+
repo = JSON.parse(thread.threadData || "{}");
|
|
1340
|
+
}
|
|
1341
|
+
catch {
|
|
1342
|
+
throw new Error("Integration delivery thread data is invalid");
|
|
1343
|
+
}
|
|
1344
|
+
if (!Array.isArray(repo.messages)) {
|
|
1345
|
+
repo.messages = [];
|
|
1346
|
+
}
|
|
1347
|
+
const stableMessageIds = buildDeliveryHistoryMessageIds(payload.incoming);
|
|
1348
|
+
const userMessageId = payload.userMessageId ?? stableMessageIds.userMessageId;
|
|
1349
|
+
const assistantMessageId = payload.assistantMessageId ?? stableMessageIds.assistantMessageId;
|
|
1350
|
+
const userMsg = userMessageId
|
|
1351
|
+
? repo.messages.find((message) => message?.id === userMessageId)
|
|
1352
|
+
: undefined;
|
|
1353
|
+
let assistantMsg = assistantMessageId
|
|
1354
|
+
? repo.messages.find((message) => message?.id === assistantMessageId)
|
|
1355
|
+
: undefined;
|
|
1356
|
+
const createdAt = payload.deliveredAt ?? new Date().toISOString();
|
|
1357
|
+
if (!userMsg) {
|
|
1358
|
+
repo.messages.push({
|
|
1359
|
+
id: userMessageId,
|
|
1360
|
+
role: "user",
|
|
1361
|
+
content: [{ type: "text", text: payload.incoming.text }],
|
|
1362
|
+
createdAt,
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
if (!assistantMsg) {
|
|
1366
|
+
assistantMsg = {
|
|
1367
|
+
id: assistantMessageId,
|
|
1368
|
+
role: "assistant",
|
|
1369
|
+
content: [{ type: "text", text: payload.message.text }],
|
|
1370
|
+
createdAt,
|
|
1371
|
+
metadata: {
|
|
1372
|
+
integrationDeliveryAttempted: true,
|
|
1373
|
+
...(payload.artifacts?.length
|
|
1374
|
+
? { integrationArtifacts: payload.artifacts }
|
|
1375
|
+
: {}),
|
|
1376
|
+
},
|
|
1377
|
+
};
|
|
1378
|
+
repo.messages.push(assistantMsg);
|
|
1379
|
+
}
|
|
1380
|
+
if (!assistantMsg.metadata || typeof assistantMsg.metadata !== "object") {
|
|
1381
|
+
assistantMsg.metadata = {};
|
|
1382
|
+
}
|
|
1383
|
+
assistantMsg.metadata.integrationDeliveryAttempted = true;
|
|
1384
|
+
assistantMsg.metadata.integrationDelivery = {
|
|
1385
|
+
platform: payload.incoming.platform,
|
|
1386
|
+
status: "delivered",
|
|
1387
|
+
text: payload.message.text,
|
|
1388
|
+
deliveredAt: payload.deliveredAt ?? new Date().toISOString(),
|
|
1389
|
+
...(receipt.messageRefs?.length
|
|
1390
|
+
? { messageRefs: receipt.messageRefs }
|
|
1391
|
+
: {}),
|
|
1392
|
+
};
|
|
1393
|
+
const meta = extractThreadMeta(repo);
|
|
1394
|
+
await updateThreadData(payload.internalThreadId, JSON.stringify(repo), meta.title || thread.title || "Integration Chat", meta.preview || thread.preview || "", repo.messages.length);
|
|
1173
1395
|
}
|
|
1174
1396
|
//# sourceMappingURL=webhook-handler.js.map
|