@agent-native/core 0.122.2 → 0.122.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-loop-with-resume.ts +12 -0
- package/corpus/core/src/cli/index.ts +2 -1
- package/corpus/core/src/cli/mcp.ts +13 -3
- package/corpus/core/src/cli/skills-content/rewind-skill.ts +36 -4
- package/corpus/core/src/cli/skills.ts +250 -37
- package/corpus/core/src/cli/telemetry-routing.ts +57 -0
- package/corpus/core/src/cli/telemetry.ts +3 -3
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +182 -0
- package/corpus/core/src/integrations/a2a-continuations-store.ts +122 -12
- package/corpus/core/src/integrations/integration-campaign-recovery.ts +127 -0
- package/corpus/core/src/integrations/integration-campaigns-store.ts +1039 -0
- package/corpus/core/src/integrations/integration-durable-dispatch.ts +28 -3
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +8 -0
- package/corpus/core/src/integrations/plugin.ts +453 -35
- package/corpus/core/src/integrations/webhook-handler.ts +469 -11
- package/corpus/core/src/mcp/screen-memory-stdio.ts +21 -2
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +7 -3
- package/corpus/templates/content/app/components/editor/body-hydration.ts +10 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -0
- package/corpus/templates/content/app/root.tsx +12 -1
- package/corpus/templates/content/changelog/2026-07-23-new-pages-open-immediately-while-slow-network-work-continues.md +6 -0
- package/corpus/templates/content/changelog/2026-07-25-database-preview-actions-open-reliably.md +6 -0
- package/corpus/templates/content/e2e/playwright.config.ts +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +7 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +7 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +12 -3
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/skills-content/rewind-skill.d.ts +1 -1
- package/dist/cli/skills-content/rewind-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/rewind-skill.js +36 -4
- package/dist/cli/skills-content/rewind-skill.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +191 -40
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/telemetry-routing.d.ts +2 -0
- package/dist/cli/telemetry-routing.d.ts.map +1 -0
- package/dist/cli/telemetry-routing.js +54 -0
- package/dist/cli/telemetry-routing.js.map +1 -0
- package/dist/cli/telemetry.js +3 -3
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +3 -3
- package/dist/integrations/a2a-continuation-processor.d.ts +14 -0
- package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +134 -1
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts +23 -0
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +99 -13
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/integration-campaign-recovery.d.ts +16 -0
- package/dist/integrations/integration-campaign-recovery.d.ts.map +1 -0
- package/dist/integrations/integration-campaign-recovery.js +94 -0
- package/dist/integrations/integration-campaign-recovery.js.map +1 -0
- package/dist/integrations/integration-campaigns-store.d.ts +121 -0
- package/dist/integrations/integration-campaigns-store.d.ts.map +1 -0
- package/dist/integrations/integration-campaigns-store.js +784 -0
- package/dist/integrations/integration-campaigns-store.js.map +1 -0
- package/dist/integrations/integration-durable-dispatch.d.ts +3 -0
- package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -1
- package/dist/integrations/integration-durable-dispatch.js +22 -3
- package/dist/integrations/integration-durable-dispatch.js.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +6 -0
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +325 -18
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts +16 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +368 -13
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +2 -0
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp/screen-memory-stdio.js +12 -2
- package/dist/mcp/screen-memory-stdio.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
- package/src/agent/run-loop-with-resume.ts +12 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/mcp.ts +13 -3
- package/src/cli/skills-content/rewind-skill.ts +36 -4
- package/src/cli/skills.ts +250 -37
- package/src/cli/telemetry-routing.ts +57 -0
- package/src/cli/telemetry.ts +3 -3
- package/src/integrations/a2a-continuation-processor.ts +182 -0
- package/src/integrations/a2a-continuations-store.ts +122 -12
- package/src/integrations/integration-campaign-recovery.ts +127 -0
- package/src/integrations/integration-campaigns-store.ts +1039 -0
- package/src/integrations/integration-durable-dispatch.ts +28 -3
- package/src/integrations/pending-tasks-retry-job.ts +8 -0
- package/src/integrations/plugin.ts +453 -35
- package/src/integrations/webhook-handler.ts +469 -11
- package/src/mcp/screen-memory-stdio.ts +21 -2
|
@@ -36,8 +36,12 @@ import { runWithRequestContext } from "../server/request-context.js";
|
|
|
36
36
|
import {
|
|
37
37
|
processA2AContinuationById,
|
|
38
38
|
processDueA2AContinuations,
|
|
39
|
+
recoverDueA2AContinuations,
|
|
39
40
|
} from "./a2a-continuation-processor.js";
|
|
40
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
failA2AContinuation,
|
|
43
|
+
hasActiveA2AContinuationsForIntegrationTask,
|
|
44
|
+
} from "./a2a-continuations-store.js";
|
|
41
45
|
import { mergeIntegrationAdapters } from "./adapter-overrides.js";
|
|
42
46
|
import { discordAdapter } from "./adapters/discord.js";
|
|
43
47
|
import { emailAdapter } from "./adapters/email.js";
|
|
@@ -69,11 +73,27 @@ import {
|
|
|
69
73
|
updateIntegrationInstallation,
|
|
70
74
|
upsertIntegrationInstallation,
|
|
71
75
|
} from "./installations-store.js";
|
|
76
|
+
import { recoverDueIntegrationCampaigns } from "./integration-campaign-recovery.js";
|
|
77
|
+
import {
|
|
78
|
+
claimIntegrationCampaignDeliveryForTask,
|
|
79
|
+
completeIntegrationCampaignTaskAfterA2A,
|
|
80
|
+
completeIntegrationCampaignTask,
|
|
81
|
+
failIntegrationCampaignTaskDeliveryContainment,
|
|
82
|
+
failDisabledIntegrationCampaignTask,
|
|
83
|
+
failIntegrationCampaign,
|
|
84
|
+
refreshIntegrationCampaignTaskA2AReceiptRetry,
|
|
85
|
+
terminalizeIntegrationCampaignForTask,
|
|
86
|
+
transitionIntegrationCampaignTaskToA2AReceiptRetry,
|
|
87
|
+
transitionIntegrationCampaignTaskToDeliveryRetry,
|
|
88
|
+
waitForA2AIntegrationCampaign,
|
|
89
|
+
} from "./integration-campaigns-store.js";
|
|
72
90
|
import {
|
|
73
91
|
dispatchPendingIntegrationTask,
|
|
92
|
+
INTEGRATION_CAMPAIGN_PROCESSOR_FIELD,
|
|
74
93
|
INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT,
|
|
75
94
|
integrationDispatchScopeValue,
|
|
76
95
|
isIntegrationDurableDispatchConfigured,
|
|
96
|
+
isIntegrationDurableDispatchEnabledForTask,
|
|
77
97
|
} from "./integration-durable-dispatch.js";
|
|
78
98
|
import {
|
|
79
99
|
forgetIntegrationMemory,
|
|
@@ -88,8 +108,8 @@ import {
|
|
|
88
108
|
} from "./pending-tasks-retry-job.js";
|
|
89
109
|
import {
|
|
90
110
|
claimPendingTask,
|
|
91
|
-
failTaskDeliveryTransition,
|
|
92
111
|
getNextPendingTaskForThread,
|
|
112
|
+
getPendingTask,
|
|
93
113
|
insertPendingTask,
|
|
94
114
|
isDuplicateEventError,
|
|
95
115
|
MAX_PENDING_TASK_ATTEMPTS,
|
|
@@ -98,6 +118,7 @@ import {
|
|
|
98
118
|
markTaskFailed,
|
|
99
119
|
markTaskRetryable,
|
|
100
120
|
stageTaskDeliveryPayload,
|
|
121
|
+
type PendingTask,
|
|
101
122
|
} from "./pending-tasks-store.js";
|
|
102
123
|
import {
|
|
103
124
|
claimNextComputerCommand,
|
|
@@ -172,6 +193,136 @@ import {
|
|
|
172
193
|
type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
|
|
173
194
|
|
|
174
195
|
let a2aContinuationRetryInterval: ReturnType<typeof setInterval> | null = null;
|
|
196
|
+
const INTEGRATION_DELIVERY_LEASE_MS = 2 * 60_000;
|
|
197
|
+
|
|
198
|
+
async function checkpointIntegrationDeliveryRetry(
|
|
199
|
+
task: PendingTask,
|
|
200
|
+
payload: string,
|
|
201
|
+
errorMessage: string,
|
|
202
|
+
event: unknown,
|
|
203
|
+
campaignLease?: {
|
|
204
|
+
campaignId: string;
|
|
205
|
+
runId: string;
|
|
206
|
+
leaseToken: string;
|
|
207
|
+
campaignStatus: "completed" | "failed" | "waiting-a2a";
|
|
208
|
+
},
|
|
209
|
+
): Promise<"requeued" | "superseded"> {
|
|
210
|
+
let terminalStatus: "completed" | "failed" | undefined;
|
|
211
|
+
let confirmedReceipt = false;
|
|
212
|
+
let awaitingA2ACompletion = false;
|
|
213
|
+
try {
|
|
214
|
+
const parsed = JSON.parse(
|
|
215
|
+
payload,
|
|
216
|
+
) as Partial<IntegrationResponseDeliveryTaskPayload>;
|
|
217
|
+
terminalStatus = parsed.campaignTerminalStatus;
|
|
218
|
+
confirmedReceipt = parsed.deliveryReceipt?.status === "delivered";
|
|
219
|
+
awaitingA2ACompletion = parsed.awaitingA2ACompletion === true;
|
|
220
|
+
} catch {}
|
|
221
|
+
if (awaitingA2ACompletion) {
|
|
222
|
+
if (campaignLease && campaignLease.campaignStatus !== "waiting-a2a") {
|
|
223
|
+
throw new Error("A2A receipt retry lease has the wrong custody mode");
|
|
224
|
+
}
|
|
225
|
+
const transitioned = campaignLease
|
|
226
|
+
? await transitionIntegrationCampaignTaskToA2AReceiptRetry(task.id, {
|
|
227
|
+
payload,
|
|
228
|
+
errorMessage,
|
|
229
|
+
campaignId: campaignLease.campaignId,
|
|
230
|
+
runId: campaignLease.runId,
|
|
231
|
+
leaseToken: campaignLease.leaseToken,
|
|
232
|
+
nextRunAt: Date.now() + 15_000,
|
|
233
|
+
})
|
|
234
|
+
: await refreshIntegrationCampaignTaskA2AReceiptRetry(task.id, {
|
|
235
|
+
payload,
|
|
236
|
+
errorMessage,
|
|
237
|
+
});
|
|
238
|
+
if (!transitioned) return "superseded";
|
|
239
|
+
await dispatchPendingIntegrationTask({
|
|
240
|
+
taskId: task.id,
|
|
241
|
+
task: {
|
|
242
|
+
platform: task.platform,
|
|
243
|
+
externalThreadId: task.externalThreadId,
|
|
244
|
+
platformContext: task.dispatchScope
|
|
245
|
+
? { channelId: task.dispatchScope }
|
|
246
|
+
: undefined,
|
|
247
|
+
},
|
|
248
|
+
event,
|
|
249
|
+
baseUrl: getBaseUrl(event),
|
|
250
|
+
campaignContinuation: true,
|
|
251
|
+
allowPortableConfirmedReceiptReconciliation: confirmedReceipt,
|
|
252
|
+
});
|
|
253
|
+
return "requeued";
|
|
254
|
+
}
|
|
255
|
+
if (terminalStatus && !campaignLease && !confirmedReceipt) {
|
|
256
|
+
throw new Error("Campaign delivery retry is missing its lease");
|
|
257
|
+
}
|
|
258
|
+
if (terminalStatus && campaignLease) {
|
|
259
|
+
if (terminalStatus !== campaignLease.campaignStatus) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
"Campaign delivery retry status does not match its lease",
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const transitioned = await transitionIntegrationCampaignTaskToDeliveryRetry(
|
|
265
|
+
task.id,
|
|
266
|
+
{
|
|
267
|
+
payload,
|
|
268
|
+
errorMessage,
|
|
269
|
+
campaignStatus: campaignLease.campaignStatus,
|
|
270
|
+
campaignId: campaignLease.campaignId,
|
|
271
|
+
runId: campaignLease.runId,
|
|
272
|
+
leaseToken: campaignLease.leaseToken,
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
if (!transitioned) {
|
|
276
|
+
return "superseded";
|
|
277
|
+
}
|
|
278
|
+
} else {
|
|
279
|
+
await markTaskDeliveryRetryable(task.id, payload, errorMessage);
|
|
280
|
+
}
|
|
281
|
+
await dispatchPendingIntegrationTask({
|
|
282
|
+
taskId: task.id,
|
|
283
|
+
task: {
|
|
284
|
+
platform: task.platform,
|
|
285
|
+
externalThreadId: task.externalThreadId,
|
|
286
|
+
platformContext: task.dispatchScope
|
|
287
|
+
? { channelId: task.dispatchScope }
|
|
288
|
+
: undefined,
|
|
289
|
+
},
|
|
290
|
+
event,
|
|
291
|
+
baseUrl: getBaseUrl(event),
|
|
292
|
+
});
|
|
293
|
+
return "requeued";
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function containFailedDeliveryTransition(
|
|
297
|
+
task: PendingTask,
|
|
298
|
+
errorMessage: string,
|
|
299
|
+
event: unknown,
|
|
300
|
+
): Promise<void> {
|
|
301
|
+
const contained = await failIntegrationCampaignTaskDeliveryContainment(
|
|
302
|
+
task.id,
|
|
303
|
+
errorMessage,
|
|
304
|
+
);
|
|
305
|
+
if (!contained) {
|
|
306
|
+
throw new Error("Delivery containment lost pending-task custody");
|
|
307
|
+
}
|
|
308
|
+
const nextTask = await getNextPendingTaskForThread(
|
|
309
|
+
task.platform,
|
|
310
|
+
task.externalThreadId,
|
|
311
|
+
);
|
|
312
|
+
if (!nextTask) return;
|
|
313
|
+
await dispatchPendingIntegrationTask({
|
|
314
|
+
taskId: nextTask.id,
|
|
315
|
+
task: {
|
|
316
|
+
platform: task.platform,
|
|
317
|
+
externalThreadId: task.externalThreadId,
|
|
318
|
+
platformContext: nextTask.dispatchScope
|
|
319
|
+
? { channelId: nextTask.dispatchScope }
|
|
320
|
+
: undefined,
|
|
321
|
+
},
|
|
322
|
+
event,
|
|
323
|
+
baseUrl: getBaseUrl(event),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
175
326
|
|
|
176
327
|
function startA2AContinuationRetryJob(
|
|
177
328
|
adapters: Map<string, PlatformAdapter>,
|
|
@@ -1627,12 +1778,41 @@ export function createIntegrationsPlugin(
|
|
|
1627
1778
|
if (!isIntegrationDurableDispatchConfigured()) {
|
|
1628
1779
|
return { ok: true, disabled: true };
|
|
1629
1780
|
}
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1781
|
+
const webhookBaseUrl = getBaseUrl(event);
|
|
1782
|
+
const [pendingTasks, campaigns, a2aContinuations] = await Promise.all([
|
|
1783
|
+
retryStuckPendingTasks({
|
|
1784
|
+
webhookBaseUrl,
|
|
1785
|
+
limit: 20,
|
|
1786
|
+
durableOnly: true,
|
|
1787
|
+
}).catch((error) => {
|
|
1788
|
+
console.error(
|
|
1789
|
+
"[integrations] Pending-task recovery failed:",
|
|
1790
|
+
error,
|
|
1791
|
+
);
|
|
1792
|
+
return { error: "pending-task-recovery-failed" };
|
|
1793
|
+
}),
|
|
1794
|
+
recoverDueIntegrationCampaigns({
|
|
1795
|
+
event,
|
|
1796
|
+
webhookBaseUrl,
|
|
1797
|
+
limit: 20,
|
|
1798
|
+
}).catch((error) => {
|
|
1799
|
+
console.error("[integrations] Campaign recovery failed:", error);
|
|
1800
|
+
return { error: "campaign-recovery-failed" };
|
|
1801
|
+
}),
|
|
1802
|
+
recoverDueA2AContinuations({
|
|
1803
|
+
webhookBaseUrl,
|
|
1804
|
+
limit: 10,
|
|
1805
|
+
}).catch((error) => {
|
|
1806
|
+
console.error("[integrations] A2A recovery failed:", error);
|
|
1807
|
+
return { error: "a2a-recovery-failed" };
|
|
1808
|
+
}),
|
|
1809
|
+
]);
|
|
1810
|
+
return {
|
|
1811
|
+
ok: true,
|
|
1812
|
+
pendingTasks,
|
|
1813
|
+
campaigns,
|
|
1814
|
+
a2aContinuations,
|
|
1815
|
+
};
|
|
1636
1816
|
}),
|
|
1637
1817
|
);
|
|
1638
1818
|
|
|
@@ -1653,6 +1833,7 @@ export function createIntegrationsPlugin(
|
|
|
1653
1833
|
const body = (await readBody(event)) as {
|
|
1654
1834
|
taskId?: string;
|
|
1655
1835
|
[AGENT_BACKGROUND_PROCESSOR_FIELD]?: string;
|
|
1836
|
+
[INTEGRATION_CAMPAIGN_PROCESSOR_FIELD]?: boolean;
|
|
1656
1837
|
};
|
|
1657
1838
|
const taskId = body?.taskId;
|
|
1658
1839
|
if (!taskId) {
|
|
@@ -1695,17 +1876,82 @@ export function createIntegrationsPlugin(
|
|
|
1695
1876
|
AGENT_BACKGROUND_PROCESSOR_INTEGRATION
|
|
1696
1877
|
? "background-acknowledged"
|
|
1697
1878
|
: "portable-unconfirmed";
|
|
1698
|
-
const
|
|
1879
|
+
const campaignContinuation =
|
|
1880
|
+
body[INTEGRATION_CAMPAIGN_PROCESSOR_FIELD] === true;
|
|
1881
|
+
const task = campaignContinuation
|
|
1882
|
+
? await getPendingTask(taskId)
|
|
1883
|
+
: await claimPendingTask(taskId, { dispatchOutcome });
|
|
1699
1884
|
if (!task) {
|
|
1700
1885
|
setResponseStatus(event, 200);
|
|
1701
1886
|
return { ok: true, skipped: "already-claimed-or-missing" };
|
|
1702
1887
|
}
|
|
1888
|
+
if (campaignContinuation && task.status !== "processing") {
|
|
1889
|
+
setResponseStatus(event, 200);
|
|
1890
|
+
return { ok: true, skipped: "campaign-task-not-processing" };
|
|
1891
|
+
}
|
|
1892
|
+
let taskPayload:
|
|
1893
|
+
| IntegrationSystemNoticeTaskPayload
|
|
1894
|
+
| IntegrationResponseDeliveryTaskPayload
|
|
1895
|
+
| { kind?: undefined };
|
|
1896
|
+
try {
|
|
1897
|
+
taskPayload = JSON.parse(task.payload) as typeof taskPayload;
|
|
1898
|
+
} catch {
|
|
1899
|
+
await markTaskFailed(task.id, "Invalid integration task payload");
|
|
1900
|
+
setResponseStatus(event, 400);
|
|
1901
|
+
return { error: "Invalid integration task payload" };
|
|
1902
|
+
}
|
|
1903
|
+
const durableCampaignEnabled =
|
|
1904
|
+
isIntegrationDurableDispatchEnabledForTask({
|
|
1905
|
+
platform: task.platform,
|
|
1906
|
+
externalThreadId: task.externalThreadId,
|
|
1907
|
+
platformContext: task.dispatchScope
|
|
1908
|
+
? { channelId: task.dispatchScope }
|
|
1909
|
+
: undefined,
|
|
1910
|
+
});
|
|
1911
|
+
const confirmedDeliveryReceipt =
|
|
1912
|
+
taskPayload.kind === "response-delivery" &&
|
|
1913
|
+
taskPayload.deliveryReceipt?.status === "delivered";
|
|
1914
|
+
if (
|
|
1915
|
+
campaignContinuation &&
|
|
1916
|
+
!durableCampaignEnabled &&
|
|
1917
|
+
!confirmedDeliveryReceipt
|
|
1918
|
+
) {
|
|
1919
|
+
await failDisabledIntegrationCampaignTask(task.id);
|
|
1920
|
+
const nextTask = await getNextPendingTaskForThread(
|
|
1921
|
+
task.platform,
|
|
1922
|
+
task.externalThreadId,
|
|
1923
|
+
);
|
|
1924
|
+
if (nextTask) {
|
|
1925
|
+
await dispatchPendingIntegrationTask({
|
|
1926
|
+
taskId: nextTask.id,
|
|
1927
|
+
task: {
|
|
1928
|
+
platform: task.platform,
|
|
1929
|
+
externalThreadId: task.externalThreadId,
|
|
1930
|
+
platformContext: nextTask.dispatchScope
|
|
1931
|
+
? { channelId: nextTask.dispatchScope }
|
|
1932
|
+
: undefined,
|
|
1933
|
+
},
|
|
1934
|
+
event,
|
|
1935
|
+
baseUrl: getBaseUrl(event),
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
setResponseStatus(event, 200);
|
|
1939
|
+
return { ok: true, failed: "campaign-disabled" };
|
|
1940
|
+
}
|
|
1703
1941
|
|
|
1704
1942
|
let deliveryRetryTransitionStarted = false;
|
|
1705
1943
|
let deliveryRetryRecovery:
|
|
1706
1944
|
| { payload: string; errorMessage: string }
|
|
1707
1945
|
| undefined;
|
|
1708
1946
|
let confirmedDeliveryRetryPayload: string | undefined;
|
|
1947
|
+
let campaignDeliveryLease:
|
|
1948
|
+
| {
|
|
1949
|
+
campaignId: string;
|
|
1950
|
+
runId: string;
|
|
1951
|
+
leaseToken: string;
|
|
1952
|
+
campaignStatus: "completed" | "failed" | "waiting-a2a";
|
|
1953
|
+
}
|
|
1954
|
+
| undefined;
|
|
1709
1955
|
try {
|
|
1710
1956
|
const adapter = adapterMap.get(task.platform);
|
|
1711
1957
|
if (!adapter) {
|
|
@@ -1720,11 +1966,10 @@ export function createIntegrationsPlugin(
|
|
|
1720
1966
|
isIntegrationCaller: true,
|
|
1721
1967
|
},
|
|
1722
1968
|
async () => {
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
if (taskPayload.kind === "system-notice") {
|
|
1969
|
+
if (
|
|
1970
|
+
!campaignContinuation &&
|
|
1971
|
+
taskPayload.kind === "system-notice"
|
|
1972
|
+
) {
|
|
1728
1973
|
if (!adapter.sendSystemNotice) {
|
|
1729
1974
|
throw new Error(
|
|
1730
1975
|
`Platform ${task.platform} cannot deliver system notices`,
|
|
@@ -1752,6 +1997,33 @@ export function createIntegrationsPlugin(
|
|
|
1752
1997
|
if (taskPayload.kind === "response-delivery") {
|
|
1753
1998
|
let receipt: void | PlatformDeliveryReceipt =
|
|
1754
1999
|
taskPayload.deliveryReceipt;
|
|
2000
|
+
let deliveryLease:
|
|
2001
|
+
| { campaignId: string; runId: string; leaseToken: string }
|
|
2002
|
+
| undefined;
|
|
2003
|
+
if (campaignContinuation && !receipt) {
|
|
2004
|
+
const runId = `integration-delivery-${crypto.randomUUID()}`;
|
|
2005
|
+
const leaseToken = crypto.randomUUID();
|
|
2006
|
+
const deliveryClaim =
|
|
2007
|
+
await claimIntegrationCampaignDeliveryForTask(task.id, {
|
|
2008
|
+
runId,
|
|
2009
|
+
leaseToken,
|
|
2010
|
+
leaseDurationMs: INTEGRATION_DELIVERY_LEASE_MS,
|
|
2011
|
+
});
|
|
2012
|
+
if (!deliveryClaim) return "campaign-active" as const;
|
|
2013
|
+
deliveryLease = {
|
|
2014
|
+
campaignId: deliveryClaim.id,
|
|
2015
|
+
runId,
|
|
2016
|
+
leaseToken,
|
|
2017
|
+
};
|
|
2018
|
+
campaignDeliveryLease = {
|
|
2019
|
+
...deliveryLease,
|
|
2020
|
+
campaignStatus:
|
|
2021
|
+
taskPayload.campaignTerminalStatus ??
|
|
2022
|
+
(taskPayload.awaitingA2ACompletion
|
|
2023
|
+
? "waiting-a2a"
|
|
2024
|
+
: "completed"),
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
1755
2027
|
if (!receipt) {
|
|
1756
2028
|
receipt = await adapter.sendResponse(
|
|
1757
2029
|
taskPayload.message,
|
|
@@ -1793,6 +2065,70 @@ export function createIntegrationsPlugin(
|
|
|
1793
2065
|
deliveredPayload,
|
|
1794
2066
|
receipt,
|
|
1795
2067
|
);
|
|
2068
|
+
const campaignTerminalStatus =
|
|
2069
|
+
taskPayload.campaignTerminalStatus;
|
|
2070
|
+
if (campaignTerminalStatus) {
|
|
2071
|
+
const errorMessage =
|
|
2072
|
+
"Integration campaign exhausted its continuation limit";
|
|
2073
|
+
const terminalized = deliveryLease
|
|
2074
|
+
? campaignTerminalStatus === "failed"
|
|
2075
|
+
? await failIntegrationCampaign(
|
|
2076
|
+
deliveryLease.campaignId,
|
|
2077
|
+
{
|
|
2078
|
+
runId: deliveryLease.runId,
|
|
2079
|
+
leaseToken: deliveryLease.leaseToken,
|
|
2080
|
+
errorMessage,
|
|
2081
|
+
},
|
|
2082
|
+
)
|
|
2083
|
+
: await completeIntegrationCampaignTask(
|
|
2084
|
+
deliveryLease.campaignId,
|
|
2085
|
+
{
|
|
2086
|
+
integrationTaskId: task.id,
|
|
2087
|
+
runId: deliveryLease.runId,
|
|
2088
|
+
leaseToken: deliveryLease.leaseToken,
|
|
2089
|
+
},
|
|
2090
|
+
)
|
|
2091
|
+
: await terminalizeIntegrationCampaignForTask(task.id, {
|
|
2092
|
+
status: campaignTerminalStatus,
|
|
2093
|
+
...(campaignTerminalStatus === "failed"
|
|
2094
|
+
? { errorMessage }
|
|
2095
|
+
: {}),
|
|
2096
|
+
});
|
|
2097
|
+
if (deliveryLease && !terminalized) {
|
|
2098
|
+
return "campaign-active" as const;
|
|
2099
|
+
}
|
|
2100
|
+
return campaignTerminalStatus === "failed"
|
|
2101
|
+
? ("campaign-failed" as const)
|
|
2102
|
+
: ("completed" as const);
|
|
2103
|
+
}
|
|
2104
|
+
if (taskPayload.awaitingA2ACompletion) {
|
|
2105
|
+
if (deliveryLease) {
|
|
2106
|
+
const waiting = await waitForA2AIntegrationCampaign(
|
|
2107
|
+
deliveryLease.campaignId,
|
|
2108
|
+
{
|
|
2109
|
+
runId: deliveryLease.runId,
|
|
2110
|
+
leaseToken: deliveryLease.leaseToken,
|
|
2111
|
+
nextRunAt: Date.now() + 15_000,
|
|
2112
|
+
},
|
|
2113
|
+
);
|
|
2114
|
+
if (!waiting) return "campaign-active" as const;
|
|
2115
|
+
}
|
|
2116
|
+
if (
|
|
2117
|
+
!(await hasActiveA2AContinuationsForIntegrationTask(
|
|
2118
|
+
task.id,
|
|
2119
|
+
))
|
|
2120
|
+
) {
|
|
2121
|
+
const completed =
|
|
2122
|
+
await completeIntegrationCampaignTaskAfterA2A(task.id);
|
|
2123
|
+
return completed
|
|
2124
|
+
? ("completed" as const)
|
|
2125
|
+
: ("campaign-active" as const);
|
|
2126
|
+
}
|
|
2127
|
+
return "campaign-active" as const;
|
|
2128
|
+
}
|
|
2129
|
+
if (campaignContinuation) {
|
|
2130
|
+
return "campaign-active" as const;
|
|
2131
|
+
}
|
|
1796
2132
|
return;
|
|
1797
2133
|
}
|
|
1798
2134
|
const resources = await loadResourcesForPrompt(
|
|
@@ -1801,26 +2137,47 @@ export function createIntegrationsPlugin(
|
|
|
1801
2137
|
options?.appId,
|
|
1802
2138
|
task.orgId,
|
|
1803
2139
|
);
|
|
1804
|
-
const result = await processIntegrationTask(
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2140
|
+
const result = await processIntegrationTask(
|
|
2141
|
+
task,
|
|
2142
|
+
{
|
|
2143
|
+
adapter,
|
|
2144
|
+
systemPrompt: baseSystemPrompt + resources,
|
|
2145
|
+
actions,
|
|
2146
|
+
initialToolNames,
|
|
2147
|
+
model,
|
|
2148
|
+
apiKey: getApiKey(),
|
|
2149
|
+
engine: options?.engine,
|
|
2150
|
+
ownerEmail: task.ownerEmail,
|
|
2151
|
+
appId: options?.appId,
|
|
2152
|
+
},
|
|
2153
|
+
{
|
|
2154
|
+
enabled: durableCampaignEnabled,
|
|
2155
|
+
continuationInvocation: campaignContinuation,
|
|
2156
|
+
},
|
|
2157
|
+
);
|
|
1815
2158
|
if (result?.status === "delivery-pending") {
|
|
1816
2159
|
deliveryRetryTransitionStarted = true;
|
|
1817
|
-
await
|
|
1818
|
-
task
|
|
2160
|
+
const checkpoint = await checkpointIntegrationDeliveryRetry(
|
|
2161
|
+
task,
|
|
1819
2162
|
JSON.stringify(result.payload),
|
|
1820
2163
|
result.errorMessage,
|
|
2164
|
+
event,
|
|
2165
|
+
result.campaignLease,
|
|
1821
2166
|
);
|
|
2167
|
+
if (checkpoint === "superseded") {
|
|
2168
|
+
return "campaign-active" as const;
|
|
2169
|
+
}
|
|
1822
2170
|
return "delivery-retry" as const;
|
|
1823
2171
|
}
|
|
2172
|
+
if (
|
|
2173
|
+
result?.status === "campaign-pending" ||
|
|
2174
|
+
result?.status === "campaign-active"
|
|
2175
|
+
) {
|
|
2176
|
+
return "campaign-active" as const;
|
|
2177
|
+
}
|
|
2178
|
+
if (result?.status === "campaign-failed") {
|
|
2179
|
+
return "campaign-failed" as const;
|
|
2180
|
+
}
|
|
1824
2181
|
return "completed" as const;
|
|
1825
2182
|
},
|
|
1826
2183
|
);
|
|
@@ -1828,6 +2185,36 @@ export function createIntegrationsPlugin(
|
|
|
1828
2185
|
setResponseStatus(event, 202);
|
|
1829
2186
|
return { ok: true, taskId, retrying: "response-delivery" };
|
|
1830
2187
|
}
|
|
2188
|
+
if (processingResult === "campaign-active") {
|
|
2189
|
+
setResponseStatus(event, 202);
|
|
2190
|
+
return { ok: true, taskId, continuing: true };
|
|
2191
|
+
}
|
|
2192
|
+
if (processingResult === "campaign-failed") {
|
|
2193
|
+
await markTaskFailed(
|
|
2194
|
+
taskId,
|
|
2195
|
+
"Integration campaign exhausted its continuation limit",
|
|
2196
|
+
);
|
|
2197
|
+
const nextTask = await getNextPendingTaskForThread(
|
|
2198
|
+
task.platform,
|
|
2199
|
+
task.externalThreadId,
|
|
2200
|
+
);
|
|
2201
|
+
if (nextTask) {
|
|
2202
|
+
await dispatchPendingIntegrationTask({
|
|
2203
|
+
taskId: nextTask.id,
|
|
2204
|
+
task: {
|
|
2205
|
+
platform: task.platform,
|
|
2206
|
+
externalThreadId: task.externalThreadId,
|
|
2207
|
+
platformContext: nextTask.dispatchScope
|
|
2208
|
+
? { channelId: nextTask.dispatchScope }
|
|
2209
|
+
: undefined,
|
|
2210
|
+
},
|
|
2211
|
+
event,
|
|
2212
|
+
baseUrl: getBaseUrl(event),
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
setResponseStatus(event, 200);
|
|
2216
|
+
return { ok: true, taskId, failed: "campaign-exhausted" };
|
|
2217
|
+
}
|
|
1831
2218
|
await markTaskCompleted(taskId);
|
|
1832
2219
|
const nextTask = await getNextPendingTaskForThread(
|
|
1833
2220
|
task.platform,
|
|
@@ -1863,11 +2250,17 @@ export function createIntegrationsPlugin(
|
|
|
1863
2250
|
: "processor failed";
|
|
1864
2251
|
if (deliveryRetryRecovery) {
|
|
1865
2252
|
try {
|
|
1866
|
-
await
|
|
1867
|
-
|
|
2253
|
+
const checkpoint = await checkpointIntegrationDeliveryRetry(
|
|
2254
|
+
task,
|
|
1868
2255
|
deliveryRetryRecovery.payload,
|
|
1869
2256
|
`${deliveryRetryRecovery.errorMessage}: ${errorMessage}`,
|
|
2257
|
+
event,
|
|
2258
|
+
campaignDeliveryLease,
|
|
1870
2259
|
);
|
|
2260
|
+
if (checkpoint === "superseded") {
|
|
2261
|
+
setResponseStatus(event, 202);
|
|
2262
|
+
return { ok: true, taskId, continuing: true };
|
|
2263
|
+
}
|
|
1871
2264
|
setResponseStatus(event, 202);
|
|
1872
2265
|
return { ok: true, taskId, retrying: "response-delivery" };
|
|
1873
2266
|
} catch (transitionError) {
|
|
@@ -1875,9 +2268,10 @@ export function createIntegrationsPlugin(
|
|
|
1875
2268
|
transitionError instanceof Error
|
|
1876
2269
|
? transitionError.message
|
|
1877
2270
|
: String(transitionError);
|
|
1878
|
-
await
|
|
1879
|
-
|
|
2271
|
+
await containFailedDeliveryTransition(
|
|
2272
|
+
task,
|
|
1880
2273
|
`Could not safely checkpoint the delivery receipt: ${transitionMessage}`,
|
|
2274
|
+
event,
|
|
1881
2275
|
).catch((failureTransitionError) => {
|
|
1882
2276
|
console.error(
|
|
1883
2277
|
"[integrations] Failed to contain delivery receipt transition failure:",
|
|
@@ -1887,24 +2281,48 @@ export function createIntegrationsPlugin(
|
|
|
1887
2281
|
}
|
|
1888
2282
|
} else if (confirmedDeliveryRetryPayload) {
|
|
1889
2283
|
try {
|
|
1890
|
-
await
|
|
1891
|
-
|
|
2284
|
+
const checkpoint = await checkpointIntegrationDeliveryRetry(
|
|
2285
|
+
task,
|
|
1892
2286
|
confirmedDeliveryRetryPayload,
|
|
1893
2287
|
`Provider delivery was confirmed but history persistence failed: ${errorMessage}`,
|
|
2288
|
+
event,
|
|
2289
|
+
campaignDeliveryLease,
|
|
1894
2290
|
);
|
|
2291
|
+
if (checkpoint === "superseded") {
|
|
2292
|
+
setResponseStatus(event, 202);
|
|
2293
|
+
return { ok: true, taskId, continuing: true };
|
|
2294
|
+
}
|
|
1895
2295
|
console.error("[integrations] process-task failure:", err);
|
|
1896
2296
|
setResponseStatus(event, 202);
|
|
1897
2297
|
return { ok: true, taskId, retrying: "response-delivery" };
|
|
1898
2298
|
} catch (transitionError) {
|
|
2299
|
+
const transitionMessage =
|
|
2300
|
+
transitionError instanceof Error
|
|
2301
|
+
? transitionError.message
|
|
2302
|
+
: String(transitionError);
|
|
1899
2303
|
console.error(
|
|
1900
2304
|
"[integrations] Failed to requeue confirmed delivery history:",
|
|
1901
2305
|
transitionError,
|
|
1902
2306
|
);
|
|
2307
|
+
await containFailedDeliveryTransition(
|
|
2308
|
+
task,
|
|
2309
|
+
`Could not safely checkpoint confirmed delivery history: ${transitionMessage}`,
|
|
2310
|
+
event,
|
|
2311
|
+
).catch((failureTransitionError) => {
|
|
2312
|
+
console.error(
|
|
2313
|
+
"[integrations] Failed to contain confirmed delivery history transition failure:",
|
|
2314
|
+
failureTransitionError,
|
|
2315
|
+
);
|
|
2316
|
+
});
|
|
2317
|
+
console.error("[integrations] process-task failure:", err);
|
|
2318
|
+
setResponseStatus(event, 500);
|
|
2319
|
+
return { error: "Internal task failed" };
|
|
1903
2320
|
}
|
|
1904
2321
|
} else if (deliveryRetryTransitionStarted) {
|
|
1905
|
-
await
|
|
1906
|
-
|
|
2322
|
+
await containFailedDeliveryTransition(
|
|
2323
|
+
task,
|
|
1907
2324
|
`Could not safely checkpoint the delivery retry: ${errorMessage}`,
|
|
2325
|
+
event,
|
|
1908
2326
|
).catch((transitionError) => {
|
|
1909
2327
|
console.error(
|
|
1910
2328
|
"[integrations] Failed to contain delivery retry transition failure:",
|