@agent-native/core 0.119.5 → 0.119.6

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 (89) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/durable-background.ts +1 -0
  5. package/corpus/core/src/deploy/build.ts +144 -2
  6. package/corpus/core/src/deploy/workspace-deploy.ts +123 -1
  7. package/corpus/core/src/integrations/integration-durable-dispatch-config.ts +22 -0
  8. package/corpus/core/src/integrations/integration-durable-dispatch.ts +190 -0
  9. package/corpus/core/src/integrations/pending-tasks-retry-job.ts +175 -87
  10. package/corpus/core/src/integrations/pending-tasks-store.ts +114 -11
  11. package/corpus/core/src/integrations/plugin.ts +113 -60
  12. package/corpus/core/src/integrations/task-queue-stats.ts +68 -15
  13. package/corpus/core/src/integrations/webhook-handler.ts +21 -46
  14. package/corpus/core/src/server/auth.ts +6 -0
  15. package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
  16. package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
  17. package/corpus/core/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
  18. package/corpus/templates/dispatch/changelog/2026-07-21-recover-stalled-slack-agent-work.md +6 -0
  19. package/dist/agent/durable-background.d.ts +1 -0
  20. package/dist/agent/durable-background.d.ts.map +1 -1
  21. package/dist/agent/durable-background.js +1 -0
  22. package/dist/agent/durable-background.js.map +1 -1
  23. package/dist/collab/routes.d.ts +1 -1
  24. package/dist/collab/struct-routes.d.ts +1 -1
  25. package/dist/deploy/build.d.ts +3 -0
  26. package/dist/deploy/build.d.ts.map +1 -1
  27. package/dist/deploy/build.js +106 -3
  28. package/dist/deploy/build.js.map +1 -1
  29. package/dist/deploy/workspace-deploy.d.ts.map +1 -1
  30. package/dist/deploy/workspace-deploy.js +103 -2
  31. package/dist/deploy/workspace-deploy.js.map +1 -1
  32. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  33. package/dist/integrations/integration-durable-dispatch-config.d.ts +7 -0
  34. package/dist/integrations/integration-durable-dispatch-config.d.ts.map +1 -0
  35. package/dist/integrations/integration-durable-dispatch-config.js +16 -0
  36. package/dist/integrations/integration-durable-dispatch-config.js.map +1 -0
  37. package/dist/integrations/integration-durable-dispatch.d.ts +23 -0
  38. package/dist/integrations/integration-durable-dispatch.d.ts.map +1 -0
  39. package/dist/integrations/integration-durable-dispatch.js +115 -0
  40. package/dist/integrations/integration-durable-dispatch.js.map +1 -0
  41. package/dist/integrations/pending-tasks-retry-job.d.ts +13 -1
  42. package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
  43. package/dist/integrations/pending-tasks-retry-job.js +133 -74
  44. package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
  45. package/dist/integrations/pending-tasks-store.d.ts +14 -2
  46. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  47. package/dist/integrations/pending-tasks-store.js +75 -11
  48. package/dist/integrations/pending-tasks-store.js.map +1 -1
  49. package/dist/integrations/plugin.d.ts.map +1 -1
  50. package/dist/integrations/plugin.js +88 -55
  51. package/dist/integrations/plugin.js.map +1 -1
  52. package/dist/integrations/task-queue-stats.d.ts +15 -3
  53. package/dist/integrations/task-queue-stats.d.ts.map +1 -1
  54. package/dist/integrations/task-queue-stats.js +45 -15
  55. package/dist/integrations/task-queue-stats.js.map +1 -1
  56. package/dist/integrations/webhook-handler.d.ts +1 -1
  57. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  58. package/dist/integrations/webhook-handler.js +18 -42
  59. package/dist/integrations/webhook-handler.js.map +1 -1
  60. package/dist/notifications/routes.d.ts +3 -3
  61. package/dist/observability/routes.d.ts +5 -5
  62. package/dist/progress/routes.d.ts +1 -1
  63. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  64. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  65. package/dist/resources/handlers.d.ts +1 -1
  66. package/dist/secrets/routes.d.ts +3 -3
  67. package/dist/server/auth.d.ts.map +1 -1
  68. package/dist/server/auth.js +5 -0
  69. package/dist/server/auth.js.map +1 -1
  70. package/dist/server/realtime-token.d.ts +1 -1
  71. package/dist/server/transcribe-voice.d.ts +1 -1
  72. package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
  73. package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
  74. package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
  75. package/package.json +1 -1
  76. package/src/agent/durable-background.ts +1 -0
  77. package/src/deploy/build.ts +144 -2
  78. package/src/deploy/workspace-deploy.ts +123 -1
  79. package/src/integrations/integration-durable-dispatch-config.ts +22 -0
  80. package/src/integrations/integration-durable-dispatch.ts +190 -0
  81. package/src/integrations/pending-tasks-retry-job.ts +175 -87
  82. package/src/integrations/pending-tasks-store.ts +114 -11
  83. package/src/integrations/plugin.ts +113 -60
  84. package/src/integrations/task-queue-stats.ts +68 -15
  85. package/src/integrations/webhook-handler.ts +21 -46
  86. package/src/server/auth.ts +6 -0
  87. package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +63 -22
  88. package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +63 -22
  89. package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +63 -22
