@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
|
@@ -13,8 +13,8 @@ import { getH3App, markDefaultPluginProvided, } from "../server/framework-reques
|
|
|
13
13
|
import { decodeOAuthState, encodeOAuthState, oauthCallbackResponse, oauthErrorPage, resolveOAuthRedirectUri, } from "../server/google-oauth.js";
|
|
14
14
|
import { readBody } from "../server/h3-helpers.js";
|
|
15
15
|
import { runWithRequestContext } from "../server/request-context.js";
|
|
16
|
-
import { processA2AContinuationById, processDueA2AContinuations, } from "./a2a-continuation-processor.js";
|
|
17
|
-
import { failA2AContinuation } from "./a2a-continuations-store.js";
|
|
16
|
+
import { processA2AContinuationById, processDueA2AContinuations, recoverDueA2AContinuations, } from "./a2a-continuation-processor.js";
|
|
17
|
+
import { failA2AContinuation, hasActiveA2AContinuationsForIntegrationTask, } from "./a2a-continuations-store.js";
|
|
18
18
|
import { mergeIntegrationAdapters } from "./adapter-overrides.js";
|
|
19
19
|
import { discordAdapter } from "./adapters/discord.js";
|
|
20
20
|
import { emailAdapter } from "./adapters/email.js";
|
|
@@ -30,11 +30,13 @@ import { claimIntegrationControl } from "./controls-store.js";
|
|
|
30
30
|
import { startGoogleDocsPoller, handlePushNotification, } from "./google-docs-poller.js";
|
|
31
31
|
import { IntegrationIdentityDeclinedError, resolveDefaultIntegrationExecutionContext, } from "./identity.js";
|
|
32
32
|
import { disconnectIntegrationInstallation, listIntegrationInstallations, resolveIntegrationTokenBundle, updateIntegrationInstallation, upsertIntegrationInstallation, } from "./installations-store.js";
|
|
33
|
-
import {
|
|
33
|
+
import { recoverDueIntegrationCampaigns } from "./integration-campaign-recovery.js";
|
|
34
|
+
import { claimIntegrationCampaignDeliveryForTask, completeIntegrationCampaignTaskAfterA2A, completeIntegrationCampaignTask, failIntegrationCampaignTaskDeliveryContainment, failDisabledIntegrationCampaignTask, failIntegrationCampaign, refreshIntegrationCampaignTaskA2AReceiptRetry, terminalizeIntegrationCampaignForTask, transitionIntegrationCampaignTaskToA2AReceiptRetry, transitionIntegrationCampaignTaskToDeliveryRetry, waitForA2AIntegrationCampaign, } from "./integration-campaigns-store.js";
|
|
35
|
+
import { dispatchPendingIntegrationTask, INTEGRATION_CAMPAIGN_PROCESSOR_FIELD, INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT, integrationDispatchScopeValue, isIntegrationDurableDispatchConfigured, isIntegrationDurableDispatchEnabledForTask, } from "./integration-durable-dispatch.js";
|
|
34
36
|
import { forgetIntegrationMemory, integrationMemoryActions, listIntegrationMemory, rememberForIntegrationScope, } from "./integration-memory.js";
|
|
35
37
|
import { extractBearerToken, verifyInternalToken } from "./internal-token.js";
|
|
36
38
|
import { retryStuckPendingTasks, startPendingTasksRetryJob, } from "./pending-tasks-retry-job.js";
|
|
37
|
-
import { claimPendingTask,
|
|
39
|
+
import { claimPendingTask, getNextPendingTaskForThread, getPendingTask, insertPendingTask, isDuplicateEventError, MAX_PENDING_TASK_ATTEMPTS, markTaskCompleted, markTaskDeliveryRetryable, markTaskFailed, markTaskRetryable, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
|
|
38
40
|
import { claimNextComputerCommand, claimNextRemoteCommand, enqueueComputerCommand, enqueueRemoteCommand as enqueueRemoteCommandRow, isRemoteCommandKind, listRemoteCommandsForOwner, updateRemoteCommandResult, } from "./remote-commands-store.js";
|
|
39
41
|
import { authenticateRemoteDeviceToken, createRemoteDevice, getRemoteComputerCapabilities, getRemoteDeviceForOwner, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, toPublicRemoteDevice, unregisterRemoteDevice, updateRemoteDeviceDetails, } from "./remote-devices-store.js";
|
|
40
42
|
import { startRemotePushDeliveryJob } from "./remote-push-delivery-job.js";
|
|
@@ -48,6 +50,110 @@ import { getTaskQueueStats } from "./task-queue-stats.js";
|
|
|
48
50
|
import { listIntegrationUsageBudgets, saveIntegrationUsageBudget, } from "./usage-budget-store.js";
|
|
49
51
|
import { handleWebhook, processIntegrationTask, recordIntegrationResponseDelivery, } from "./webhook-handler.js";
|
|
50
52
|
let a2aContinuationRetryInterval = null;
|
|
53
|
+
const INTEGRATION_DELIVERY_LEASE_MS = 2 * 60_000;
|
|
54
|
+
async function checkpointIntegrationDeliveryRetry(task, payload, errorMessage, event, campaignLease) {
|
|
55
|
+
let terminalStatus;
|
|
56
|
+
let confirmedReceipt = false;
|
|
57
|
+
let awaitingA2ACompletion = false;
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(payload);
|
|
60
|
+
terminalStatus = parsed.campaignTerminalStatus;
|
|
61
|
+
confirmedReceipt = parsed.deliveryReceipt?.status === "delivered";
|
|
62
|
+
awaitingA2ACompletion = parsed.awaitingA2ACompletion === true;
|
|
63
|
+
}
|
|
64
|
+
catch { }
|
|
65
|
+
if (awaitingA2ACompletion) {
|
|
66
|
+
if (campaignLease && campaignLease.campaignStatus !== "waiting-a2a") {
|
|
67
|
+
throw new Error("A2A receipt retry lease has the wrong custody mode");
|
|
68
|
+
}
|
|
69
|
+
const transitioned = campaignLease
|
|
70
|
+
? await transitionIntegrationCampaignTaskToA2AReceiptRetry(task.id, {
|
|
71
|
+
payload,
|
|
72
|
+
errorMessage,
|
|
73
|
+
campaignId: campaignLease.campaignId,
|
|
74
|
+
runId: campaignLease.runId,
|
|
75
|
+
leaseToken: campaignLease.leaseToken,
|
|
76
|
+
nextRunAt: Date.now() + 15_000,
|
|
77
|
+
})
|
|
78
|
+
: await refreshIntegrationCampaignTaskA2AReceiptRetry(task.id, {
|
|
79
|
+
payload,
|
|
80
|
+
errorMessage,
|
|
81
|
+
});
|
|
82
|
+
if (!transitioned)
|
|
83
|
+
return "superseded";
|
|
84
|
+
await dispatchPendingIntegrationTask({
|
|
85
|
+
taskId: task.id,
|
|
86
|
+
task: {
|
|
87
|
+
platform: task.platform,
|
|
88
|
+
externalThreadId: task.externalThreadId,
|
|
89
|
+
platformContext: task.dispatchScope
|
|
90
|
+
? { channelId: task.dispatchScope }
|
|
91
|
+
: undefined,
|
|
92
|
+
},
|
|
93
|
+
event,
|
|
94
|
+
baseUrl: getBaseUrl(event),
|
|
95
|
+
campaignContinuation: true,
|
|
96
|
+
allowPortableConfirmedReceiptReconciliation: confirmedReceipt,
|
|
97
|
+
});
|
|
98
|
+
return "requeued";
|
|
99
|
+
}
|
|
100
|
+
if (terminalStatus && !campaignLease && !confirmedReceipt) {
|
|
101
|
+
throw new Error("Campaign delivery retry is missing its lease");
|
|
102
|
+
}
|
|
103
|
+
if (terminalStatus && campaignLease) {
|
|
104
|
+
if (terminalStatus !== campaignLease.campaignStatus) {
|
|
105
|
+
throw new Error("Campaign delivery retry status does not match its lease");
|
|
106
|
+
}
|
|
107
|
+
const transitioned = await transitionIntegrationCampaignTaskToDeliveryRetry(task.id, {
|
|
108
|
+
payload,
|
|
109
|
+
errorMessage,
|
|
110
|
+
campaignStatus: campaignLease.campaignStatus,
|
|
111
|
+
campaignId: campaignLease.campaignId,
|
|
112
|
+
runId: campaignLease.runId,
|
|
113
|
+
leaseToken: campaignLease.leaseToken,
|
|
114
|
+
});
|
|
115
|
+
if (!transitioned) {
|
|
116
|
+
return "superseded";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
await markTaskDeliveryRetryable(task.id, payload, errorMessage);
|
|
121
|
+
}
|
|
122
|
+
await dispatchPendingIntegrationTask({
|
|
123
|
+
taskId: task.id,
|
|
124
|
+
task: {
|
|
125
|
+
platform: task.platform,
|
|
126
|
+
externalThreadId: task.externalThreadId,
|
|
127
|
+
platformContext: task.dispatchScope
|
|
128
|
+
? { channelId: task.dispatchScope }
|
|
129
|
+
: undefined,
|
|
130
|
+
},
|
|
131
|
+
event,
|
|
132
|
+
baseUrl: getBaseUrl(event),
|
|
133
|
+
});
|
|
134
|
+
return "requeued";
|
|
135
|
+
}
|
|
136
|
+
async function containFailedDeliveryTransition(task, errorMessage, event) {
|
|
137
|
+
const contained = await failIntegrationCampaignTaskDeliveryContainment(task.id, errorMessage);
|
|
138
|
+
if (!contained) {
|
|
139
|
+
throw new Error("Delivery containment lost pending-task custody");
|
|
140
|
+
}
|
|
141
|
+
const nextTask = await getNextPendingTaskForThread(task.platform, task.externalThreadId);
|
|
142
|
+
if (!nextTask)
|
|
143
|
+
return;
|
|
144
|
+
await dispatchPendingIntegrationTask({
|
|
145
|
+
taskId: nextTask.id,
|
|
146
|
+
task: {
|
|
147
|
+
platform: task.platform,
|
|
148
|
+
externalThreadId: task.externalThreadId,
|
|
149
|
+
platformContext: nextTask.dispatchScope
|
|
150
|
+
? { channelId: nextTask.dispatchScope }
|
|
151
|
+
: undefined,
|
|
152
|
+
},
|
|
153
|
+
event,
|
|
154
|
+
baseUrl: getBaseUrl(event),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
51
157
|
function startA2AContinuationRetryJob(adapters) {
|
|
52
158
|
if (a2aContinuationRetryInterval)
|
|
53
159
|
return;
|
|
@@ -1262,12 +1368,38 @@ export function createIntegrationsPlugin(options) {
|
|
|
1262
1368
|
if (!isIntegrationDurableDispatchConfigured()) {
|
|
1263
1369
|
return { ok: true, disabled: true };
|
|
1264
1370
|
}
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1371
|
+
const webhookBaseUrl = getBaseUrl(event);
|
|
1372
|
+
const [pendingTasks, campaigns, a2aContinuations] = await Promise.all([
|
|
1373
|
+
retryStuckPendingTasks({
|
|
1374
|
+
webhookBaseUrl,
|
|
1375
|
+
limit: 20,
|
|
1376
|
+
durableOnly: true,
|
|
1377
|
+
}).catch((error) => {
|
|
1378
|
+
console.error("[integrations] Pending-task recovery failed:", error);
|
|
1379
|
+
return { error: "pending-task-recovery-failed" };
|
|
1380
|
+
}),
|
|
1381
|
+
recoverDueIntegrationCampaigns({
|
|
1382
|
+
event,
|
|
1383
|
+
webhookBaseUrl,
|
|
1384
|
+
limit: 20,
|
|
1385
|
+
}).catch((error) => {
|
|
1386
|
+
console.error("[integrations] Campaign recovery failed:", error);
|
|
1387
|
+
return { error: "campaign-recovery-failed" };
|
|
1388
|
+
}),
|
|
1389
|
+
recoverDueA2AContinuations({
|
|
1390
|
+
webhookBaseUrl,
|
|
1391
|
+
limit: 10,
|
|
1392
|
+
}).catch((error) => {
|
|
1393
|
+
console.error("[integrations] A2A recovery failed:", error);
|
|
1394
|
+
return { error: "a2a-recovery-failed" };
|
|
1395
|
+
}),
|
|
1396
|
+
]);
|
|
1397
|
+
return {
|
|
1398
|
+
ok: true,
|
|
1399
|
+
pendingTasks,
|
|
1400
|
+
campaigns,
|
|
1401
|
+
a2aContinuations,
|
|
1402
|
+
};
|
|
1271
1403
|
}));
|
|
1272
1404
|
// ─── Process pending task (cross-platform task queue) ────────
|
|
1273
1405
|
// POST /_agent-native/integrations/process-task
|
|
@@ -1317,14 +1449,62 @@ export function createIntegrationsPlugin(options) {
|
|
|
1317
1449
|
AGENT_BACKGROUND_PROCESSOR_INTEGRATION
|
|
1318
1450
|
? "background-acknowledged"
|
|
1319
1451
|
: "portable-unconfirmed";
|
|
1320
|
-
const
|
|
1452
|
+
const campaignContinuation = body[INTEGRATION_CAMPAIGN_PROCESSOR_FIELD] === true;
|
|
1453
|
+
const task = campaignContinuation
|
|
1454
|
+
? await getPendingTask(taskId)
|
|
1455
|
+
: await claimPendingTask(taskId, { dispatchOutcome });
|
|
1321
1456
|
if (!task) {
|
|
1322
1457
|
setResponseStatus(event, 200);
|
|
1323
1458
|
return { ok: true, skipped: "already-claimed-or-missing" };
|
|
1324
1459
|
}
|
|
1460
|
+
if (campaignContinuation && task.status !== "processing") {
|
|
1461
|
+
setResponseStatus(event, 200);
|
|
1462
|
+
return { ok: true, skipped: "campaign-task-not-processing" };
|
|
1463
|
+
}
|
|
1464
|
+
let taskPayload;
|
|
1465
|
+
try {
|
|
1466
|
+
taskPayload = JSON.parse(task.payload);
|
|
1467
|
+
}
|
|
1468
|
+
catch {
|
|
1469
|
+
await markTaskFailed(task.id, "Invalid integration task payload");
|
|
1470
|
+
setResponseStatus(event, 400);
|
|
1471
|
+
return { error: "Invalid integration task payload" };
|
|
1472
|
+
}
|
|
1473
|
+
const durableCampaignEnabled = isIntegrationDurableDispatchEnabledForTask({
|
|
1474
|
+
platform: task.platform,
|
|
1475
|
+
externalThreadId: task.externalThreadId,
|
|
1476
|
+
platformContext: task.dispatchScope
|
|
1477
|
+
? { channelId: task.dispatchScope }
|
|
1478
|
+
: undefined,
|
|
1479
|
+
});
|
|
1480
|
+
const confirmedDeliveryReceipt = taskPayload.kind === "response-delivery" &&
|
|
1481
|
+
taskPayload.deliveryReceipt?.status === "delivered";
|
|
1482
|
+
if (campaignContinuation &&
|
|
1483
|
+
!durableCampaignEnabled &&
|
|
1484
|
+
!confirmedDeliveryReceipt) {
|
|
1485
|
+
await failDisabledIntegrationCampaignTask(task.id);
|
|
1486
|
+
const nextTask = await getNextPendingTaskForThread(task.platform, task.externalThreadId);
|
|
1487
|
+
if (nextTask) {
|
|
1488
|
+
await dispatchPendingIntegrationTask({
|
|
1489
|
+
taskId: nextTask.id,
|
|
1490
|
+
task: {
|
|
1491
|
+
platform: task.platform,
|
|
1492
|
+
externalThreadId: task.externalThreadId,
|
|
1493
|
+
platformContext: nextTask.dispatchScope
|
|
1494
|
+
? { channelId: nextTask.dispatchScope }
|
|
1495
|
+
: undefined,
|
|
1496
|
+
},
|
|
1497
|
+
event,
|
|
1498
|
+
baseUrl: getBaseUrl(event),
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
setResponseStatus(event, 200);
|
|
1502
|
+
return { ok: true, failed: "campaign-disabled" };
|
|
1503
|
+
}
|
|
1325
1504
|
let deliveryRetryTransitionStarted = false;
|
|
1326
1505
|
let deliveryRetryRecovery;
|
|
1327
1506
|
let confirmedDeliveryRetryPayload;
|
|
1507
|
+
let campaignDeliveryLease;
|
|
1328
1508
|
try {
|
|
1329
1509
|
const adapter = adapterMap.get(task.platform);
|
|
1330
1510
|
if (!adapter) {
|
|
@@ -1337,8 +1517,8 @@ export function createIntegrationsPlugin(options) {
|
|
|
1337
1517
|
...(task.orgId ? { orgId: task.orgId } : {}),
|
|
1338
1518
|
isIntegrationCaller: true,
|
|
1339
1519
|
}, async () => {
|
|
1340
|
-
|
|
1341
|
-
|
|
1520
|
+
if (!campaignContinuation &&
|
|
1521
|
+
taskPayload.kind === "system-notice") {
|
|
1342
1522
|
if (!adapter.sendSystemNotice) {
|
|
1343
1523
|
throw new Error(`Platform ${task.platform} cannot deliver system notices`);
|
|
1344
1524
|
}
|
|
@@ -1356,6 +1536,30 @@ export function createIntegrationsPlugin(options) {
|
|
|
1356
1536
|
}
|
|
1357
1537
|
if (taskPayload.kind === "response-delivery") {
|
|
1358
1538
|
let receipt = taskPayload.deliveryReceipt;
|
|
1539
|
+
let deliveryLease;
|
|
1540
|
+
if (campaignContinuation && !receipt) {
|
|
1541
|
+
const runId = `integration-delivery-${crypto.randomUUID()}`;
|
|
1542
|
+
const leaseToken = crypto.randomUUID();
|
|
1543
|
+
const deliveryClaim = await claimIntegrationCampaignDeliveryForTask(task.id, {
|
|
1544
|
+
runId,
|
|
1545
|
+
leaseToken,
|
|
1546
|
+
leaseDurationMs: INTEGRATION_DELIVERY_LEASE_MS,
|
|
1547
|
+
});
|
|
1548
|
+
if (!deliveryClaim)
|
|
1549
|
+
return "campaign-active";
|
|
1550
|
+
deliveryLease = {
|
|
1551
|
+
campaignId: deliveryClaim.id,
|
|
1552
|
+
runId,
|
|
1553
|
+
leaseToken,
|
|
1554
|
+
};
|
|
1555
|
+
campaignDeliveryLease = {
|
|
1556
|
+
...deliveryLease,
|
|
1557
|
+
campaignStatus: taskPayload.campaignTerminalStatus ??
|
|
1558
|
+
(taskPayload.awaitingA2ACompletion
|
|
1559
|
+
? "waiting-a2a"
|
|
1560
|
+
: "completed"),
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1359
1563
|
if (!receipt) {
|
|
1360
1564
|
receipt = await adapter.sendResponse(taskPayload.message, taskPayload.incoming, {
|
|
1361
1565
|
...(taskPayload.placeholderRef
|
|
@@ -1384,6 +1588,55 @@ export function createIntegrationsPlugin(options) {
|
|
|
1384
1588
|
deliveryRetryRecovery = undefined;
|
|
1385
1589
|
}
|
|
1386
1590
|
await recordIntegrationResponseDelivery(deliveredPayload, receipt);
|
|
1591
|
+
const campaignTerminalStatus = taskPayload.campaignTerminalStatus;
|
|
1592
|
+
if (campaignTerminalStatus) {
|
|
1593
|
+
const errorMessage = "Integration campaign exhausted its continuation limit";
|
|
1594
|
+
const terminalized = deliveryLease
|
|
1595
|
+
? campaignTerminalStatus === "failed"
|
|
1596
|
+
? await failIntegrationCampaign(deliveryLease.campaignId, {
|
|
1597
|
+
runId: deliveryLease.runId,
|
|
1598
|
+
leaseToken: deliveryLease.leaseToken,
|
|
1599
|
+
errorMessage,
|
|
1600
|
+
})
|
|
1601
|
+
: await completeIntegrationCampaignTask(deliveryLease.campaignId, {
|
|
1602
|
+
integrationTaskId: task.id,
|
|
1603
|
+
runId: deliveryLease.runId,
|
|
1604
|
+
leaseToken: deliveryLease.leaseToken,
|
|
1605
|
+
})
|
|
1606
|
+
: await terminalizeIntegrationCampaignForTask(task.id, {
|
|
1607
|
+
status: campaignTerminalStatus,
|
|
1608
|
+
...(campaignTerminalStatus === "failed"
|
|
1609
|
+
? { errorMessage }
|
|
1610
|
+
: {}),
|
|
1611
|
+
});
|
|
1612
|
+
if (deliveryLease && !terminalized) {
|
|
1613
|
+
return "campaign-active";
|
|
1614
|
+
}
|
|
1615
|
+
return campaignTerminalStatus === "failed"
|
|
1616
|
+
? "campaign-failed"
|
|
1617
|
+
: "completed";
|
|
1618
|
+
}
|
|
1619
|
+
if (taskPayload.awaitingA2ACompletion) {
|
|
1620
|
+
if (deliveryLease) {
|
|
1621
|
+
const waiting = await waitForA2AIntegrationCampaign(deliveryLease.campaignId, {
|
|
1622
|
+
runId: deliveryLease.runId,
|
|
1623
|
+
leaseToken: deliveryLease.leaseToken,
|
|
1624
|
+
nextRunAt: Date.now() + 15_000,
|
|
1625
|
+
});
|
|
1626
|
+
if (!waiting)
|
|
1627
|
+
return "campaign-active";
|
|
1628
|
+
}
|
|
1629
|
+
if (!(await hasActiveA2AContinuationsForIntegrationTask(task.id))) {
|
|
1630
|
+
const completed = await completeIntegrationCampaignTaskAfterA2A(task.id);
|
|
1631
|
+
return completed
|
|
1632
|
+
? "completed"
|
|
1633
|
+
: "campaign-active";
|
|
1634
|
+
}
|
|
1635
|
+
return "campaign-active";
|
|
1636
|
+
}
|
|
1637
|
+
if (campaignContinuation) {
|
|
1638
|
+
return "campaign-active";
|
|
1639
|
+
}
|
|
1387
1640
|
return;
|
|
1388
1641
|
}
|
|
1389
1642
|
const resources = await loadResourcesForPrompt(task.ownerEmail, true, options?.appId, task.orgId);
|
|
@@ -1397,18 +1650,55 @@ export function createIntegrationsPlugin(options) {
|
|
|
1397
1650
|
engine: options?.engine,
|
|
1398
1651
|
ownerEmail: task.ownerEmail,
|
|
1399
1652
|
appId: options?.appId,
|
|
1653
|
+
}, {
|
|
1654
|
+
enabled: durableCampaignEnabled,
|
|
1655
|
+
continuationInvocation: campaignContinuation,
|
|
1400
1656
|
});
|
|
1401
1657
|
if (result?.status === "delivery-pending") {
|
|
1402
1658
|
deliveryRetryTransitionStarted = true;
|
|
1403
|
-
await
|
|
1659
|
+
const checkpoint = await checkpointIntegrationDeliveryRetry(task, JSON.stringify(result.payload), result.errorMessage, event, result.campaignLease);
|
|
1660
|
+
if (checkpoint === "superseded") {
|
|
1661
|
+
return "campaign-active";
|
|
1662
|
+
}
|
|
1404
1663
|
return "delivery-retry";
|
|
1405
1664
|
}
|
|
1665
|
+
if (result?.status === "campaign-pending" ||
|
|
1666
|
+
result?.status === "campaign-active") {
|
|
1667
|
+
return "campaign-active";
|
|
1668
|
+
}
|
|
1669
|
+
if (result?.status === "campaign-failed") {
|
|
1670
|
+
return "campaign-failed";
|
|
1671
|
+
}
|
|
1406
1672
|
return "completed";
|
|
1407
1673
|
});
|
|
1408
1674
|
if (processingResult === "delivery-retry") {
|
|
1409
1675
|
setResponseStatus(event, 202);
|
|
1410
1676
|
return { ok: true, taskId, retrying: "response-delivery" };
|
|
1411
1677
|
}
|
|
1678
|
+
if (processingResult === "campaign-active") {
|
|
1679
|
+
setResponseStatus(event, 202);
|
|
1680
|
+
return { ok: true, taskId, continuing: true };
|
|
1681
|
+
}
|
|
1682
|
+
if (processingResult === "campaign-failed") {
|
|
1683
|
+
await markTaskFailed(taskId, "Integration campaign exhausted its continuation limit");
|
|
1684
|
+
const nextTask = await getNextPendingTaskForThread(task.platform, task.externalThreadId);
|
|
1685
|
+
if (nextTask) {
|
|
1686
|
+
await dispatchPendingIntegrationTask({
|
|
1687
|
+
taskId: nextTask.id,
|
|
1688
|
+
task: {
|
|
1689
|
+
platform: task.platform,
|
|
1690
|
+
externalThreadId: task.externalThreadId,
|
|
1691
|
+
platformContext: nextTask.dispatchScope
|
|
1692
|
+
? { channelId: nextTask.dispatchScope }
|
|
1693
|
+
: undefined,
|
|
1694
|
+
},
|
|
1695
|
+
event,
|
|
1696
|
+
baseUrl: getBaseUrl(event),
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
setResponseStatus(event, 200);
|
|
1700
|
+
return { ok: true, taskId, failed: "campaign-exhausted" };
|
|
1701
|
+
}
|
|
1412
1702
|
await markTaskCompleted(taskId);
|
|
1413
1703
|
const nextTask = await getNextPendingTaskForThread(task.platform, task.externalThreadId);
|
|
1414
1704
|
if (nextTask) {
|
|
@@ -1439,7 +1729,11 @@ export function createIntegrationsPlugin(options) {
|
|
|
1439
1729
|
: "processor failed";
|
|
1440
1730
|
if (deliveryRetryRecovery) {
|
|
1441
1731
|
try {
|
|
1442
|
-
await
|
|
1732
|
+
const checkpoint = await checkpointIntegrationDeliveryRetry(task, deliveryRetryRecovery.payload, `${deliveryRetryRecovery.errorMessage}: ${errorMessage}`, event, campaignDeliveryLease);
|
|
1733
|
+
if (checkpoint === "superseded") {
|
|
1734
|
+
setResponseStatus(event, 202);
|
|
1735
|
+
return { ok: true, taskId, continuing: true };
|
|
1736
|
+
}
|
|
1443
1737
|
setResponseStatus(event, 202);
|
|
1444
1738
|
return { ok: true, taskId, retrying: "response-delivery" };
|
|
1445
1739
|
}
|
|
@@ -1447,24 +1741,37 @@ export function createIntegrationsPlugin(options) {
|
|
|
1447
1741
|
const transitionMessage = transitionError instanceof Error
|
|
1448
1742
|
? transitionError.message
|
|
1449
1743
|
: String(transitionError);
|
|
1450
|
-
await
|
|
1744
|
+
await containFailedDeliveryTransition(task, `Could not safely checkpoint the delivery receipt: ${transitionMessage}`, event).catch((failureTransitionError) => {
|
|
1451
1745
|
console.error("[integrations] Failed to contain delivery receipt transition failure:", failureTransitionError);
|
|
1452
1746
|
});
|
|
1453
1747
|
}
|
|
1454
1748
|
}
|
|
1455
1749
|
else if (confirmedDeliveryRetryPayload) {
|
|
1456
1750
|
try {
|
|
1457
|
-
await
|
|
1751
|
+
const checkpoint = await checkpointIntegrationDeliveryRetry(task, confirmedDeliveryRetryPayload, `Provider delivery was confirmed but history persistence failed: ${errorMessage}`, event, campaignDeliveryLease);
|
|
1752
|
+
if (checkpoint === "superseded") {
|
|
1753
|
+
setResponseStatus(event, 202);
|
|
1754
|
+
return { ok: true, taskId, continuing: true };
|
|
1755
|
+
}
|
|
1458
1756
|
console.error("[integrations] process-task failure:", err);
|
|
1459
1757
|
setResponseStatus(event, 202);
|
|
1460
1758
|
return { ok: true, taskId, retrying: "response-delivery" };
|
|
1461
1759
|
}
|
|
1462
1760
|
catch (transitionError) {
|
|
1761
|
+
const transitionMessage = transitionError instanceof Error
|
|
1762
|
+
? transitionError.message
|
|
1763
|
+
: String(transitionError);
|
|
1463
1764
|
console.error("[integrations] Failed to requeue confirmed delivery history:", transitionError);
|
|
1765
|
+
await containFailedDeliveryTransition(task, `Could not safely checkpoint confirmed delivery history: ${transitionMessage}`, event).catch((failureTransitionError) => {
|
|
1766
|
+
console.error("[integrations] Failed to contain confirmed delivery history transition failure:", failureTransitionError);
|
|
1767
|
+
});
|
|
1768
|
+
console.error("[integrations] process-task failure:", err);
|
|
1769
|
+
setResponseStatus(event, 500);
|
|
1770
|
+
return { error: "Internal task failed" };
|
|
1464
1771
|
}
|
|
1465
1772
|
}
|
|
1466
1773
|
else if (deliveryRetryTransitionStarted) {
|
|
1467
|
-
await
|
|
1774
|
+
await containFailedDeliveryTransition(task, `Could not safely checkpoint the delivery retry: ${errorMessage}`, event).catch((transitionError) => {
|
|
1468
1775
|
console.error("[integrations] Failed to contain delivery retry transition failure:", transitionError);
|
|
1469
1776
|
});
|
|
1470
1777
|
}
|