@agent-native/core 0.122.1 → 0.122.3

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.
Files changed (114) hide show
  1. package/README.md +1 -13
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +12 -0
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/production-agent.ts +65 -1
  6. package/corpus/core/src/agent/run-loop-with-resume.ts +12 -0
  7. package/corpus/core/src/agent/run-store.ts +61 -1
  8. package/corpus/core/src/integrations/a2a-continuation-processor.ts +182 -0
  9. package/corpus/core/src/integrations/a2a-continuations-store.ts +122 -12
  10. package/corpus/core/src/integrations/integration-campaign-recovery.ts +127 -0
  11. package/corpus/core/src/integrations/integration-campaigns-store.ts +1039 -0
  12. package/corpus/core/src/integrations/integration-durable-dispatch.ts +28 -3
  13. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +8 -0
  14. package/corpus/core/src/integrations/plugin.ts +453 -35
  15. package/corpus/core/src/integrations/webhook-handler.ts +469 -11
  16. package/corpus/core/src/server/builder-design-systems.ts +1 -1
  17. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +15 -0
  18. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +3 -3
  19. package/corpus/templates/analytics/changelog/2026-07-25-analytics-dashboards-and-scheduled-reports-are-faster-and-mo.md +6 -0
  20. package/corpus/templates/analytics/server/db/schema.ts +12 -0
  21. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +8 -4
  22. package/corpus/templates/analytics/server/lib/dashboard-report.ts +136 -5
  23. package/corpus/templates/analytics/server/lib/dashboard-time-scope.ts +250 -1
  24. package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +167 -0
  25. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +34 -9
  26. package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +105 -23
  27. package/corpus/templates/analytics/server/lib/first-party-unbounded-panel-repair.ts +169 -0
  28. package/corpus/templates/analytics/server/plugins/db.ts +41 -1
  29. package/corpus/templates/analytics/shared/dashboard-report-timeouts.ts +6 -3
  30. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +7 -3
  31. package/corpus/templates/content/app/components/editor/body-hydration.ts +10 -0
  32. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -0
  33. package/corpus/templates/content/app/root.tsx +12 -1
  34. package/corpus/templates/content/changelog/2026-07-23-new-pages-open-immediately-while-slow-network-work-continues.md +6 -0
  35. package/corpus/templates/content/changelog/2026-07-25-database-preview-actions-open-reliably.md +6 -0
  36. package/corpus/templates/content/e2e/playwright.config.ts +1 -1
  37. package/corpus/templates/design/actions/edit-design.ts +66 -27
  38. package/corpus/templates/design/changelog/2026-07-25-design-edits-retry-concurrent-changes.md +6 -0
  39. package/corpus/templates/design/server/source-workspace.ts +5 -1
  40. package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +6 -4
  41. package/corpus/templates/forms/actions/create-form.ts +5 -1
  42. package/corpus/templates/forms/actions/export-responses.ts +22 -11
  43. package/corpus/templates/forms/actions/update-form.ts +5 -1
  44. package/corpus/templates/forms/changelog/2026-07-25-fields-can-be-created-without-ids.md +6 -0
  45. package/corpus/templates/forms/changelog/2026-07-25-response-exports-use-file-storage.md +6 -0
  46. package/corpus/templates/forms/server/lib/validate-fields.ts +43 -0
  47. package/corpus/templates/slides/actions/extract-pdf.ts +4 -4
  48. package/corpus/templates/slides/actions/import-file.ts +3 -4
  49. package/corpus/templates/slides/changelog/2026-07-25-gemini-image-models-updated.md +6 -0
  50. package/corpus/templates/slides/changelog/2026-07-25-pdf-imports-tolerate-missing-canvas.md +6 -0
  51. package/corpus/templates/slides/server/handlers/image-providers/gemini.ts +5 -2
  52. package/corpus/templates/slides/server/lib/pdf-parse-setup.ts +150 -0
  53. package/dist/agent/production-agent.d.ts.map +1 -1
  54. package/dist/agent/production-agent.js +63 -1
  55. package/dist/agent/production-agent.js.map +1 -1
  56. package/dist/agent/run-loop-with-resume.d.ts +7 -1
  57. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  58. package/dist/agent/run-loop-with-resume.js +7 -0
  59. package/dist/agent/run-loop-with-resume.js.map +1 -1
  60. package/dist/agent/run-store.d.ts.map +1 -1
  61. package/dist/agent/run-store.js +49 -0
  62. package/dist/agent/run-store.js.map +1 -1
  63. package/dist/collab/routes.d.ts +1 -1
  64. package/dist/collab/struct-routes.d.ts +1 -1
  65. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  66. package/dist/integrations/a2a-continuation-processor.d.ts +14 -0
  67. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  68. package/dist/integrations/a2a-continuation-processor.js +134 -1
  69. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  70. package/dist/integrations/a2a-continuations-store.d.ts +23 -0
  71. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  72. package/dist/integrations/a2a-continuations-store.js +99 -13
  73. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  74. package/dist/integrations/integration-campaign-recovery.d.ts +16 -0
  75. package/dist/integrations/integration-campaign-recovery.d.ts.map +1 -0
  76. package/dist/integrations/integration-campaign-recovery.js +94 -0
  77. package/dist/integrations/integration-campaign-recovery.js.map +1 -0
  78. package/dist/integrations/integration-campaigns-store.d.ts +121 -0
  79. package/dist/integrations/integration-campaigns-store.d.ts.map +1 -0
  80. package/dist/integrations/integration-campaigns-store.js +784 -0
  81. package/dist/integrations/integration-campaigns-store.js.map +1 -0
  82. package/dist/integrations/integration-durable-dispatch.d.ts +3 -0
  83. package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -1
  84. package/dist/integrations/integration-durable-dispatch.js +22 -3
  85. package/dist/integrations/integration-durable-dispatch.js.map +1 -1
  86. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  87. package/dist/integrations/pending-tasks-retry-job.js +6 -0
  88. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  89. package/dist/integrations/plugin.d.ts.map +1 -1
  90. package/dist/integrations/plugin.js +325 -18
  91. package/dist/integrations/plugin.js.map +1 -1
  92. package/dist/integrations/webhook-handler.d.ts +16 -1
  93. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  94. package/dist/integrations/webhook-handler.js +368 -13
  95. package/dist/integrations/webhook-handler.js.map +1 -1
  96. package/dist/observability/routes.d.ts +1 -1
  97. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  98. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  99. package/dist/secrets/routes.d.ts +9 -9
  100. package/dist/server/builder-design-systems.js +1 -1
  101. package/dist/server/builder-design-systems.js.map +1 -1
  102. package/package.json +3 -3
  103. package/src/agent/production-agent.ts +65 -1
  104. package/src/agent/run-loop-with-resume.ts +12 -0
  105. package/src/agent/run-store.ts +61 -1
  106. package/src/integrations/a2a-continuation-processor.ts +182 -0
  107. package/src/integrations/a2a-continuations-store.ts +122 -12
  108. package/src/integrations/integration-campaign-recovery.ts +127 -0
  109. package/src/integrations/integration-campaigns-store.ts +1039 -0
  110. package/src/integrations/integration-durable-dispatch.ts +28 -3
  111. package/src/integrations/pending-tasks-retry-job.ts +8 -0
  112. package/src/integrations/plugin.ts +453 -35
  113. package/src/integrations/webhook-handler.ts +469 -11
  114. package/src/server/builder-design-systems.ts +1 -1
@@ -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): Promise<ProcessIntegrationTaskResult>;
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":"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"}
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
- await recordInboundIntegrationAudit(task, parsed.incoming);
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
- ...existingMessages,
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 = `integration-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
594
- const progress = await adapter.startRunProgress?.(incoming).catch(() => null);
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
- threadCheckpoint = await persistThreadData(threadId, incoming.text, completedRun, thread, deliveredResponse, toolResults, stagedDeliveryPayload, artifactSecrets);
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, artifactSecrets);
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
  });