@@ -1,6 +1,7 @@
1
1
  import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, getQuery, sendRedirect, } from "h3";
2
2
  import { getRequestHeader } from "h3";
3
3
  import { createRemoteJWKSet, jwtVerify } from "jose";
4
+ import { AGENT_BACKGROUND_PROCESSOR_FIELD, AGENT_BACKGROUND_PROCESSOR_INTEGRATION, } from "../agent/durable-background.js";
4
5
  import { abortRun } from "../agent/run-manager.js";
5
6
  import { getOrgContext, resolveOrgIdForEmail } from "../org/context.js";
6
7
  import { loadResourcesForPrompt } from "../server/agent-chat-plugin.js";
@@ -29,10 +30,11 @@ import { claimIntegrationControl } from "./controls-store.js";
29
30
  import { startGoogleDocsPoller, handlePushNotification, } from "./google-docs-poller.js";
30
31
  import { IntegrationIdentityDeclinedError, resolveDefaultIntegrationExecutionContext, } from "./identity.js";
31
32
  import { disconnectIntegrationInstallation, listIntegrationInstallations, resolveIntegrationTokenBundle, updateIntegrationInstallation, upsertIntegrationInstallation, } from "./installations-store.js";
33
+ import { dispatchPendingIntegrationTask, INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT, integrationDispatchScopeValue, isIntegrationDurableDispatchConfigured, } from "./integration-durable-dispatch.js";
32
34
  import { forgetIntegrationMemory, integrationMemoryActions, listIntegrationMemory, rememberForIntegrationScope, } from "./integration-memory.js";
33
- import { extractBearerToken, signInternalToken, verifyInternalToken, } from "./internal-token.js";
34
- import { startPendingTasksRetryJob } from "./pending-tasks-retry-job.js";
35
- import { claimPendingTask, failTaskDeliveryTransition, getNextPendingTaskIdForThread, insertPendingTask, isDuplicateEventError, MAX_PENDING_TASK_ATTEMPTS, markTaskCompleted, markTaskDeliveryRetryable, markTaskFailed, markTaskRetryable, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
35
+ import { extractBearerToken, verifyInternalToken } from "./internal-token.js";
36
+ import { retryStuckPendingTasks, startPendingTasksRetryJob, } from "./pending-tasks-retry-job.js";
37
+ import { claimPendingTask, failTaskDeliveryTransition, getNextPendingTaskForThread, insertPendingTask, isDuplicateEventError, MAX_PENDING_TASK_ATTEMPTS, markTaskCompleted, markTaskDeliveryRetryable, markTaskFailed, markTaskRetryable, stageTaskDeliveryPayload, } from "./pending-tasks-store.js";
36
38
  import { claimNextComputerCommand, claimNextRemoteCommand, enqueueComputerCommand, enqueueRemoteCommand as enqueueRemoteCommandRow, isRemoteCommandKind, listRemoteCommandsForOwner, updateRemoteCommandResult, } from "./remote-commands-store.js";
37
39
  import { authenticateRemoteDeviceToken, createRemoteDevice, getRemoteComputerCapabilities, getRemoteDeviceForOwner, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, toPublicRemoteDevice, unregisterRemoteDevice, updateRemoteDeviceDetails, } from "./remote-devices-store.js";
38
40
  import { startRemotePushDeliveryJob } from "./remote-push-delivery-job.js";
@@ -503,6 +505,11 @@ export function createIntegrationsPlugin(options) {
503
505
  externalEventKey: opts?.dedupeKey
504
506
  ? systemNoticeEventKey(opts.dedupeKey, dedupeTtlMs)
505
507
  : undefined,
508
+ dispatchScope: integrationDispatchScopeValue({
509
+ platform: incoming.platform,
510
+ externalThreadId: noticeThreadId,
511
+ platformContext: incoming.platformContext,
512
+ }),
506
513
  });
