@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
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
appendA2AArtifactLinks,
|
|
5
5
|
buildA2AVerifiedMutationReceipt,
|
|
6
6
|
extractA2AArtifactIdentities,
|
|
7
|
+
type A2AArtifactIdentity,
|
|
7
8
|
type A2AToolResultSummary,
|
|
8
9
|
} from "../a2a/artifact-response.js";
|
|
9
10
|
import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js";
|
|
@@ -43,7 +44,7 @@ import { attachToolSearch } from "../agent/tool-search.js";
|
|
|
43
44
|
import { createThread, getThread } from "../chat-threads/store.js";
|
|
44
45
|
import { updateThreadData } from "../chat-threads/store.js";
|
|
45
46
|
import { isLocalDatabase } from "../db/client.js";
|
|
46
|
-
import { resolveOrgIdForEmail } from "../org/context.js";
|
|
47
|
+
import { getOrgA2ASecret, resolveOrgIdForEmail } from "../org/context.js";
|
|
47
48
|
import { withConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
48
49
|
import { FRAMEWORK_ROUTE_PREFIX } from "../server/core-routes-plugin.js";
|
|
49
50
|
import {
|
|
@@ -62,6 +63,7 @@ import { signInternalToken } from "./internal-token.js";
|
|
|
62
63
|
import {
|
|
63
64
|
insertPendingTask,
|
|
64
65
|
isDuplicateEventError,
|
|
66
|
+
stageTaskDeliveryPayload,
|
|
65
67
|
type PendingTask,
|
|
66
68
|
} from "./pending-tasks-store.js";
|
|
67
69
|
import { integrationScopeSubjectKey } from "./scope-store.js";
|
|
@@ -69,6 +71,7 @@ import { getThreadMapping, saveThreadMapping } from "./thread-mapping-store.js";
|
|
|
69
71
|
import type {
|
|
70
72
|
PlatformAdapter,
|
|
71
73
|
IncomingMessage,
|
|
74
|
+
OutgoingMessage,
|
|
72
75
|
PlatformDeliveryReceipt,
|
|
73
76
|
} from "./types.js";
|
|
74
77
|
import {
|
|
@@ -92,6 +95,27 @@ type ToolDoneEvent = {
|
|
|
92
95
|
completedSideEffect?: boolean;
|
|
93
96
|
};
|
|
94
97
|
|
|
98
|
+
export type IntegrationResponseDeliveryTaskPayload = {
|
|
99
|
+
kind: "response-delivery";
|
|
100
|
+
incoming: IncomingMessage;
|
|
101
|
+
message: OutgoingMessage;
|
|
102
|
+
placeholderRef?: string;
|
|
103
|
+
internalThreadId?: string;
|
|
104
|
+
userMessageId?: string;
|
|
105
|
+
assistantMessageId?: string;
|
|
106
|
+
deliveryReceipt?: PlatformDeliveryReceipt;
|
|
107
|
+
deliveredAt?: string;
|
|
108
|
+
artifacts?: A2AArtifactIdentity[];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export type ProcessIntegrationTaskResult =
|
|
112
|
+
| { status: "completed" }
|
|
113
|
+
| {
|
|
114
|
+
status: "delivery-pending";
|
|
115
|
+
payload: IntegrationResponseDeliveryTaskPayload;
|
|
116
|
+
errorMessage: string;
|
|
117
|
+
};
|
|
118
|
+
|
|
95
119
|
/**
|
|
96
120
|
* Build a stable per-event dedup key from the incoming message. The same
|
|
97
121
|
* key is computed for every retry of the same event from the platform —
|
|
@@ -124,6 +148,17 @@ function buildEventDedupKey(incoming: IncomingMessage): string {
|
|
|
124
148
|
return `${incoming.platform}:${incoming.externalThreadId}:${eventReference}`;
|
|
125
149
|
}
|
|
126
150
|
|
|
151
|
+
function buildDeliveryHistoryMessageIds(incoming: IncomingMessage): {
|
|
152
|
+
userMessageId: string;
|
|
153
|
+
assistantMessageId: string;
|
|
154
|
+
} {
|
|
155
|
+
const eventKey = buildEventDedupKey(incoming);
|
|
156
|
+
return {
|
|
157
|
+
userMessageId: `integration-${eventKey}-user`,
|
|
158
|
+
assistantMessageId: `integration-${eventKey}-assistant`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
127
162
|
export interface WebhookHandlerOptions {
|
|
128
163
|
adapter: PlatformAdapter;
|
|
129
164
|
/** Resolved system prompt string */
|
|
@@ -546,7 +581,7 @@ export function resolveBaseUrl(event: H3Event): string {
|
|
|
546
581
|
export async function processIntegrationTask(
|
|
547
582
|
task: PendingTask,
|
|
548
583
|
options: WebhookHandlerOptions,
|
|
549
|
-
): Promise<
|
|
584
|
+
): Promise<ProcessIntegrationTaskResult> {
|
|
550
585
|
const parsed = JSON.parse(task.payload) as {
|
|
551
586
|
incoming: IncomingMessage;
|
|
552
587
|
placeholderRef?: string;
|
|
@@ -555,7 +590,7 @@ export async function processIntegrationTask(
|
|
|
555
590
|
|
|
556
591
|
await recordInboundIntegrationAudit(task, parsed.incoming);
|
|
557
592
|
|
|
558
|
-
|
|
593
|
+
return processIncomingMessage(parsed.incoming, options, {
|
|
559
594
|
taskId: task.id,
|
|
560
595
|
attempts: task.attempts,
|
|
561
596
|
placeholderRef: parsed.placeholderRef,
|
|
@@ -615,7 +650,7 @@ async function processIncomingMessage(
|
|
|
615
650
|
orgId?: string;
|
|
616
651
|
principalType?: "user" | "service";
|
|
617
652
|
} = {},
|
|
618
|
-
): Promise<
|
|
653
|
+
): Promise<ProcessIntegrationTaskResult> {
|
|
619
654
|
const {
|
|
620
655
|
adapter,
|
|
621
656
|
systemPrompt,
|
|
@@ -698,10 +733,49 @@ async function processIncomingMessage(
|
|
|
698
733
|
const outgoing = adapter.formatAgentResponse(
|
|
699
734
|
"This channel or requester has reached its configured AI usage budget. An admin can review the budget in Messaging settings.",
|
|
700
735
|
);
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
736
|
+
let deliveryPayload: IntegrationResponseDeliveryTaskPayload = {
|
|
737
|
+
kind: "response-delivery",
|
|
738
|
+
incoming,
|
|
739
|
+
message: outgoing,
|
|
740
|
+
...(opts.placeholderRef ? { placeholderRef: opts.placeholderRef } : {}),
|
|
741
|
+
};
|
|
742
|
+
try {
|
|
743
|
+
if (opts.taskId) {
|
|
744
|
+
await stageTaskDeliveryPayload(
|
|
745
|
+
opts.taskId,
|
|
746
|
+
JSON.stringify(deliveryPayload),
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
const receipt = await adapter.sendResponse(outgoing, incoming, {
|
|
750
|
+
placeholderRef: opts.placeholderRef,
|
|
751
|
+
});
|
|
752
|
+
if (receipt?.status !== "delivered") {
|
|
753
|
+
throw new Error(
|
|
754
|
+
`${incoming.platform} response completed without delivery proof`,
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
deliveryPayload = {
|
|
758
|
+
...deliveryPayload,
|
|
759
|
+
deliveryReceipt: receipt,
|
|
760
|
+
deliveredAt: new Date().toISOString(),
|
|
761
|
+
};
|
|
762
|
+
if (opts.taskId) {
|
|
763
|
+
await stageTaskDeliveryPayload(
|
|
764
|
+
opts.taskId,
|
|
765
|
+
JSON.stringify(deliveryPayload),
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
return { status: "completed" };
|
|
769
|
+
} catch (error) {
|
|
770
|
+
return {
|
|
771
|
+
status: "delivery-pending",
|
|
772
|
+
payload: deliveryPayload,
|
|
773
|
+
errorMessage:
|
|
774
|
+
error instanceof Error
|
|
775
|
+
? error.message.slice(0, 1000)
|
|
776
|
+
: `${incoming.platform} response delivery failed`,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
705
779
|
}
|
|
706
780
|
|
|
707
781
|
let threadId: string;
|
|
@@ -785,11 +859,13 @@ async function processIncomingMessage(
|
|
|
785
859
|
// A2A delegation. Without this, getRequestOrgId() returns undefined and
|
|
786
860
|
// call-agent can't look up the org's a2a_secret or org_domain.
|
|
787
861
|
let orgId: string | null | undefined;
|
|
862
|
+
let artifactSecrets: string[];
|
|
788
863
|
let runnableActions: Record<string, ActionEntry>;
|
|
789
864
|
let tools: ReturnType<typeof actionsToEngineTools>;
|
|
790
865
|
let availableTools: ReturnType<typeof actionsToEngineTools>;
|
|
791
866
|
try {
|
|
792
867
|
orgId = opts.orgId ?? (await resolveOrgIdForEmail(ownerEmail));
|
|
868
|
+
artifactSecrets = await resolveIntegrationArtifactSecrets(orgId);
|
|
793
869
|
// Attach tool-search on a shallow copy so framework additions merged in
|
|
794
870
|
// by `createIntegrationsPlugin` (integration memory, `call-agent`) can be
|
|
795
871
|
// deferred behind it without mutating the plugin's long-lived registry.
|
|
@@ -811,7 +887,7 @@ async function processIncomingMessage(
|
|
|
811
887
|
|
|
812
888
|
// Wait for the run to complete inside this fresh function execution.
|
|
813
889
|
// We use a Promise so the processor endpoint can await the full lifecycle.
|
|
814
|
-
|
|
890
|
+
return new Promise<ProcessIntegrationTaskResult>((resolve) => {
|
|
815
891
|
startRun(
|
|
816
892
|
runId,
|
|
817
893
|
threadId,
|
|
@@ -921,6 +997,14 @@ async function processIncomingMessage(
|
|
|
921
997
|
async (completedRun: ActiveRun) => {
|
|
922
998
|
let keepSlackInputWindow = false;
|
|
923
999
|
let queuedA2AContinuation = false;
|
|
1000
|
+
let outgoingForDelivery: OutgoingMessage | undefined;
|
|
1001
|
+
let stagedDeliveryPayload:
|
|
1002
|
+
| IntegrationResponseDeliveryTaskPayload
|
|
1003
|
+
| undefined;
|
|
1004
|
+
let threadCheckpoint:
|
|
1005
|
+
| { userMessageId: string; assistantMessageId?: string }
|
|
1006
|
+
| undefined;
|
|
1007
|
+
let outcome: ProcessIntegrationTaskResult = { status: "completed" };
|
|
924
1008
|
try {
|
|
925
1009
|
queuedA2AContinuation = hasQueuedA2AContinuation(completedRun);
|
|
926
1010
|
const slackInputRequest =
|
|
@@ -1029,6 +1113,26 @@ async function processIncomingMessage(
|
|
|
1029
1113
|
const outgoing = adapter.formatAgentResponse(responseText, {
|
|
1030
1114
|
threadDeepLinkUrl,
|
|
1031
1115
|
});
|
|
1116
|
+
outgoingForDelivery = outgoing;
|
|
1117
|
+
stagedDeliveryPayload = {
|
|
1118
|
+
kind: "response-delivery",
|
|
1119
|
+
incoming,
|
|
1120
|
+
message: outgoing,
|
|
1121
|
+
...(opts.placeholderRef
|
|
1122
|
+
? { placeholderRef: opts.placeholderRef }
|
|
1123
|
+
: {}),
|
|
1124
|
+
internalThreadId: threadId,
|
|
1125
|
+
...buildDeliveryHistoryMessageIds(incoming),
|
|
1126
|
+
artifacts: extractA2AArtifactIdentities(toolResults, {
|
|
1127
|
+
persistedArtifactSecrets: artifactSecrets,
|
|
1128
|
+
}),
|
|
1129
|
+
};
|
|
1130
|
+
if (opts.taskId) {
|
|
1131
|
+
await stageTaskDeliveryPayload(
|
|
1132
|
+
opts.taskId,
|
|
1133
|
+
JSON.stringify(stagedDeliveryPayload),
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1032
1136
|
let deliveryReceipt: void | PlatformDeliveryReceipt;
|
|
1033
1137
|
if (queuedA2AContinuation && progress?.ref) {
|
|
1034
1138
|
// Post substantive parent results as a normal thread reply while
|
|
@@ -1054,24 +1158,33 @@ async function processIncomingMessage(
|
|
|
1054
1158
|
placeholderRef: opts.placeholderRef,
|
|
1055
1159
|
});
|
|
1056
1160
|
}
|
|
1057
|
-
|
|
1058
|
-
if (!delivered) {
|
|
1161
|
+
if (deliveryReceipt?.status !== "delivered") {
|
|
1059
1162
|
throw new Error(
|
|
1060
1163
|
`${incoming.platform} response completed without delivery proof`,
|
|
1061
1164
|
);
|
|
1062
1165
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1166
|
+
const deliveredAt = new Date().toISOString();
|
|
1167
|
+
stagedDeliveryPayload = {
|
|
1168
|
+
...stagedDeliveryPayload,
|
|
1169
|
+
deliveryReceipt,
|
|
1170
|
+
deliveredAt,
|
|
1171
|
+
};
|
|
1172
|
+
if (opts.taskId) {
|
|
1173
|
+
await stageTaskDeliveryPayload(
|
|
1174
|
+
opts.taskId,
|
|
1175
|
+
JSON.stringify(stagedDeliveryPayload),
|
|
1176
|
+
);
|
|
1073
1177
|
}
|
|
1074
|
-
|
|
1178
|
+
deliveredResponse = {
|
|
1179
|
+
platform: incoming.platform,
|
|
1180
|
+
status: "delivered",
|
|
1181
|
+
text: outgoing.text,
|
|
1182
|
+
deliveredAt,
|
|
1183
|
+
...(deliveryReceipt.messageRefs?.length
|
|
1184
|
+
? { messageRefs: deliveryReceipt.messageRefs }
|
|
1185
|
+
: {}),
|
|
1186
|
+
};
|
|
1187
|
+
if (slackInputRequest && incoming.senderId) {
|
|
1075
1188
|
await setIntegrationAwaitingInput({
|
|
1076
1189
|
platform: "slack",
|
|
1077
1190
|
externalThreadId: incoming.externalThreadId,
|
|
@@ -1113,14 +1226,34 @@ async function processIncomingMessage(
|
|
|
1113
1226
|
}
|
|
1114
1227
|
|
|
1115
1228
|
// Persist thread data
|
|
1116
|
-
await persistThreadData(
|
|
1229
|
+
threadCheckpoint = await persistThreadData(
|
|
1117
1230
|
threadId,
|
|
1118
1231
|
incoming.text,
|
|
1119
1232
|
completedRun,
|
|
1120
1233
|
thread,
|
|
1121
1234
|
deliveredResponse,
|
|
1122
1235
|
toolResults,
|
|
1236
|
+
stagedDeliveryPayload,
|
|
1237
|
+
artifactSecrets,
|
|
1123
1238
|
);
|
|
1239
|
+
if (outgoingForDelivery && stagedDeliveryPayload) {
|
|
1240
|
+
if (!threadCheckpoint) {
|
|
1241
|
+
throw new Error("Integration response history checkpoint failed");
|
|
1242
|
+
}
|
|
1243
|
+
stagedDeliveryPayload = {
|
|
1244
|
+
...stagedDeliveryPayload,
|
|
1245
|
+
userMessageId: threadCheckpoint.userMessageId,
|
|
1246
|
+
...(threadCheckpoint.assistantMessageId
|
|
1247
|
+
? { assistantMessageId: threadCheckpoint.assistantMessageId }
|
|
1248
|
+
: {}),
|
|
1249
|
+
};
|
|
1250
|
+
if (opts.taskId) {
|
|
1251
|
+
await stageTaskDeliveryPayload(
|
|
1252
|
+
opts.taskId,
|
|
1253
|
+
JSON.stringify(stagedDeliveryPayload),
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1124
1257
|
await recordIntegrationUsage({
|
|
1125
1258
|
usage,
|
|
1126
1259
|
ownerEmail,
|
|
@@ -1141,9 +1274,64 @@ async function processIncomingMessage(
|
|
|
1141
1274
|
`[integrations] Error sending response to ${incoming.platform}:`,
|
|
1142
1275
|
err,
|
|
1143
1276
|
);
|
|
1277
|
+
if (outgoingForDelivery) {
|
|
1278
|
+
const errorMessage =
|
|
1279
|
+
err instanceof Error
|
|
1280
|
+
? err.message.slice(0, 1000)
|
|
1281
|
+
: `${incoming.platform} response delivery failed`;
|
|
1282
|
+
if (!stagedDeliveryPayload?.deliveryReceipt) {
|
|
1283
|
+
threadCheckpoint = await persistThreadData(
|
|
1284
|
+
threadId,
|
|
1285
|
+
incoming.text,
|
|
1286
|
+
completedRun,
|
|
1287
|
+
thread,
|
|
1288
|
+
undefined,
|
|
1289
|
+
collectToolResultSummaries(completedRun),
|
|
1290
|
+
stagedDeliveryPayload,
|
|
1291
|
+
artifactSecrets,
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
if (usage) {
|
|
1295
|
+
await recordIntegrationUsage({
|
|
1296
|
+
usage,
|
|
1297
|
+
ownerEmail,
|
|
1298
|
+
appId: options.appId,
|
|
1299
|
+
runId,
|
|
1300
|
+
threadId,
|
|
1301
|
+
taskId: opts.taskId,
|
|
1302
|
+
orgId: orgId ?? undefined,
|
|
1303
|
+
incoming,
|
|
1304
|
+
}).catch(() => {});
|
|
1305
|
+
try {
|
|
1306
|
+
await settleApplicableIntegrationBudgets(
|
|
1307
|
+
budgetReservations.reservations,
|
|
1308
|
+
usage,
|
|
1309
|
+
);
|
|
1310
|
+
budgetsSettled = true;
|
|
1311
|
+
} catch {}
|
|
1312
|
+
}
|
|
1313
|
+
outcome = {
|
|
1314
|
+
status: "delivery-pending",
|
|
1315
|
+
payload: {
|
|
1316
|
+
...(stagedDeliveryPayload ?? {
|
|
1317
|
+
kind: "response-delivery",
|
|
1318
|
+
incoming,
|
|
1319
|
+
message: outgoingForDelivery,
|
|
1320
|
+
internalThreadId: threadId,
|
|
1321
|
+
}),
|
|
1322
|
+
...(threadCheckpoint?.userMessageId
|
|
1323
|
+
? { userMessageId: threadCheckpoint.userMessageId }
|
|
1324
|
+
: {}),
|
|
1325
|
+
...(threadCheckpoint?.assistantMessageId
|
|
1326
|
+
? { assistantMessageId: threadCheckpoint.assistantMessageId }
|
|
1327
|
+
: {}),
|
|
1328
|
+
},
|
|
1329
|
+
errorMessage,
|
|
1330
|
+
};
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1144
1333
|
// A queued continuation owns the final platform response. Later
|
|
1145
|
-
// bookkeeping failures
|
|
1146
|
-
// must not close its resumable native stream with a false failure.
|
|
1334
|
+
// bookkeeping failures must not close its stream with a false failure.
|
|
1147
1335
|
if (queuedA2AContinuation) return;
|
|
1148
1336
|
// Last-ditch: try to post a brief apology so the thread isn't silent.
|
|
1149
1337
|
try {
|
|
@@ -1170,7 +1358,7 @@ async function processIncomingMessage(
|
|
|
1170
1358
|
budgetReservations.reservations,
|
|
1171
1359
|
);
|
|
1172
1360
|
}
|
|
1173
|
-
resolve();
|
|
1361
|
+
resolve(outcome);
|
|
1174
1362
|
}
|
|
1175
1363
|
},
|
|
1176
1364
|
// Integration workers are ordinary self-dispatched serverless requests,
|
|
@@ -1583,7 +1771,12 @@ async function persistThreadData(
|
|
|
1583
1771
|
messageRefs?: string[];
|
|
1584
1772
|
},
|
|
1585
1773
|
toolResults: A2AToolResultSummary[] = [],
|
|
1586
|
-
|
|
1774
|
+
messageIds?: Pick<
|
|
1775
|
+
IntegrationResponseDeliveryTaskPayload,
|
|
1776
|
+
"userMessageId" | "assistantMessageId"
|
|
1777
|
+
>,
|
|
1778
|
+
artifactSecrets: readonly string[] = [],
|
|
1779
|
+
): Promise<{ userMessageId: string; assistantMessageId?: string } | undefined> {
|
|
1587
1780
|
try {
|
|
1588
1781
|
let repo: any;
|
|
1589
1782
|
try {
|
|
@@ -1595,31 +1788,44 @@ async function persistThreadData(
|
|
|
1595
1788
|
|
|
1596
1789
|
// Add user message
|
|
1597
1790
|
const userMsg = {
|
|
1598
|
-
id: `msg-${Date.now()}-user`,
|
|
1791
|
+
id: messageIds?.userMessageId ?? `msg-${Date.now()}-user`,
|
|
1599
1792
|
role: "user",
|
|
1600
1793
|
content: [{ type: "text", text: userText }],
|
|
1601
1794
|
createdAt: new Date().toISOString(),
|
|
1602
1795
|
};
|
|
1603
1796
|
|
|
1604
1797
|
// Build assistant message from run events
|
|
1605
|
-
const
|
|
1798
|
+
const builtAssistantMsg = buildAssistantMessage(
|
|
1606
1799
|
completedRun.events ?? [],
|
|
1607
1800
|
completedRun.runId,
|
|
1608
1801
|
);
|
|
1802
|
+
if (builtAssistantMsg && messageIds?.assistantMessageId) {
|
|
1803
|
+
builtAssistantMsg.id = messageIds.assistantMessageId;
|
|
1804
|
+
}
|
|
1805
|
+
const existingAssistantMsg = builtAssistantMsg
|
|
1806
|
+
? repo.messages.find(
|
|
1807
|
+
(message: any) => message?.id === builtAssistantMsg.id,
|
|
1808
|
+
)
|
|
1809
|
+
: undefined;
|
|
1810
|
+
const assistantMsg = existingAssistantMsg ?? builtAssistantMsg;
|
|
1609
1811
|
if (assistantMsg) {
|
|
1610
1812
|
assistantMsg.metadata.integrationDeliveryAttempted = true;
|
|
1813
|
+
const artifactIdentities = extractA2AArtifactIdentities(toolResults, {
|
|
1814
|
+
persistedArtifactSecrets: artifactSecrets,
|
|
1815
|
+
});
|
|
1816
|
+
if (artifactIdentities.length > 0) {
|
|
1817
|
+
assistantMsg.metadata.integrationArtifacts = artifactIdentities;
|
|
1818
|
+
}
|
|
1611
1819
|
if (deliveredResponse) {
|
|
1612
1820
|
assistantMsg.metadata.integrationDelivery = deliveredResponse;
|
|
1613
|
-
const artifactIdentities = extractA2AArtifactIdentities(toolResults);
|
|
1614
|
-
if (artifactIdentities.length > 0) {
|
|
1615
|
-
assistantMsg.metadata.integrationArtifacts = artifactIdentities;
|
|
1616
|
-
}
|
|
1617
1821
|
}
|
|
1618
1822
|
}
|
|
1619
1823
|
|
|
1620
|
-
repo.messages.
|
|
1621
|
-
|
|
1622
|
-
|
|
1824
|
+
if (!repo.messages.some((message: any) => message?.id === userMsg.id)) {
|
|
1825
|
+
repo.messages.push(userMsg);
|
|
1826
|
+
}
|
|
1827
|
+
if (builtAssistantMsg && !existingAssistantMsg) {
|
|
1828
|
+
repo.messages.push(builtAssistantMsg);
|
|
1623
1829
|
}
|
|
1624
1830
|
|
|
1625
1831
|
const meta = extractThreadMeta(repo);
|
|
@@ -1630,7 +1836,104 @@ async function persistThreadData(
|
|
|
1630
1836
|
meta.preview || thread?.preview || "",
|
|
1631
1837
|
repo.messages.length,
|
|
1632
1838
|
);
|
|
1839
|
+
return {
|
|
1840
|
+
userMessageId: userMsg.id,
|
|
1841
|
+
...(assistantMsg?.id ? { assistantMessageId: assistantMsg.id } : {}),
|
|
1842
|
+
};
|
|
1633
1843
|
} catch {
|
|
1634
1844
|
// Best-effort persistence
|
|
1845
|
+
return undefined;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
async function resolveIntegrationArtifactSecrets(
|
|
1850
|
+
orgId: string | null | undefined,
|
|
1851
|
+
): Promise<string[]> {
|
|
1852
|
+
const secrets: string[] = [];
|
|
1853
|
+
const add = (secret: string | null | undefined) => {
|
|
1854
|
+
const value = secret?.trim();
|
|
1855
|
+
if (value && !secrets.includes(value)) secrets.push(value);
|
|
1856
|
+
};
|
|
1857
|
+
add(process.env.A2A_SECRET);
|
|
1858
|
+
if (orgId) {
|
|
1859
|
+
try {
|
|
1860
|
+
add(await getOrgA2ASecret(orgId));
|
|
1861
|
+
} catch {}
|
|
1635
1862
|
}
|
|
1863
|
+
return secrets;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
export async function recordIntegrationResponseDelivery(
|
|
1867
|
+
payload: IntegrationResponseDeliveryTaskPayload,
|
|
1868
|
+
receipt: PlatformDeliveryReceipt,
|
|
1869
|
+
): Promise<void> {
|
|
1870
|
+
if (!payload.internalThreadId) return;
|
|
1871
|
+
const thread = await getThread(payload.internalThreadId);
|
|
1872
|
+
if (!thread) throw new Error("Integration delivery thread was not found");
|
|
1873
|
+
|
|
1874
|
+
let repo: any;
|
|
1875
|
+
try {
|
|
1876
|
+
repo = JSON.parse(thread.threadData || "{}");
|
|
1877
|
+
} catch {
|
|
1878
|
+
throw new Error("Integration delivery thread data is invalid");
|
|
1879
|
+
}
|
|
1880
|
+
if (!Array.isArray(repo.messages)) {
|
|
1881
|
+
repo.messages = [];
|
|
1882
|
+
}
|
|
1883
|
+
const stableMessageIds = buildDeliveryHistoryMessageIds(payload.incoming);
|
|
1884
|
+
const userMessageId = payload.userMessageId ?? stableMessageIds.userMessageId;
|
|
1885
|
+
const assistantMessageId =
|
|
1886
|
+
payload.assistantMessageId ?? stableMessageIds.assistantMessageId;
|
|
1887
|
+
const userMsg = userMessageId
|
|
1888
|
+
? repo.messages.find((message: any) => message?.id === userMessageId)
|
|
1889
|
+
: undefined;
|
|
1890
|
+
let assistantMsg = assistantMessageId
|
|
1891
|
+
? repo.messages.find((message: any) => message?.id === assistantMessageId)
|
|
1892
|
+
: undefined;
|
|
1893
|
+
const createdAt = payload.deliveredAt ?? new Date().toISOString();
|
|
1894
|
+
if (!userMsg) {
|
|
1895
|
+
repo.messages.push({
|
|
1896
|
+
id: userMessageId,
|
|
1897
|
+
role: "user",
|
|
1898
|
+
content: [{ type: "text", text: payload.incoming.text }],
|
|
1899
|
+
createdAt,
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
if (!assistantMsg) {
|
|
1903
|
+
assistantMsg = {
|
|
1904
|
+
id: assistantMessageId,
|
|
1905
|
+
role: "assistant",
|
|
1906
|
+
content: [{ type: "text", text: payload.message.text }],
|
|
1907
|
+
createdAt,
|
|
1908
|
+
metadata: {
|
|
1909
|
+
integrationDeliveryAttempted: true,
|
|
1910
|
+
...(payload.artifacts?.length
|
|
1911
|
+
? { integrationArtifacts: payload.artifacts }
|
|
1912
|
+
: {}),
|
|
1913
|
+
},
|
|
1914
|
+
};
|
|
1915
|
+
repo.messages.push(assistantMsg);
|
|
1916
|
+
}
|
|
1917
|
+
if (!assistantMsg.metadata || typeof assistantMsg.metadata !== "object") {
|
|
1918
|
+
assistantMsg.metadata = {};
|
|
1919
|
+
}
|
|
1920
|
+
assistantMsg.metadata.integrationDeliveryAttempted = true;
|
|
1921
|
+
assistantMsg.metadata.integrationDelivery = {
|
|
1922
|
+
platform: payload.incoming.platform,
|
|
1923
|
+
status: "delivered",
|
|
1924
|
+
text: payload.message.text,
|
|
1925
|
+
deliveredAt: payload.deliveredAt ?? new Date().toISOString(),
|
|
1926
|
+
...(receipt.messageRefs?.length
|
|
1927
|
+
? { messageRefs: receipt.messageRefs }
|
|
1928
|
+
: {}),
|
|
1929
|
+
};
|
|
1930
|
+
|
|
1931
|
+
const meta = extractThreadMeta(repo);
|
|
1932
|
+
await updateThreadData(
|
|
1933
|
+
payload.internalThreadId,
|
|
1934
|
+
JSON.stringify(repo),
|
|
1935
|
+
meta.title || thread.title || "Integration Chat",
|
|
1936
|
+
meta.preview || thread.preview || "",
|
|
1937
|
+
repo.messages.length,
|
|
1938
|
+
);
|
|
1636
1939
|
}
|