@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
|
@@ -15,13 +15,25 @@ export type IntegrationResponseDeliveryTaskPayload = {
|
|
|
15
15
|
deliveryReceipt?: PlatformDeliveryReceipt;
|
|
16
16
|
deliveredAt?: string;
|
|
17
17
|
artifacts?: A2AArtifactIdentity[];
|
|
18
|
+
campaignTerminalStatus?: "completed" | "failed";
|
|
19
|
+
awaitingA2ACompletion?: true;
|
|
18
20
|
};
|
|
19
21
|
export type ProcessIntegrationTaskResult = {
|
|
20
22
|
status: "completed";
|
|
23
|
+
} | {
|
|
24
|
+
status: "campaign-pending" | "campaign-active";
|
|
25
|
+
} | {
|
|
26
|
+
status: "campaign-failed";
|
|
21
27
|
} | {
|
|
22
28
|
status: "delivery-pending";
|
|
23
29
|
payload: IntegrationResponseDeliveryTaskPayload;
|
|
24
30
|
errorMessage: string;
|
|
31
|
+
campaignLease?: {
|
|
32
|
+
campaignId: string;
|
|
33
|
+
runId: string;
|
|
34
|
+
leaseToken: string;
|
|
35
|
+
campaignStatus: "completed" | "failed" | "waiting-a2a";
|
|
36
|
+
};
|
|
25
37
|
};
|
|
26
38
|
export interface WebhookHandlerOptions {
|
|
27
39
|
adapter: PlatformAdapter;
|
|
@@ -105,6 +117,9 @@ export declare function resolveBaseUrl(event: H3Event): string;
|
|
|
105
117
|
* processor endpoint in `plugin.ts`. This is a fresh function execution, so
|
|
106
118
|
* it gets its own timeout budget independent of the inbound webhook handler.
|
|
107
119
|
*/
|
|
108
|
-
export declare function processIntegrationTask(task: PendingTask, options: WebhookHandlerOptions
|
|
120
|
+
export declare function processIntegrationTask(task: PendingTask, options: WebhookHandlerOptions, campaignOptions?: {
|
|
121
|
+
enabled?: boolean;
|
|
122
|
+
continuationInvocation?: boolean;
|
|
123
|
+
}): Promise<ProcessIntegrationTaskResult>;
|
|
109
124
|
export declare function recordIntegrationResponseDelivery(payload: IntegrationResponseDeliveryTaskPayload, receipt: PlatformDeliveryReceipt): Promise<void>;
|
|
110
125
|
//# 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;AAElC,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,6BAA6B,CAAC;AAerC,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAC3E,OAAO,EAOL,KAAK,WAAW,EACjB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../src/integrations/webhook-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import { getConfiguredEngineNameForRequest, getStoredModelForEngine, normalizeMo
|
|
|
6
6
|
import { resolveMainChatMaxOutputTokens } from "../agent/engine/output-tokens.js";
|
|
7
7
|
import { PROVIDER_TO_ENV } from "../agent/engine/provider-env-vars.js";
|
|
8
8
|
import { runAgentLoop, actionsToEngineTools, filterInitialEngineTools, getOwnerActiveApiKey, getOwnerApiKey, engineToProvider, } from "../agent/production-agent.js";
|
|
9
|
+
import { appendDurableContinuationContext } from "../agent/run-loop-with-resume.js";
|
|
9
10
|
import { startRun } from "../agent/run-manager.js";
|
|
10
11
|
import { buildCurrentTimeUserContext, buildRuntimeContextPrompt, } from "../agent/runtime-context.js";
|
|
11
12
|
import { buildAssistantMessage, extractThreadMeta, threadDataToEngineMessages, } from "../agent/thread-data-builder.js";
|
|
@@ -19,7 +20,9 @@ import { canUseDeployCredentialFallbackForRequest, readDeployCredentialEnv, } fr
|
|
|
19
20
|
import { runWithRequestContext } from "../server/request-context.js";
|
|
20
21
|
import { normalizeReasoningEffortForRequest } from "../shared/reasoning-effort.js";
|
|
21
22
|
import { A2A_CONTINUATION_QUEUED_MARKER } from "./a2a-continuation-marker.js";
|
|
23
|
+
import { hasActiveA2AContinuationsForIntegrationTask } from "./a2a-continuations-store.js";
|
|
22
24
|
import { clearIntegrationAwaitingInput, setIntegrationAwaitingInput, } from "./awaiting-input-store.js";
|
|
25
|
+
import { claimIntegrationCampaignDeliveryForTask, claimIntegrationCampaign, completeIntegrationCampaignTask, createIntegrationCampaign, failIntegrationCampaign, heartbeatIntegrationCampaign, scheduleNextIntegrationCampaign, waitForA2AIntegrationCampaign, } from "./integration-campaigns-store.js";
|
|
23
26
|
import { dispatchPendingIntegrationTask, integrationDispatchScopeValue, } from "./integration-durable-dispatch.js";
|
|
24
27
|
import { loadIntegrationMemoryPrompt } from "./integration-memory.js";
|
|
25
28
|
import { insertPendingTask, isDuplicateEventError, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
|
|
@@ -32,6 +35,12 @@ const DEFERRED_RESPONSE_MAX_HANDLER_MS = 2_500;
|
|
|
32
35
|
const EMPTY_INTEGRATION_RESPONSE_MESSAGE = "The model finished without a visible answer. Try again, or open the thread in Dispatch to inspect the run.";
|
|
33
36
|
const CUTOFF_INTEGRATION_RESPONSE_MESSAGE = "I ran out of time on this one before I could write up an answer — it needed more research than a single run allows. " +
|
|
34
37
|
"Open the thread in Dispatch to see what I gathered, or ask me again in smaller pieces (one source at a time works best).";
|
|
38
|
+
const INTEGRATION_CAMPAIGN_LEASE_MS = 16 * 60_000;
|
|
39
|
+
const INTEGRATION_CAMPAIGN_MAX_CHUNKS = 4;
|
|
40
|
+
const INTEGRATION_CAMPAIGN_A2A_CHECK_MS = 30_000;
|
|
41
|
+
// Keep a lost handoff plus the one-minute sweep inside the two-minute messaging
|
|
42
|
+
// target without shortening general background-run budgets.
|
|
43
|
+
const INTEGRATION_CAMPAIGN_NO_PROGRESS_TIMEOUT_MS = 45_000;
|
|
35
44
|
/**
|
|
36
45
|
* True when the run stopped at a continuation boundary rather than finishing:
|
|
37
46
|
* the run-manager's soft timeout emits `auto_continue` and aborts the loop, and
|
|
@@ -42,6 +51,46 @@ const CUTOFF_INTEGRATION_RESPONSE_MESSAGE = "I ran out of time on this one befor
|
|
|
42
51
|
function endedAtContinuationBoundary(run) {
|
|
43
52
|
return run.events.some((runEvent) => runEvent.event.type === "auto_continue");
|
|
44
53
|
}
|
|
54
|
+
function continuationBoundaryReason(run) {
|
|
55
|
+
for (let index = run.events.length - 1; index >= 0; index -= 1) {
|
|
56
|
+
const runEvent = run.events[index];
|
|
57
|
+
if (!runEvent)
|
|
58
|
+
continue;
|
|
59
|
+
if (runEvent.event.type === "auto_continue")
|
|
60
|
+
return runEvent.event.reason;
|
|
61
|
+
}
|
|
62
|
+
return "run_timeout";
|
|
63
|
+
}
|
|
64
|
+
function checkpointContinuationReason(checkpoint) {
|
|
65
|
+
if (!checkpoint)
|
|
66
|
+
return "run_timeout";
|
|
67
|
+
try {
|
|
68
|
+
const reason = JSON.parse(checkpoint).reason;
|
|
69
|
+
if (reason === "run_timeout" ||
|
|
70
|
+
reason === "loop_limit" ||
|
|
71
|
+
reason === "max_tokens" ||
|
|
72
|
+
reason === "no_progress" ||
|
|
73
|
+
reason === "stream_ended" ||
|
|
74
|
+
reason === "gateway_timeout" ||
|
|
75
|
+
reason === "network_interrupted") {
|
|
76
|
+
return reason;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
80
|
+
return "run_timeout";
|
|
81
|
+
}
|
|
82
|
+
function parseIntegrationProgressRef(value) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(value);
|
|
85
|
+
return typeof parsed.kind === "string" &&
|
|
86
|
+
typeof parsed.streamTs === "string"
|
|
87
|
+
? { kind: parsed.kind, streamTs: parsed.streamTs }
|
|
88
|
+
: null;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
45
94
|
/**
|
|
46
95
|
* Build a stable per-event dedup key from the incoming message. The same
|
|
47
96
|
* key is computed for every retry of the same event from the platform —
|
|
@@ -354,15 +403,18 @@ export function resolveBaseUrl(event) {
|
|
|
354
403
|
* processor endpoint in `plugin.ts`. This is a fresh function execution, so
|
|
355
404
|
* it gets its own timeout budget independent of the inbound webhook handler.
|
|
356
405
|
*/
|
|
357
|
-
export async function processIntegrationTask(task, options) {
|
|
406
|
+
export async function processIntegrationTask(task, options, campaignOptions) {
|
|
358
407
|
const parsed = JSON.parse(task.payload);
|
|
359
|
-
|
|
408
|
+
if (!campaignOptions?.continuationInvocation) {
|
|
409
|
+
await recordInboundIntegrationAudit(task, parsed.incoming);
|
|
410
|
+
}
|
|
360
411
|
return processIncomingMessage(parsed.incoming, options, {
|
|
361
412
|
taskId: task.id,
|
|
362
413
|
attempts: task.attempts,
|
|
363
414
|
placeholderRef: parsed.placeholderRef,
|
|
364
415
|
orgId: task.orgId ?? undefined,
|
|
365
416
|
principalType: parsed.principalType ?? options.principalType ?? "user",
|
|
417
|
+
durableCampaign: campaignOptions?.enabled === true,
|
|
366
418
|
});
|
|
367
419
|
}
|
|
368
420
|
async function recordInboundIntegrationAudit(task, incoming) {
|
|
@@ -528,6 +580,152 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
528
580
|
console.warn(`[integrations] Could not grant ${incoming.platform} sender access to thread ${threadId}:`, error instanceof Error ? error.message : error);
|
|
529
581
|
});
|
|
530
582
|
}
|
|
583
|
+
let campaign;
|
|
584
|
+
if (opts.durableCampaign && opts.taskId) {
|
|
585
|
+
const created = await createIntegrationCampaign({
|
|
586
|
+
integrationTaskId: opts.taskId,
|
|
587
|
+
threadId,
|
|
588
|
+
turnId: `integration-turn-${opts.taskId}`,
|
|
589
|
+
});
|
|
590
|
+
if (created.status === "completed") {
|
|
591
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
592
|
+
return { status: "completed" };
|
|
593
|
+
}
|
|
594
|
+
if (created.status === "failed") {
|
|
595
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
596
|
+
return { status: "campaign-failed" };
|
|
597
|
+
}
|
|
598
|
+
const runId = `integration-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
599
|
+
const leaseToken = crypto.randomUUID();
|
|
600
|
+
const claimed = await claimIntegrationCampaign(created.id, {
|
|
601
|
+
runId,
|
|
602
|
+
leaseToken,
|
|
603
|
+
leaseDurationMs: INTEGRATION_CAMPAIGN_LEASE_MS,
|
|
604
|
+
maxChunks: INTEGRATION_CAMPAIGN_MAX_CHUNKS,
|
|
605
|
+
});
|
|
606
|
+
if (claimed.kind === "chunk-limit") {
|
|
607
|
+
const deliveryRunId = `integration-delivery-${crypto.randomUUID()}`;
|
|
608
|
+
const deliveryLeaseToken = crypto.randomUUID();
|
|
609
|
+
const deliveryCampaign = await claimIntegrationCampaignDeliveryForTask(opts.taskId, {
|
|
610
|
+
runId: deliveryRunId,
|
|
611
|
+
leaseToken: deliveryLeaseToken,
|
|
612
|
+
leaseDurationMs: INTEGRATION_CAMPAIGN_LEASE_MS,
|
|
613
|
+
});
|
|
614
|
+
if (!deliveryCampaign) {
|
|
615
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
616
|
+
return { status: "campaign-active" };
|
|
617
|
+
}
|
|
618
|
+
const exhaustedProgressRef = deliveryCampaign.progressRef
|
|
619
|
+
? parseIntegrationProgressRef(deliveryCampaign.progressRef)
|
|
620
|
+
: null;
|
|
621
|
+
const exhaustedProgress = exhaustedProgressRef
|
|
622
|
+
? await adapter
|
|
623
|
+
.resumeRunProgress?.(incoming, exhaustedProgressRef)
|
|
624
|
+
.catch(() => null)
|
|
625
|
+
: null;
|
|
626
|
+
const exhaustedMessage = adapter.formatAgentResponse("I couldn't safely finish this request after several continuation attempts. No completed write will be replayed.");
|
|
627
|
+
let deliveryPayload = {
|
|
628
|
+
kind: "response-delivery",
|
|
629
|
+
incoming,
|
|
630
|
+
message: exhaustedMessage,
|
|
631
|
+
...(opts.placeholderRef ? { placeholderRef: opts.placeholderRef } : {}),
|
|
632
|
+
internalThreadId: threadId,
|
|
633
|
+
campaignTerminalStatus: "failed",
|
|
634
|
+
...buildDeliveryHistoryMessageIds(incoming),
|
|
635
|
+
};
|
|
636
|
+
try {
|
|
637
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(deliveryPayload));
|
|
638
|
+
let receipt;
|
|
639
|
+
if (exhaustedProgress) {
|
|
640
|
+
try {
|
|
641
|
+
receipt = await exhaustedProgress.complete(exhaustedMessage);
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
receipt = await adapter.sendResponse(exhaustedMessage, incoming, {
|
|
645
|
+
placeholderRef: opts.placeholderRef,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
receipt = await adapter.sendResponse(exhaustedMessage, incoming, {
|
|
651
|
+
placeholderRef: opts.placeholderRef,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
if (receipt?.status !== "delivered") {
|
|
655
|
+
throw new Error(`${incoming.platform} exhaustion response completed without delivery proof`);
|
|
656
|
+
}
|
|
657
|
+
deliveryPayload = {
|
|
658
|
+
...deliveryPayload,
|
|
659
|
+
deliveryReceipt: receipt,
|
|
660
|
+
deliveredAt: new Date().toISOString(),
|
|
661
|
+
};
|
|
662
|
+
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(deliveryPayload));
|
|
663
|
+
await recordIntegrationResponseDelivery(deliveryPayload, receipt);
|
|
664
|
+
const terminalized = await failIntegrationCampaign(deliveryCampaign.id, {
|
|
665
|
+
runId: deliveryRunId,
|
|
666
|
+
leaseToken: deliveryLeaseToken,
|
|
667
|
+
errorMessage: "Integration campaign exhausted its chunk limit",
|
|
668
|
+
});
|
|
669
|
+
if (!terminalized) {
|
|
670
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
671
|
+
return { status: "campaign-active" };
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
676
|
+
return {
|
|
677
|
+
status: "delivery-pending",
|
|
678
|
+
payload: deliveryPayload,
|
|
679
|
+
campaignLease: {
|
|
680
|
+
campaignId: deliveryCampaign.id,
|
|
681
|
+
runId: deliveryRunId,
|
|
682
|
+
leaseToken: deliveryLeaseToken,
|
|
683
|
+
campaignStatus: "failed",
|
|
684
|
+
},
|
|
685
|
+
errorMessage: error instanceof Error
|
|
686
|
+
? error.message.slice(0, 1000)
|
|
687
|
+
: "Integration exhaustion response delivery failed",
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
691
|
+
return { status: "campaign-failed" };
|
|
692
|
+
}
|
|
693
|
+
if (claimed.kind !== "claimed") {
|
|
694
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
695
|
+
return { status: "campaign-active" };
|
|
696
|
+
}
|
|
697
|
+
campaign = { row: claimed.campaign, runId, leaseToken };
|
|
698
|
+
if (campaign.row.checkpoint) {
|
|
699
|
+
let waitingForA2A = false;
|
|
700
|
+
try {
|
|
701
|
+
waitingForA2A =
|
|
702
|
+
JSON.parse(campaign.row.checkpoint).waitingForA2A === true;
|
|
703
|
+
}
|
|
704
|
+
catch { }
|
|
705
|
+
if (waitingForA2A) {
|
|
706
|
+
const activeA2A = await hasActiveA2AContinuationsForIntegrationTask(opts.taskId);
|
|
707
|
+
if (activeA2A) {
|
|
708
|
+
const waiting = await waitForA2AIntegrationCampaign(campaign.row.id, {
|
|
709
|
+
runId: campaign.runId,
|
|
710
|
+
leaseToken: campaign.leaseToken,
|
|
711
|
+
nextRunAt: Date.now() + INTEGRATION_CAMPAIGN_A2A_CHECK_MS,
|
|
712
|
+
progressRef: campaign.row.progressRef,
|
|
713
|
+
});
|
|
714
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
715
|
+
return {
|
|
716
|
+
status: waiting ? "campaign-pending" : "campaign-active",
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const completed = await completeIntegrationCampaignTask(campaign.row.id, {
|
|
720
|
+
integrationTaskId: opts.taskId,
|
|
721
|
+
runId: campaign.runId,
|
|
722
|
+
leaseToken: campaign.leaseToken,
|
|
723
|
+
});
|
|
724
|
+
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
725
|
+
return { status: completed ? "completed" : "campaign-active" };
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
531
729
|
const existingMessages = [];
|
|
532
730
|
if (thread?.threadData) {
|
|
533
731
|
existingMessages.push(...threadDataToEngineMessages(thread.threadData));
|
|
@@ -555,15 +753,18 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
555
753
|
// Precise current time rides the engine-facing user message (not the cached
|
|
556
754
|
// system-prompt prefix, and not the persisted thread text) — the runtime
|
|
557
755
|
// context appended to the system prompt is day-granular only.
|
|
558
|
-
const messages = [
|
|
559
|
-
|
|
560
|
-
|
|
756
|
+
const messages = [...existingMessages];
|
|
757
|
+
if (campaign && campaign.row.chunkCount > 1) {
|
|
758
|
+
await appendDurableContinuationContext(messages, checkpointContinuationReason(campaign.row.checkpoint), threadId);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
messages.push({
|
|
561
762
|
role: "user",
|
|
562
763
|
content: [
|
|
563
764
|
{ type: "text", text: userText + buildCurrentTimeUserContext() },
|
|
564
765
|
],
|
|
565
|
-
}
|
|
566
|
-
|
|
766
|
+
});
|
|
767
|
+
}
|
|
567
768
|
// Run agent loop via startRun, wrapped in a request context so that
|
|
568
769
|
// tools (especially call-agent) can resolve the caller's org for org-scoped
|
|
569
770
|
// A2A delegation. Without this, getRequestOrgId() returns undefined and
|
|
@@ -590,8 +791,25 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
590
791
|
await releaseApplicableIntegrationBudgets(budgetReservations.reservations);
|
|
591
792
|
throw error;
|
|
592
793
|
}
|
|
593
|
-
const runId =
|
|
594
|
-
|
|
794
|
+
const runId = campaign?.runId ??
|
|
795
|
+
`integration-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
796
|
+
const storedProgressRef = campaign?.row.progressRef
|
|
797
|
+
? parseIntegrationProgressRef(campaign.row.progressRef)
|
|
798
|
+
: null;
|
|
799
|
+
const progress = storedProgressRef
|
|
800
|
+
? await adapter
|
|
801
|
+
.resumeRunProgress?.(incoming, storedProgressRef)
|
|
802
|
+
.catch(() => null)
|
|
803
|
+
: await adapter.startRunProgress?.(incoming).catch(() => null);
|
|
804
|
+
if (campaign && campaign.row.chunkCount > 1 && progress) {
|
|
805
|
+
await Promise.resolve(progress.onEvent({
|
|
806
|
+
type: "agent_call_progress",
|
|
807
|
+
agent: "Agent Native",
|
|
808
|
+
state: "working",
|
|
809
|
+
elapsedSeconds: 0,
|
|
810
|
+
detail: "Continuing in the background",
|
|
811
|
+
})).catch(() => { });
|
|
812
|
+
}
|
|
595
813
|
let usage = null;
|
|
596
814
|
let budgetsSettled = false;
|
|
597
815
|
// Wait for the run to complete inside this fresh function execution.
|
|
@@ -684,7 +902,23 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
684
902
|
let threadCheckpoint;
|
|
685
903
|
let outcome = { status: "completed" };
|
|
686
904
|
try {
|
|
905
|
+
if (campaign) {
|
|
906
|
+
const stillOwned = await heartbeatIntegrationCampaign(campaign.row.id, {
|
|
907
|
+
runId: campaign.runId,
|
|
908
|
+
leaseToken: campaign.leaseToken,
|
|
909
|
+
leaseDurationMs: INTEGRATION_CAMPAIGN_LEASE_MS,
|
|
910
|
+
});
|
|
911
|
+
if (!stillOwned) {
|
|
912
|
+
outcome = { status: "campaign-active" };
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
687
916
|
queuedA2AContinuation = hasQueuedA2AContinuation(completedRun);
|
|
917
|
+
const durableCampaignContinuation = Boolean(campaign &&
|
|
918
|
+
campaign.row.chunkCount < INTEGRATION_CAMPAIGN_MAX_CHUNKS &&
|
|
919
|
+
completedRun.status === "completed" &&
|
|
920
|
+
endedAtContinuationBoundary(completedRun) &&
|
|
921
|
+
!queuedA2AContinuation);
|
|
688
922
|
const slackInputRequest = incoming.platform === "slack"
|
|
689
923
|
? extractSlackInputRequest(completedRun)
|
|
690
924
|
: null;
|
|
@@ -704,6 +938,7 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
704
938
|
}
|
|
705
939
|
let suppressPlatformReply = queuedA2AContinuation &&
|
|
706
940
|
isQueuedA2AContinuationDeferral(responseText);
|
|
941
|
+
suppressPlatformReply ||= durableCampaignContinuation;
|
|
707
942
|
// Compute trusted tool receipts before choosing the empty-answer
|
|
708
943
|
// fallback. A completed write must not be reported as though nothing
|
|
709
944
|
// happened merely because the model ran out of time before its prose
|
|
@@ -787,6 +1022,14 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
787
1022
|
artifacts: extractA2AArtifactIdentities(toolResults, {
|
|
788
1023
|
persistedArtifactSecrets: artifactSecrets,
|
|
789
1024
|
}),
|
|
1025
|
+
...(campaign && queuedA2AContinuation
|
|
1026
|
+
? { awaitingA2ACompletion: true }
|
|
1027
|
+
: {}),
|
|
1028
|
+
...(campaign &&
|
|
1029
|
+
!queuedA2AContinuation &&
|
|
1030
|
+
!durableCampaignContinuation
|
|
1031
|
+
? { campaignTerminalStatus: "completed" }
|
|
1032
|
+
: {}),
|
|
790
1033
|
};
|
|
791
1034
|
if (opts.taskId) {
|
|
792
1035
|
await stageTaskDeliveryPayload(opts.taskId, JSON.stringify(stagedDeliveryPayload));
|
|
@@ -875,7 +1118,9 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
875
1118
|
}
|
|
876
1119
|
}
|
|
877
1120
|
// Persist thread data
|
|
878
|
-
|
|
1121
|
+
const historyMessageIds = stagedDeliveryPayload ??
|
|
1122
|
+
(campaign ? buildDeliveryHistoryMessageIds(incoming) : undefined);
|
|
1123
|
+
threadCheckpoint = await persistThreadData(threadId, incoming.text, completedRun, thread, deliveredResponse, toolResults, historyMessageIds, artifactSecrets, Boolean(campaign), !durableCampaignContinuation);
|
|
879
1124
|
if (outgoingForDelivery && stagedDeliveryPayload) {
|
|
880
1125
|
if (!threadCheckpoint) {
|
|
881
1126
|
throw new Error("Integration response history checkpoint failed");
|
|
@@ -903,6 +1148,62 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
903
1148
|
});
|
|
904
1149
|
await settleApplicableIntegrationBudgets(budgetReservations.reservations, usage);
|
|
905
1150
|
budgetsSettled = true;
|
|
1151
|
+
if (campaign && queuedA2AContinuation) {
|
|
1152
|
+
const waiting = await waitForA2AIntegrationCampaign(campaign.row.id, {
|
|
1153
|
+
runId: campaign.runId,
|
|
1154
|
+
leaseToken: campaign.leaseToken,
|
|
1155
|
+
nextRunAt: Date.now() + INTEGRATION_CAMPAIGN_A2A_CHECK_MS,
|
|
1156
|
+
progressRef: progress?.ref
|
|
1157
|
+
? JSON.stringify(progress.ref)
|
|
1158
|
+
: undefined,
|
|
1159
|
+
});
|
|
1160
|
+
if (!waiting) {
|
|
1161
|
+
throw new Error("Integration campaign lease was superseded");
|
|
1162
|
+
}
|
|
1163
|
+
outcome = { status: "campaign-pending" };
|
|
1164
|
+
}
|
|
1165
|
+
else if (campaign && durableCampaignContinuation) {
|
|
1166
|
+
const scheduled = await scheduleNextIntegrationCampaign(campaign.row.id, {
|
|
1167
|
+
runId: campaign.runId,
|
|
1168
|
+
leaseToken: campaign.leaseToken,
|
|
1169
|
+
nextRunAt: Date.now(),
|
|
1170
|
+
progressRef: progress?.ref
|
|
1171
|
+
? JSON.stringify(progress.ref)
|
|
1172
|
+
: undefined,
|
|
1173
|
+
checkpoint: JSON.stringify({
|
|
1174
|
+
reason: continuationBoundaryReason(completedRun),
|
|
1175
|
+
threadId,
|
|
1176
|
+
...(threadCheckpoint?.assistantMessageId
|
|
1177
|
+
? {
|
|
1178
|
+
assistantMessageId: threadCheckpoint.assistantMessageId,
|
|
1179
|
+
}
|
|
1180
|
+
: {}),
|
|
1181
|
+
}),
|
|
1182
|
+
});
|
|
1183
|
+
if (!scheduled) {
|
|
1184
|
+
throw new Error("Integration campaign lease was superseded");
|
|
1185
|
+
}
|
|
1186
|
+
await dispatchPendingIntegrationTask({
|
|
1187
|
+
taskId: opts.taskId,
|
|
1188
|
+
task: {
|
|
1189
|
+
platform: incoming.platform,
|
|
1190
|
+
externalThreadId: incoming.externalThreadId,
|
|
1191
|
+
platformContext: incoming.platformContext,
|
|
1192
|
+
},
|
|
1193
|
+
campaignContinuation: true,
|
|
1194
|
+
});
|
|
1195
|
+
outcome = { status: "campaign-pending" };
|
|
1196
|
+
}
|
|
1197
|
+
else if (campaign) {
|
|
1198
|
+
const completed = await completeIntegrationCampaignTask(campaign.row.id, {
|
|
1199
|
+
integrationTaskId: opts.taskId,
|
|
1200
|
+
runId: campaign.runId,
|
|
1201
|
+
leaseToken: campaign.leaseToken,
|
|
1202
|
+
});
|
|
1203
|
+
if (!completed) {
|
|
1204
|
+
throw new Error("Integration campaign lease was superseded");
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
906
1207
|
}
|
|
907
1208
|
catch (err) {
|
|
908
1209
|
console.error(`[integrations] Error sending response to ${incoming.platform}:`, err);
|
|
@@ -911,7 +1212,10 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
911
1212
|
? err.message.slice(0, 1000)
|
|
912
1213
|
: `${incoming.platform} response delivery failed`;
|
|
913
1214
|
if (!stagedDeliveryPayload?.deliveryReceipt) {
|
|
914
|
-
threadCheckpoint = await persistThreadData(threadId, incoming.text, completedRun, thread, undefined, collectToolResultSummaries(completedRun), stagedDeliveryPayload
|
|
1215
|
+
threadCheckpoint = await persistThreadData(threadId, incoming.text, completedRun, thread, undefined, collectToolResultSummaries(completedRun), stagedDeliveryPayload ??
|
|
1216
|
+
(campaign
|
|
1217
|
+
? buildDeliveryHistoryMessageIds(incoming)
|
|
1218
|
+
: undefined), artifactSecrets, Boolean(campaign));
|
|
915
1219
|
}
|
|
916
1220
|
if (usage) {
|
|
917
1221
|
await recordIntegrationUsage({
|
|
@@ -946,10 +1250,30 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
946
1250
|
? { assistantMessageId: threadCheckpoint.assistantMessageId }
|
|
947
1251
|
: {}),
|
|
948
1252
|
},
|
|
1253
|
+
...(campaign &&
|
|
1254
|
+
(stagedDeliveryPayload?.campaignTerminalStatus ||
|
|
1255
|
+
stagedDeliveryPayload?.awaitingA2ACompletion)
|
|
1256
|
+
? {
|
|
1257
|
+
campaignLease: {
|
|
1258
|
+
campaignId: campaign.row.id,
|
|
1259
|
+
runId: campaign.runId,
|
|
1260
|
+
leaseToken: campaign.leaseToken,
|
|
1261
|
+
campaignStatus: stagedDeliveryPayload.campaignTerminalStatus ??
|
|
1262
|
+
"waiting-a2a",
|
|
1263
|
+
},
|
|
1264
|
+
}
|
|
1265
|
+
: {}),
|
|
949
1266
|
errorMessage,
|
|
950
1267
|
};
|
|
951
1268
|
return;
|
|
952
1269
|
}
|
|
1270
|
+
if (campaign) {
|
|
1271
|
+
// A campaign-owned, no-delivery path must never fall through to
|
|
1272
|
+
// the default completed outcome. Its lease/checkpoint recovery
|
|
1273
|
+
// remains the sole owner of this logical turn.
|
|
1274
|
+
outcome = { status: "campaign-active" };
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
953
1277
|
// A queued continuation owns the final platform response. Later
|
|
954
1278
|
// bookkeeping failures must not close its stream with a false failure.
|
|
955
1279
|
if (queuedA2AContinuation)
|
|
@@ -991,6 +1315,12 @@ async function processIncomingMessage(incoming, options, opts = {}) {
|
|
|
991
1315
|
{
|
|
992
1316
|
useHostedSoftTimeoutDefault: true,
|
|
993
1317
|
backgroundFunction: isInBackgroundFunctionRuntime(),
|
|
1318
|
+
...(campaign
|
|
1319
|
+
? {
|
|
1320
|
+
turnId: campaign.row.turnId,
|
|
1321
|
+
noProgressTimeoutMs: INTEGRATION_CAMPAIGN_NO_PROGRESS_TIMEOUT_MS,
|
|
1322
|
+
}
|
|
1323
|
+
: {}),
|
|
994
1324
|
});
|
|
995
1325
|
});
|
|
996
1326
|
}
|
|
@@ -1274,7 +1604,7 @@ function hasSubstantiveA2APartialAnswer(text) {
|
|
|
1274
1604
|
* Persist the user message and agent response to the thread data,
|
|
1275
1605
|
* so the conversation history is available in the web UI too.
|
|
1276
1606
|
*/
|
|
1277
|
-
async function persistThreadData(threadId, userText, completedRun, thread, deliveredResponse, toolResults = [], messageIds, artifactSecrets = []) {
|
|
1607
|
+
async function persistThreadData(threadId, userText, completedRun, thread, deliveredResponse, toolResults = [], messageIds, artifactSecrets = [], mergeRunContent = false, deliveryAttempted = true) {
|
|
1278
1608
|
try {
|
|
1279
1609
|
let repo;
|
|
1280
1610
|
try {
|
|
@@ -1300,9 +1630,34 @@ async function persistThreadData(threadId, userText, completedRun, thread, deliv
|
|
|
1300
1630
|
const existingAssistantMsg = builtAssistantMsg
|
|
1301
1631
|
? repo.messages.find((message) => message?.id === builtAssistantMsg.id)
|
|
1302
1632
|
: undefined;
|
|
1633
|
+
if (builtAssistantMsg && mergeRunContent) {
|
|
1634
|
+
builtAssistantMsg.metadata.integrationRunIds = [completedRun.runId];
|
|
1635
|
+
}
|
|
1636
|
+
if (mergeRunContent && existingAssistantMsg && builtAssistantMsg) {
|
|
1637
|
+
const previousRunIds = Array.isArray(existingAssistantMsg.metadata?.integrationRunIds)
|
|
1638
|
+
? existingAssistantMsg.metadata.integrationRunIds.filter((value) => typeof value === "string")
|
|
1639
|
+
: [];
|
|
1640
|
+
if (!previousRunIds.includes(completedRun.runId)) {
|
|
1641
|
+
existingAssistantMsg.content = [
|
|
1642
|
+
...(Array.isArray(existingAssistantMsg.content)
|
|
1643
|
+
? existingAssistantMsg.content
|
|
1644
|
+
: []),
|
|
1645
|
+
...(Array.isArray(builtAssistantMsg.content)
|
|
1646
|
+
? builtAssistantMsg.content
|
|
1647
|
+
: []),
|
|
1648
|
+
];
|
|
1649
|
+
existingAssistantMsg.metadata = {
|
|
1650
|
+
...existingAssistantMsg.metadata,
|
|
1651
|
+
...builtAssistantMsg.metadata,
|
|
1652
|
+
integrationRunIds: [...previousRunIds, completedRun.runId],
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1303
1656
|
const assistantMsg = existingAssistantMsg ?? builtAssistantMsg;
|
|
1304
|
-
if (assistantMsg) {
|
|
1657
|
+
if (assistantMsg && deliveryAttempted) {
|
|
1305
1658
|
assistantMsg.metadata.integrationDeliveryAttempted = true;
|
|
1659
|
+
}
|
|
1660
|
+
if (assistantMsg) {
|
|
1306
1661
|
const artifactIdentities = extractA2AArtifactIdentities(toolResults, {
|
|
1307
1662
|
persistedArtifactSecrets: artifactSecrets,
|
|
1308
1663
|
});
|