507
514
  }
508
515
  catch (err) {
@@ -510,35 +517,17 @@ export function createIntegrationsPlugin(options) {
510
517
  return;
511
518
  throw err;
512
519
  }
513
- // The SQL row is the durable source of truth. This best-effort self-call
514
- // only reduces latency; the recurring pending-task sweep retries a row
515
- // if the serverless host freezes this webhook execution immediately.
516
- let token;
517
- try {
518
- token = signInternalToken(taskId);
519
- }
520
- catch (err) {
521
- if (process.env.NODE_ENV === "production")
522
- throw err;
523
- }
524
- void fetch(`${getBaseUrl(event)}${P}/process-task`, {
525
- method: "POST",
526
- headers: {
527
- "Content-Type": "application/json",
528
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
520
+ await dispatchPendingIntegrationTask({
521
+ taskId,
522
+ task: {
523
+ platform: incoming.platform,
524
+ externalThreadId: noticeThreadId,
525
+ platformContext: incoming.platformContext,
529
526
  },
530
- body: JSON.stringify({ taskId }),
531
- }).catch((err) => {
532
- console.warn("[integrations] System notice dispatch failed:", err);
527
+ event,
528
+ baseUrl: getBaseUrl(event),
533
529
  });
534
530
  }
535
- async function requireSession(event) {
536
- const session = await getSession(event).catch(() => null);
537
- if (session?.email)
538
- return true;
539
- setResponseStatus(event, 401);
540
- return false;
541
- }
542
531
  async function requireSessionContext(event) {
543
532
  const session = await getSession(event).catch(() => null);
544
533
  if (!session?.email) {
@@ -660,10 +649,11 @@ export function createIntegrationsPlugin(options) {
660
649
  setResponseStatus(event, 405);
661
650
  return { error: "Method not allowed" };
662
651
  }
663
- if (!(await requireSession(event)))
652
+ const scope = await requireSessionContext(event);
653
+ if (!scope)
664
654
  return { error: "unauthorized" };
665
655
  try {
666
- return await getTaskQueueStats();
656
+ return await getTaskQueueStats(scope);
667
657
  }
668
658
  catch (err) {
669
659
  setResponseStatus(event, 500);
@@ -1248,10 +1238,42 @@ export function createIntegrationsPlugin(options) {
1248
1238
  });
1249
1239
  return { ok: true, ...result };
1250
1240
  }));
1241
+ // ─── Durable pending-task recovery sweep ─────────────────────
1242
+ h3.use(`${P}/retry-stuck-tasks`, defineEventHandler(async (event) => {
1243
+ if (getMethod(event) !== "POST") {
1244
+ setResponseStatus(event, 405);
1245
+ return { error: "Method not allowed" };
1246
+ }
1247
+ const body = (await readBody(event));
1248
+ if (body?.taskId !== INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT) {
1249
+ setResponseStatus(event, 400);
1250
+ return { error: "invalid sweep subject" };
1251
+ }
1252
+ if (!process.env.A2A_SECRET) {
1253
+ setResponseStatus(event, 503);
1254
+ return { error: "durable integration recovery is not configured" };
1255
+ }
1256
+ const token = extractBearerToken(getRequestHeader(event, "authorization"));
1257
+ if (!token ||
1258
+ !verifyInternalToken(INTEGRATION_RETRY_SWEEP_TOKEN_SUBJECT, token)) {
1259
+ setResponseStatus(event, 401);
1260
+ return { error: "Invalid or expired internal token" };
1261
+ }
1262
+ if (!isIntegrationDurableDispatchConfigured()) {
1263
+ return { ok: true, disabled: true };
1264
+ }
1265
+ const result = await retryStuckPendingTasks({
1266
+ webhookBaseUrl: getBaseUrl(event),
1267
+ limit: 20,
1268
+ durableOnly: true,
1269
+ });
1270
+ return { ok: true, ...result };
1271
+ }));
1251
1272
  // ─── Process pending task (cross-platform task queue) ────────
1252
1273
  // POST /_agent-native/integrations/process-task
1253
- // Internal endpoint invoked via fire-and-forget self-webhook from the
1254
- // public webhook handler. Auth: HMAC bearer signed with A2A_SECRET.
1274
+ // Internal endpoint invoked from the public webhook handler through either
1275
+ // the portable self-dispatch path or an acknowledged background handoff.
1276
+ // Auth: HMAC bearer signed with A2A_SECRET.
1255
1277
  // Each invocation runs the agent loop in a fresh function execution.
1256
1278
  h3.use(`${P}/process-task`, defineEventHandler(async (event) => {
1257
1279
  if (getMethod(event) !== "POST") {
@@ -1291,7 +1313,11 @@ export function createIntegrationsPlugin(options) {
1291
1313
  }
1292
1314
  }
1293
1315
  // Atomic claim: only one invocation gets to process this task
1294
- const task = await claimPendingTask(taskId);
1316
+ const dispatchOutcome = body[AGENT_BACKGROUND_PROCESSOR_FIELD] ===
1317
+ AGENT_BACKGROUND_PROCESSOR_INTEGRATION
1318
+ ? "background-acknowledged"
1319
+ : "portable-unconfirmed";
1320
+ const task = await claimPendingTask(taskId, { dispatchOutcome });
1295
1321
  if (!task) {
1296
1322
  setResponseStatus(event, 200);
1297
1323
  return { ok: true, skipped: "already-claimed-or-missing" };
@@ -1384,18 +1410,19 @@ export function createIntegrationsPlugin(options) {
1384
1410
  return { ok: true, taskId, retrying: "response-delivery" };
1385
1411
  }
1386
1412
  await markTaskCompleted(taskId);
1387
- const nextTaskId = await getNextPendingTaskIdForThread(task.platform, task.externalThreadId);
1388
- if (nextTaskId) {
1389
- const nextToken = signInternalToken(nextTaskId);
1390
- void fetch(`${getBaseUrl(event)}${P}/process-task`, {
1391
- method: "POST",
1392
- headers: {
1393
- "Content-Type": "application/json",
1394
- ...(nextToken ? { Authorization: `Bearer ${nextToken}` } : {}),
1413
+ const nextTask = await getNextPendingTaskForThread(task.platform, task.externalThreadId);
1414
+ if (nextTask) {
1415
+ await dispatchPendingIntegrationTask({
1416
+ taskId: nextTask.id,
1417
+ task: {
1418
+ platform: task.platform,
1419
+ externalThreadId: task.externalThreadId,
1420
+ platformContext: nextTask.dispatchScope
1421
+ ? { channelId: nextTask.dispatchScope }
1422
+ : undefined,
1395
1423
  },
1396
- body: JSON.stringify({ taskId: nextTaskId }),
1397
- }).catch((err) => {
1398
- console.error("[integrations] Failed to dispatch queued thread turn:", err);
1424
+ event,
1425
+ baseUrl: getBaseUrl(event),
1399
1426
  });
1400
1427
  }
1401
1428
  await processDueA2AContinuations({
@@ -1580,18 +1607,21 @@ export function createIntegrationsPlugin(options) {
1580
1607
  ownerEmail: control.ownerEmail,
1581
1608
  orgId: control.orgId,
1582
1609
  externalEventKey: `control:${control.id}`,
1610
+ dispatchScope: integrationDispatchScopeValue({
1611
+ platform: incoming.platform,
1612
+ externalThreadId: incoming.externalThreadId,
1613
+ platformContext: incoming.platformContext,
1614
+ }),
1583
1615
  });
1584
- const processUrl = `${getBaseUrl(event)}${P}/process-task`;
1585
- const token = signInternalToken(taskId);
1586
- void fetch(processUrl, {
1587
- method: "POST",
1588
- headers: {
1589
- "Content-Type": "application/json",
1590
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
1616
+ await dispatchPendingIntegrationTask({
1617
+ taskId,
1618
+ task: {
1619
+ platform: incoming.platform,
1620
+ externalThreadId: incoming.externalThreadId,
1621
+ platformContext: incoming.platformContext,
1591
1622
  },
1592
- body: JSON.stringify({ taskId }),
1593
- }).catch((err) => {
1594
- console.error("[slack] Approval dispatch failed:", err);
1623
+ event,
1624
+ baseUrl: getBaseUrl(event),
1595
1625
  });
1596
1626
  }
1597
1627
  catch (err) {
@@ -1943,6 +1973,9 @@ export function createIntegrationsPlugin(options) {
1943
1973
  // Already handled by the dedicated /process-task route above
1944
1974
  if (parts[0] === "process-task")
1945
1975
  return;
1976
+ // Already handled by the signed durable recovery route above
1977
+ if (parts[0] === "retry-stuck-tasks")
1978
+ return;
1946
1979
  // Already handled by the dedicated /process-a2a-continuation route above
1947
1980
  if (parts[0] === "process-a2a-continuation")
1948
1981
  return;