@adcp/sdk 16.0.0 → 16.2.0

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 (85) hide show
  1. package/README.md +52 -0
  2. package/bin/adcp-identity.js +3 -2
  3. package/dist/lib/client/core.d.mts +1 -1
  4. package/dist/lib/client/core.d.ts +1 -1
  5. package/dist/lib/core/AgentClient.d.mts +8 -1
  6. package/dist/lib/core/AgentClient.d.ts +8 -1
  7. package/dist/lib/core/AgentClient.js +10 -0
  8. package/dist/lib/core/AgentClient.mjs +10 -0
  9. package/dist/lib/core/AsyncHandler.d.mts +2 -0
  10. package/dist/lib/core/AsyncHandler.d.ts +2 -0
  11. package/dist/lib/core/AsyncHandler.js +1 -1
  12. package/dist/lib/core/AsyncHandler.mjs +1 -1
  13. package/dist/lib/core/ConversationTypes.d.mts +32 -0
  14. package/dist/lib/core/ConversationTypes.d.ts +32 -0
  15. package/dist/lib/core/SingleAgentClient.d.mts +11 -1
  16. package/dist/lib/core/SingleAgentClient.d.ts +11 -1
  17. package/dist/lib/core/SingleAgentClient.js +25 -6
  18. package/dist/lib/core/SingleAgentClient.mjs +26 -7
  19. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  20. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  21. package/dist/lib/core/TaskExecutor.js +127 -34
  22. package/dist/lib/core/TaskExecutor.mjs +127 -34
  23. package/dist/lib/core/task-deadline.d.mts +2 -0
  24. package/dist/lib/core/task-deadline.d.ts +2 -0
  25. package/dist/lib/core/task-deadline.js +20 -2
  26. package/dist/lib/core/task-deadline.mjs +19 -2
  27. package/dist/lib/governance/authorization.d.mts +66 -0
  28. package/dist/lib/governance/authorization.d.ts +66 -0
  29. package/dist/lib/governance/authorization.js +181 -37
  30. package/dist/lib/governance/authorization.mjs +183 -36
  31. package/dist/lib/governance/index.d.mts +2 -2
  32. package/dist/lib/governance/index.d.ts +2 -2
  33. package/dist/lib/governance/index.js +4 -2
  34. package/dist/lib/governance/index.mjs +3 -1
  35. package/dist/lib/identity-3.3/index.d.mts +1 -0
  36. package/dist/lib/identity-3.3/index.d.ts +1 -0
  37. package/dist/lib/identity-3.3/index.js +3 -1
  38. package/dist/lib/identity-3.3/index.mjs +1 -0
  39. package/dist/lib/identity-3.3/jws-artifacts.d.mts +147 -0
  40. package/dist/lib/identity-3.3/jws-artifacts.d.ts +147 -0
  41. package/dist/lib/identity-3.3/jws-artifacts.js +258 -0
  42. package/dist/lib/identity-3.3/jws-artifacts.mjs +234 -0
  43. package/dist/lib/identity-3.3/revocation.d.mts +7 -0
  44. package/dist/lib/identity-3.3/revocation.d.ts +7 -0
  45. package/dist/lib/identity-3.3/revocation.js +13 -0
  46. package/dist/lib/identity-3.3/revocation.mjs +13 -0
  47. package/dist/lib/identity-3.3/schemas.draft.js +1 -1
  48. package/dist/lib/identity-3.3/schemas.draft.mjs +1 -1
  49. package/dist/lib/identity-3.3/trust.d.mts +8 -0
  50. package/dist/lib/identity-3.3/trust.d.ts +8 -0
  51. package/dist/lib/identity-3.3/trust.js +41 -12
  52. package/dist/lib/identity-3.3/trust.mjs +40 -12
  53. package/dist/lib/index.d.mts +3 -3
  54. package/dist/lib/index.d.ts +3 -3
  55. package/dist/lib/index.js +2 -0
  56. package/dist/lib/index.mjs +2 -0
  57. package/dist/lib/registry/types.generated.d.mts +24 -1
  58. package/dist/lib/registry/types.generated.d.ts +24 -1
  59. package/dist/lib/server/account-sync-contract.js +4 -1
  60. package/dist/lib/server/account-sync-contract.mjs +4 -1
  61. package/dist/lib/server/decisioning/runtime/from-platform.js +22 -17
  62. package/dist/lib/server/decisioning/runtime/from-platform.mjs +22 -17
  63. package/dist/lib/server/index.d.mts +2 -2
  64. package/dist/lib/server/index.d.ts +2 -2
  65. package/dist/lib/server/index.js +2 -0
  66. package/dist/lib/server/index.mjs +2 -0
  67. package/dist/lib/signing/web-bot-auth.js +25 -5
  68. package/dist/lib/signing/web-bot-auth.mjs +25 -6
  69. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  70. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  71. package/dist/lib/v2/projection/catalog.d.mts +6 -0
  72. package/dist/lib/v2/projection/catalog.d.ts +6 -0
  73. package/dist/lib/v2/projection/catalog.js +5 -0
  74. package/dist/lib/v2/projection/catalog.mjs +4 -0
  75. package/dist/lib/v2/projection/v1-to-v2.js +3 -2
  76. package/dist/lib/v2/projection/v1-to-v2.mjs +4 -3
  77. package/dist/lib/version.d.mts +3 -3
  78. package/dist/lib/version.d.ts +3 -3
  79. package/dist/lib/version.js +3 -3
  80. package/dist/lib/version.mjs +3 -3
  81. package/docs/TYPE-SUMMARY.md +12 -2
  82. package/docs/llms.txt +3 -3
  83. package/docs/migration-14.x-rc-worksheet.md +4 -4
  84. package/docs/migration-agent-identity-3.3.md +72 -19
  85. package/package.json +6 -4
@@ -614,6 +614,10 @@ class TaskExecutor {
614
614
  * @internal
615
615
  */
616
616
  async observeExternalTaskStatus(taskId, status, result, identity = {}) {
617
+ const task = this.activeTasks.get(taskId);
618
+ if (task && (identity.serverTaskId !== void 0 && task.serverTaskId !== void 0 && identity.serverTaskId !== task.serverTaskId || identity.taskType !== void 0 && identity.taskType !== task.taskName)) {
619
+ throw new ConfigurationError("The pushed task identity does not match the tracked operation.");
620
+ }
617
621
  if (!["completed", "failed", "rejected", "canceled", "governance-denied", "aborted"].includes(status)) {
618
622
  return { settled: false, result };
619
623
  }
@@ -642,7 +646,9 @@ class TaskExecutor {
642
646
  this.pendingExternalTaskObservations.set(taskId, pending);
643
647
  return { settled: false, queued: true };
644
648
  }
645
- this.updateTaskStatus(taskId, status, result);
649
+ if (task && task.serverTaskId === void 0 && identity.serverTaskId !== void 0)
650
+ task.serverTaskId = identity.serverTaskId;
651
+ if (task && !TERMINAL_TASK_STATUSES.has(task.status)) this.updateTaskStatus(taskId, status, result);
646
652
  return { settled: false, result };
647
653
  }
648
654
  /** Whether this process can safely settle or reject a durable callback for the operation. */
@@ -770,6 +776,9 @@ class TaskExecutor {
770
776
  this.requireDeferredSettlementOperationRouting(this.config.deferredStorage, true);
771
777
  }
772
778
  const taskId = getTaskOperationId(options) ?? randomUUID();
779
+ if (this.activeTasks.has(taskId) || this.compactedTaskIds.has(taskId) || this.deferredTerminalPublicationTaskIds.has(taskId) || this.closedExternalTaskSettlementTaskIds.has(taskId) || this.externalTaskSettlementHandlers.has(taskId)) {
780
+ throw new ConfigurationError("operationId is already in use by this client; track the existing task instead.");
781
+ }
773
782
  const startTime = Date.now();
774
783
  const workingTimeout = this.config.workingTimeout || 12e4;
775
784
  const wireAdcpVersion = options.wireAdcpVersion ?? this.config.wireAdcpVersion;
@@ -1045,12 +1054,14 @@ class TaskExecutor {
1045
1054
  );
1046
1055
  throwIfAborted(postDispatchOptions.signal);
1047
1056
  const respStatus = this.responseParser.getStatus(response);
1057
+ taskState.serverTaskId = this.responseParser.getTaskId(response);
1048
1058
  await this.config.onActivity?.({
1049
1059
  type: "protocol_response",
1050
1060
  operation_id: taskId,
1051
1061
  agent_id: agent.id,
1052
1062
  context_id: options.contextId,
1053
1063
  task_id: taskId,
1064
+ server_task_id: taskState.serverTaskId,
1054
1065
  task_type: taskName,
1055
1066
  status: respStatus,
1056
1067
  payload: response,
@@ -1225,6 +1236,9 @@ class TaskExecutor {
1225
1236
  }
1226
1237
  async handleAsyncResponse(agent, taskId, taskName, params, response, messages, inputHandler, options = {}, debugLogs = [], startTime = Date.now(), deferTerminalTaskStatus = false, persistPausedContinuation = true, serverVersion = "v3", deferredClientContext, requireDeferredSettlementResumeAuthorization = false) {
1227
1238
  const status = this.responseParser.getStatus(response);
1239
+ const serverTaskId = this.responseParser.getTaskId(response);
1240
+ const task = this.activeTasks.get(taskId);
1241
+ if (task && serverTaskId) task.serverTaskId = serverTaskId;
1228
1242
  switch (status) {
1229
1243
  case ADCP_STATUS.COMPLETED:
1230
1244
  const completedData = this.extractResponseData(response, debugLogs, taskName, serverVersion);
@@ -1580,8 +1594,70 @@ class TaskExecutor {
1580
1594
  debug_logs: debugLogs
1581
1595
  };
1582
1596
  }
1597
+ /** Rebuild the polling continuation for seller work persisted by the caller. */
1598
+ trackTask(agent, request) {
1599
+ if (typeof request.taskId !== "string" || !request.taskId.trim()) {
1600
+ throw new TypeError("taskId must be a non-empty seller task id.");
1601
+ }
1602
+ if (typeof request.taskType !== "string" || !request.taskType.trim()) {
1603
+ throw new TypeError("taskType must be a non-empty tool name.");
1604
+ }
1605
+ const operationId = getTaskOperationId(request) ?? randomUUID();
1606
+ const existing = this.activeTasks.get(operationId);
1607
+ if (existing && (existing.serverTaskId !== request.taskId || existing.taskName !== request.taskType || existing.agent.id !== agent.id || existing.agent.protocol !== agent.protocol || existing.status === "pending")) {
1608
+ throw new ConfigurationError(
1609
+ "operationId is already tracked by this client for a different or dispatching task."
1610
+ );
1611
+ }
1612
+ if (this.deferredTerminalPublicationTaskIds.has(operationId) || this.closedExternalTaskSettlementTaskIds.has(operationId) || this.externalTaskSettlementHandlers.has(operationId)) {
1613
+ throw new ConfigurationError("This operation requires its original continuation or durable settlement recovery.");
1614
+ }
1615
+ const startTime = existing?.startTime ?? Date.now();
1616
+ if (!existing)
1617
+ this.activeTasks.set(operationId, {
1618
+ taskId: operationId,
1619
+ serverTaskId: request.taskId,
1620
+ taskName: request.taskType,
1621
+ status: "submitted",
1622
+ messages: [],
1623
+ startTime,
1624
+ attempt: 0,
1625
+ maxAttempts: 0,
1626
+ options: {},
1627
+ agent: { id: agent.id, name: agent.name, protocol: agent.protocol },
1628
+ serverVersion: "v3"
1629
+ });
1630
+ if (!existing) this.compactIntermediateTaskState(operationId, "submitted");
1631
+ const continuation = this.createSubmittedContinuation(
1632
+ agent,
1633
+ operationId,
1634
+ request.taskType,
1635
+ request.taskId,
1636
+ void 0,
1637
+ snapshotTransportOptions(request.transport ?? this.config.transport),
1638
+ {
1639
+ ...this.buildMetadata({
1640
+ taskId: operationId,
1641
+ taskName: request.taskType,
1642
+ agent,
1643
+ startTime,
1644
+ status: "submitted"
1645
+ }),
1646
+ serverTaskId: request.taskId,
1647
+ a2aTaskId: request.cancelOnAbort === true ? request.a2aTaskId : void 0
1648
+ },
1649
+ false,
1650
+ "v3",
1651
+ this.config.wireAdcpVersion,
1652
+ this.config.versionEnvelope,
1653
+ request.signal
1654
+ );
1655
+ return { ...continuation, operationId };
1656
+ }
1583
1657
  /** Build polling closures around one authoritative seller work handle. */
1584
- createSubmittedContinuation(agent, taskId, taskName, serverTaskId, webhookUrl, pollingTransport, metadata, deferTerminalTaskStatus, serverVersion, wireAdcpVersion, versionEnvelope) {
1658
+ createSubmittedContinuation(agent, taskId, taskName, serverTaskId, webhookUrl, pollingTransport, metadata, deferTerminalTaskStatus, serverVersion, wireAdcpVersion, versionEnvelope, pollingSignal) {
1659
+ const registeredTask = this.activeTasks.get(taskId);
1660
+ const canPublish = (status) => registeredTask !== void 0 && this.activeTasks.get(taskId) === registeredTask && !TERMINAL_TASK_STATUSES.has(registeredTask.status) && registeredTask.status !== status;
1585
1661
  return {
1586
1662
  taskId: serverTaskId,
1587
1663
  webhookUrl,
@@ -1591,25 +1667,27 @@ class TaskExecutor {
1591
1667
  agent,
1592
1668
  serverTaskId,
1593
1669
  transportSnapshot,
1594
- void 0,
1670
+ pollingSignal,
1595
1671
  taskName,
1596
1672
  serverVersion,
1597
1673
  wireAdcpVersion,
1598
- versionEnvelope
1674
+ versionEnvelope,
1675
+ taskId
1599
1676
  )).task;
1600
- if (!deferTerminalTaskStatus && ["completed", "failed", "rejected", "canceled"].includes(task.status)) {
1677
+ if (!deferTerminalTaskStatus && ["completed", "failed", "rejected", "canceled"].includes(task.status) && canPublish(task.status)) {
1601
1678
  this.updateTaskStatus(taskId, task.status, task.result, task.error);
1602
1679
  }
1603
- return task;
1680
+ return { ...task, operationId: taskId, serverTaskId };
1604
1681
  },
1605
1682
  waitForCompletion: async (pollInterval = 6e4, signal, requireExactTaskIdentity = false) => {
1606
1683
  void requireExactTaskIdentity;
1684
+ const completionSignal = pollingSignal && signal ? AbortSignal.any([pollingSignal, signal]) : signal ?? pollingSignal;
1607
1685
  const completed = await this.pollTaskCompletion(
1608
1686
  agent,
1609
1687
  serverTaskId,
1610
1688
  pollInterval,
1611
1689
  pollingTransport,
1612
- signal,
1690
+ completionSignal,
1613
1691
  metadata.a2aTaskId,
1614
1692
  taskName,
1615
1693
  taskId,
@@ -1617,7 +1695,9 @@ class TaskExecutor {
1617
1695
  wireAdcpVersion,
1618
1696
  versionEnvelope
1619
1697
  );
1620
- if (!deferTerminalTaskStatus || !TERMINAL_TASK_STATUSES.has(completed.status)) {
1698
+ if ((!deferTerminalTaskStatus || !TERMINAL_TASK_STATUSES.has(completed.status)) && // Cancellation, identity mismatches, missing tasks, and malformed
1699
+ // responses establish observation failure, not seller task failure.
1700
+ (!TERMINAL_TASK_STATUSES.has(completed.status) || isAuthoritativePolledTerminal(completed)) && canPublish(completed.status)) {
1621
1701
  this.updateTaskStatus(taskId, completed.status, completed.data, completed.error);
1622
1702
  }
1623
1703
  return completed;
@@ -1630,7 +1710,9 @@ class TaskExecutor {
1630
1710
  * local task publication only after those final wrappers return.
1631
1711
  */
1632
1712
  attachSettledContinuationTaskStatus(taskId, result) {
1713
+ const registeredTask = this.activeTasks.get(taskId);
1633
1714
  const publish = (status, data, error, identity) => {
1715
+ if (registeredTask === void 0 || this.activeTasks.get(taskId) !== registeredTask) return;
1634
1716
  this.publishSettledTaskStatus(taskId, status, data, error, false, identity);
1635
1717
  };
1636
1718
  if (result.submitted) {
@@ -2261,7 +2343,7 @@ class TaskExecutor {
2261
2343
  return [];
2262
2344
  }
2263
2345
  }
2264
- async getTaskStatusWithRawResponse(agent, taskId, transport, signal, expectedTaskType, serverVersion, wireAdcpVersion, versionEnvelope) {
2346
+ async getTaskStatusWithRawResponse(agent, taskId, transport, signal, expectedTaskType, serverVersion, wireAdcpVersion, versionEnvelope, operationId) {
2265
2347
  const toolName = agent.protocol === "mcp" ? "tasks_get" : "tasks/get";
2266
2348
  const response = await ProtocolClient.callTool(
2267
2349
  agent,
@@ -2280,7 +2362,8 @@ class TaskExecutor {
2280
2362
  signal,
2281
2363
  onTransportActivity: this.config.onTransportActivity,
2282
2364
  transportActivityContext: {
2283
- taskId
2365
+ taskId,
2366
+ ...operationId !== void 0 && { operationId }
2284
2367
  }
2285
2368
  }
2286
2369
  );
@@ -2312,12 +2395,13 @@ class TaskExecutor {
2312
2395
  wireOptions?.versionEnvelope
2313
2396
  )).task;
2314
2397
  }
2315
- async pollTaskCompletion(agent, taskId, pollInterval = 6e4, transport, signal, a2aCancellationTaskId, expectedTaskType, metadataTaskId = taskId, serverVersion, wireAdcpVersion, versionEnvelope) {
2398
+ async pollTaskCompletion(agent, taskId, pollInterval = 6e4, transport, signal, a2aCancellationTaskId, expectedTaskType, metadataTaskId, serverVersion, wireAdcpVersion, versionEnvelope) {
2316
2399
  const transportSnapshot = snapshotTransportOptions(transport ?? this.config.transport);
2317
2400
  if (!Number.isFinite(pollInterval) || pollInterval < 0 || pollInterval > MAX_TIMER_DELAY_MS) {
2318
2401
  throw new RangeError(`pollInterval must be a finite non-negative number <= ${MAX_TIMER_DELAY_MS}`);
2319
2402
  }
2320
2403
  const pollStartTime = Date.now();
2404
+ const correlationId = metadataTaskId ?? taskId;
2321
2405
  const withServerTaskId = (metadata) => ({
2322
2406
  ...metadata,
2323
2407
  serverTaskId: taskId
@@ -2345,8 +2429,8 @@ class TaskExecutor {
2345
2429
  error: `tasks/get polling was cancelled before a terminal state was observed: ${reason}`,
2346
2430
  metadata: withServerTaskId(
2347
2431
  this.buildMetadata({
2348
- taskId: metadataTaskId,
2349
- taskName: "unknown",
2432
+ taskId: correlationId,
2433
+ taskName: expectedTaskType ?? "unknown",
2350
2434
  agent,
2351
2435
  startTime: pollStartTime,
2352
2436
  status: "failed"
@@ -2365,11 +2449,15 @@ class TaskExecutor {
2365
2449
  expectedTaskType,
2366
2450
  serverVersion,
2367
2451
  wireAdcpVersion,
2368
- versionEnvelope
2452
+ versionEnvelope,
2453
+ metadataTaskId
2369
2454
  );
2370
2455
  status = pollResult.task;
2371
2456
  rawResponse = pollResult.rawResponse;
2372
2457
  } catch (err) {
2458
+ if (signal?.aborted && agent.protocol === "a2a" && a2aCancellationTaskId && isAbortOrTimeoutError(err)) {
2459
+ continue;
2460
+ }
2373
2461
  if (err instanceof TaskBindingMismatchError) {
2374
2462
  return attachMatch({
2375
2463
  success: false,
@@ -2377,8 +2465,8 @@ class TaskExecutor {
2377
2465
  error: err.message,
2378
2466
  metadata: withServerTaskId(
2379
2467
  this.buildMetadata({
2380
- taskId: metadataTaskId,
2381
- taskName: "unknown",
2468
+ taskId: correlationId,
2469
+ taskName: expectedTaskType ?? "unknown",
2382
2470
  agent,
2383
2471
  startTime: pollStartTime,
2384
2472
  status: "failed",
@@ -2395,8 +2483,8 @@ class TaskExecutor {
2395
2483
  error: `Task ${taskId} is no longer queryable \u2014 it may have been completed and evicted by the seller before this poll arrived. Consider using push notifications (reporting_webhook) instead of polling, or configuring a longer task retention TTL on the seller.`,
2396
2484
  metadata: withServerTaskId(
2397
2485
  this.buildMetadata({
2398
- taskId: metadataTaskId,
2399
- taskName: "unknown",
2486
+ taskId: correlationId,
2487
+ taskName: expectedTaskType ?? "unknown",
2400
2488
  agent,
2401
2489
  startTime: pollStartTime,
2402
2490
  status: "failed"
@@ -2411,13 +2499,15 @@ class TaskExecutor {
2411
2499
  success: false,
2412
2500
  status: "failed",
2413
2501
  error: "The seller returned a task outside the requested durable task identity.",
2414
- metadata: this.buildMetadata({
2415
- taskId,
2416
- taskName: "unknown",
2417
- agent,
2418
- startTime: pollStartTime,
2419
- status: "failed"
2420
- })
2502
+ metadata: withServerTaskId(
2503
+ this.buildMetadata({
2504
+ taskId: correlationId,
2505
+ taskName: expectedTaskType ?? "unknown",
2506
+ agent,
2507
+ startTime: pollStartTime,
2508
+ status: "failed"
2509
+ })
2510
+ )
2421
2511
  });
2422
2512
  }
2423
2513
  if (status.status === ADCP_STATUS.COMPLETED) {
@@ -2430,7 +2520,7 @@ class TaskExecutor {
2430
2520
  data: status.result,
2431
2521
  metadata: withServerTaskId(
2432
2522
  this.buildMetadata({
2433
- taskId: metadataTaskId,
2523
+ taskId: correlationId,
2434
2524
  taskName: status.taskType,
2435
2525
  agent,
2436
2526
  responseTimeMs: Date.now() - status.createdAt,
@@ -2449,11 +2539,11 @@ class TaskExecutor {
2449
2539
  data: status.result,
2450
2540
  error: this.extractOperationError(status.result),
2451
2541
  adcpError: asyncResultErr,
2452
- errorInstance: this.buildErrorInstance(metadataTaskId, asyncResultErr),
2542
+ errorInstance: this.buildErrorInstance(correlationId, asyncResultErr),
2453
2543
  correlationId: extractCorrelationId(status.result),
2454
2544
  metadata: withServerTaskId(
2455
2545
  this.buildMetadata({
2456
- taskId: metadataTaskId,
2546
+ taskId: correlationId,
2457
2547
  taskName: status.taskType,
2458
2548
  agent,
2459
2549
  responseTimeMs: Date.now() - status.createdAt,
@@ -2473,11 +2563,11 @@ class TaskExecutor {
2473
2563
  data: status.result,
2474
2564
  error: status.error || status.message || `Task ${status.status}`,
2475
2565
  adcpError: asyncFailedErr,
2476
- errorInstance: this.buildErrorInstance(metadataTaskId, asyncFailedErr),
2566
+ errorInstance: this.buildErrorInstance(correlationId, asyncFailedErr),
2477
2567
  correlationId: extractCorrelationId(status.result),
2478
2568
  metadata: withServerTaskId(
2479
2569
  this.buildMetadata({
2480
- taskId: metadataTaskId,
2570
+ taskId: correlationId,
2481
2571
  taskName: status.taskType,
2482
2572
  agent,
2483
2573
  responseTimeMs: Date.now() - status.createdAt,
@@ -2495,7 +2585,7 @@ class TaskExecutor {
2495
2585
  data: status.result,
2496
2586
  metadata: withServerTaskId(
2497
2587
  this.buildMetadata({
2498
- taskId: metadataTaskId,
2588
+ taskId: correlationId,
2499
2589
  taskName: status.taskType,
2500
2590
  agent,
2501
2591
  responseTimeMs: Date.now() - status.createdAt,
@@ -2512,8 +2602,8 @@ class TaskExecutor {
2512
2602
  error: `tasks/get returned an unrecognizable response (parsed status: "unknown", taskId: "${status.taskId}"). The seller may not implement tasks/get as an AdCP skill, or its response did not match any supported envelope shape (flat AdCP, MCP structuredContent, or A2A DataPart artifact).`,
2513
2603
  metadata: withServerTaskId(
2514
2604
  this.buildMetadata({
2515
- taskId: metadataTaskId,
2516
- taskName: "unknown",
2605
+ taskId: correlationId,
2606
+ taskName: expectedTaskType ?? "unknown",
2517
2607
  agent,
2518
2608
  startTime: pollStartTime,
2519
2609
  status: "failed",
@@ -4497,6 +4587,8 @@ class TaskExecutor {
4497
4587
  task.status = status;
4498
4588
  const taskInfo = {
4499
4589
  taskId: task.taskId,
4590
+ operationId: task.taskId,
4591
+ serverTaskId: task.serverTaskId,
4500
4592
  status,
4501
4593
  taskType: task.taskName,
4502
4594
  createdAt: task.startTime,
@@ -4511,6 +4603,7 @@ class TaskExecutor {
4511
4603
  agent_id: task.agent.id,
4512
4604
  context_id: void 0,
4513
4605
  task_id: task.taskId,
4606
+ server_task_id: task.serverTaskId,
4514
4607
  task_type: task.taskName,
4515
4608
  status,
4516
4609
  payload: result ?? (error ? { error } : void 0),
@@ -4525,7 +4618,7 @@ class TaskExecutor {
4525
4618
  task.options = {};
4526
4619
  delete task.pendingInput;
4527
4620
  setTimeout(() => {
4528
- this.cleanupTerminalTaskInspectionState(taskId);
4621
+ if (this.activeTasks.get(taskId) === task) this.cleanupTerminalTaskInspectionState(taskId);
4529
4622
  }, 3e4).unref();
4530
4623
  }
4531
4624
  }
@@ -6,6 +6,8 @@ type DeadlineTaskOptions<TOptions extends TaskOptions = TaskOptions> = TOptions
6
6
  [TASK_OPERATION_ID]: string;
7
7
  [TASK_DEADLINE_ERROR]?: TaskTimeoutError;
8
8
  };
9
+ /** Give a child dispatch its own correlation id while retaining the parent deadline and cancellation. */
10
+ export declare function childTaskOptions(options?: TaskOptions): TaskOptions;
9
11
  export declare function getTaskOperationId(options: TaskOptions | undefined): string | undefined;
10
12
  export declare function attachTaskDeadlineIdempotencyKey(options: TaskOptions, idempotencyKey: string): void;
11
13
  export declare function attachTaskDeadlineGovernanceRecovery(options: TaskOptions, recovery: GovernanceOutcomeRecovery): void;
@@ -6,6 +6,8 @@ type DeadlineTaskOptions<TOptions extends TaskOptions = TaskOptions> = TOptions
6
6
  [TASK_OPERATION_ID]: string;
7
7
  [TASK_DEADLINE_ERROR]?: TaskTimeoutError;
8
8
  };
9
+ /** Give a child dispatch its own correlation id while retaining the parent deadline and cancellation. */
10
+ export declare function childTaskOptions(options?: TaskOptions): TaskOptions;
9
11
  export declare function getTaskOperationId(options: TaskOptions | undefined): string | undefined;
10
12
  export declare function attachTaskDeadlineIdempotencyKey(options: TaskOptions, idempotencyKey: string): void;
11
13
  export declare function attachTaskDeadlineGovernanceRecovery(options: TaskOptions, recovery: GovernanceOutcomeRecovery): void;
@@ -20,6 +20,7 @@ var task_deadline_exports = {};
20
20
  __export(task_deadline_exports, {
21
21
  attachTaskDeadlineGovernanceRecovery: () => attachTaskDeadlineGovernanceRecovery,
22
22
  attachTaskDeadlineIdempotencyKey: () => attachTaskDeadlineIdempotencyKey,
23
+ childTaskOptions: () => childTaskOptions,
23
24
  getTaskOperationId: () => getTaskOperationId,
24
25
  withTaskDeadline: () => withTaskDeadline
25
26
  });
@@ -29,8 +30,24 @@ var import_errors = require('../errors/index.js');
29
30
  var import_abort = require('../protocols/abort.js');
30
31
  const TASK_OPERATION_ID = /* @__PURE__ */ Symbol("adcp.taskOperationId");
31
32
  const TASK_DEADLINE_ERROR = /* @__PURE__ */ Symbol("adcp.taskDeadlineError");
33
+ function childTaskOptions(options) {
34
+ const child = { ...options };
35
+ delete child.operationId;
36
+ delete child[TASK_OPERATION_ID];
37
+ return child;
38
+ }
32
39
  function getTaskOperationId(options) {
33
- return options?.[TASK_OPERATION_ID];
40
+ const operationId = options?.operationId;
41
+ if (operationId !== void 0 && (typeof operationId !== "string" || !/^(?!\.+$)[A-Za-z0-9_.:-]{1,255}$/.test(operationId) || operationId.startsWith("delivery_route:"))) {
42
+ throw new TypeError(
43
+ "operationId must contain 1\u2013255 letters, digits, underscores, periods, colons, or hyphens; dot-only IDs and the delivery_route: prefix are reserved."
44
+ );
45
+ }
46
+ const internalId = options?.[TASK_OPERATION_ID];
47
+ if (internalId !== void 0 && operationId !== void 0 && internalId !== operationId) {
48
+ throw new TypeError("operationId does not match the operation already assigned to this call.");
49
+ }
50
+ return internalId ?? operationId;
34
51
  }
35
52
  function attachTaskDeadlineIdempotencyKey(options, idempotencyKey) {
36
53
  const error = options[TASK_DEADLINE_ERROR];
@@ -44,7 +61,7 @@ function attachTaskDeadlineGovernanceRecovery(options, recovery) {
44
61
  }
45
62
  async function withTaskDeadline(options, run) {
46
63
  const existing = options;
47
- const taskId = existing?.[TASK_OPERATION_ID] ?? (0, import_node_crypto.randomUUID)();
64
+ const taskId = getTaskOperationId(existing) ?? (0, import_node_crypto.randomUUID)();
48
65
  const timeout = options?.timeout;
49
66
  const baseOptions = { ...options, timeout: void 0, [TASK_OPERATION_ID]: taskId };
50
67
  (0, import_abort.throwIfAborted)(options?.signal);
@@ -68,6 +85,7 @@ async function withTaskDeadline(options, run) {
68
85
  0 && (module.exports = {
69
86
  attachTaskDeadlineGovernanceRecovery,
70
87
  attachTaskDeadlineIdempotencyKey,
88
+ childTaskOptions,
71
89
  getTaskOperationId,
72
90
  withTaskDeadline
73
91
  });
@@ -3,8 +3,24 @@ import { TaskTimeoutError } from "../errors/index.mjs";
3
3
  import { MAX_TIMER_DELAY_MS, throwIfAborted, withAbortSignal } from "../protocols/abort.mjs";
4
4
  const TASK_OPERATION_ID = /* @__PURE__ */ Symbol("adcp.taskOperationId");
5
5
  const TASK_DEADLINE_ERROR = /* @__PURE__ */ Symbol("adcp.taskDeadlineError");
6
+ function childTaskOptions(options) {
7
+ const child = { ...options };
8
+ delete child.operationId;
9
+ delete child[TASK_OPERATION_ID];
10
+ return child;
11
+ }
6
12
  function getTaskOperationId(options) {
7
- return options?.[TASK_OPERATION_ID];
13
+ const operationId = options?.operationId;
14
+ if (operationId !== void 0 && (typeof operationId !== "string" || !/^(?!\.+$)[A-Za-z0-9_.:-]{1,255}$/.test(operationId) || operationId.startsWith("delivery_route:"))) {
15
+ throw new TypeError(
16
+ "operationId must contain 1\u2013255 letters, digits, underscores, periods, colons, or hyphens; dot-only IDs and the delivery_route: prefix are reserved."
17
+ );
18
+ }
19
+ const internalId = options?.[TASK_OPERATION_ID];
20
+ if (internalId !== void 0 && operationId !== void 0 && internalId !== operationId) {
21
+ throw new TypeError("operationId does not match the operation already assigned to this call.");
22
+ }
23
+ return internalId ?? operationId;
8
24
  }
9
25
  function attachTaskDeadlineIdempotencyKey(options, idempotencyKey) {
10
26
  const error = options[TASK_DEADLINE_ERROR];
@@ -18,7 +34,7 @@ function attachTaskDeadlineGovernanceRecovery(options, recovery) {
18
34
  }
19
35
  async function withTaskDeadline(options, run) {
20
36
  const existing = options;
21
- const taskId = existing?.[TASK_OPERATION_ID] ?? randomUUID();
37
+ const taskId = getTaskOperationId(existing) ?? randomUUID();
22
38
  const timeout = options?.timeout;
23
39
  const baseOptions = { ...options, timeout: void 0, [TASK_OPERATION_ID]: taskId };
24
40
  throwIfAborted(options?.signal);
@@ -41,6 +57,7 @@ async function withTaskDeadline(options, run) {
41
57
  export {
42
58
  attachTaskDeadlineGovernanceRecovery,
43
59
  attachTaskDeadlineIdempotencyKey,
60
+ childTaskOptions,
44
61
  getTaskOperationId,
45
62
  withTaskDeadline
46
63
  };
@@ -12,7 +12,9 @@ import type { CheckGovernanceRequest, GovernancePhase, PlannedDelivery } from '.
12
12
  import type { JwksResolver } from '../signing/jwks.mjs';
13
13
  import type { ReplayStore } from '../signing/replay.mjs';
14
14
  import type { RevocationStore } from '../signing/revocation.mjs';
15
+ import type { RevocationDetail } from '../identity-3.3/types.mjs';
15
16
  import { type HttpsJwksResolverOptions } from '../signing/jwks-https.mjs';
17
+ import { type GovernanceKeyArchiveResolver, type JwsArtifactWebBotAuthOptions } from '../identity-3.3/jws-artifacts.mjs';
16
18
  export declare const GOVERNANCE_AUTHORIZATION_CRITICAL_CLAIMS: readonly ["authorized_commitment", "authorized_task", "authorized_payload_hash"];
17
19
  export type GovernanceAuthorizationCriticalClaim = (typeof GOVERNANCE_AUTHORIZATION_CRITICAL_CLAIMS)[number];
18
20
  export type GovernanceEnforcementMode = 'signed_context' | 'online_execution_check';
@@ -51,6 +53,12 @@ export interface GovernanceAuthorizationFailure {
51
53
  ok: false;
52
54
  error: GovernanceAuthorizationErrorCode;
53
55
  message: string;
56
+ /**
57
+ * Set when the failure is an outage (an issuer trust record, key directory,
58
+ * archive or revocation list could not be read), not a verdict on the
59
+ * token: the same token may verify on retry.
60
+ */
61
+ retryable?: true;
54
62
  }
55
63
  export type GovernanceAuthorizationResult = GovernanceAuthorizationSuccess | GovernanceAuthorizationFailure;
56
64
  export interface GovernanceReplayStore {
@@ -127,6 +135,18 @@ export interface VerifyGovernanceAuthorizationOptions {
127
135
  jwks?: JwksResolver;
128
136
  /** When supplied, discover keys from this exact authenticated buyer record. */
129
137
  buyerIdentity?: GovernanceBuyerIdentity;
138
+ /**
139
+ * AdCP 3.3 (`specs/agent-identity-3-3.md` R4): when the trust record derived
140
+ * from the issuer's origin lists that origin, verify the token with a key
141
+ * from the origin's Web Bot Auth key directory (`alg` `EdDSA`, `kid` the
142
+ * key's JWK thumbprint, no `use`/`key_ops`/`adcp_use` checks) and check
143
+ * revocation against the origin's list, never under the 3.2 profile. A
144
+ * trust-record fetch failure rejects the token; it never falls back to 3.2.
145
+ * An issuer no trust record lists stays on the 3.2 profile (`jwks` or
146
+ * `buyerIdentity`). Every other check is unchanged, including the
147
+ * `buyerIdentity` cross-check that the issuer is the buyer's governance agent.
148
+ */
149
+ webBotAuth?: JwsArtifactWebBotAuthOptions;
130
150
  replayStore: GovernanceReplayStore;
131
151
  /** Optional governance-key revocation source. */
132
152
  revocationStore?: RevocationStore;
@@ -187,6 +207,52 @@ export declare function targetDeclaresGovernanceEnforcement(capabilities: unknow
187
207
  export declare function targetDeclaresLegacyGovernanceAwareness(capabilities: unknown, task: string): boolean;
188
208
  /** Verify the published compact-JWS authorization profile and all service bindings. */
189
209
  export declare function verifyGovernanceAuthorization(options: VerifyGovernanceAuthorizationOptions): Promise<GovernanceAuthorizationResult>;
210
+ export interface VerifyHistoricalGovernanceContextOptions {
211
+ token: unknown;
212
+ /** The governance agent the verifier expected to sign, from its own records. */
213
+ expectedIssuer: string;
214
+ /**
215
+ * The WBA components plus the governance key archive at the issuer's
216
+ * origin (`{origin}/.well-known/jwks-archive.json`, `security.mdx`
217
+ * §Revocation), which holds keys rotated out of the directory.
218
+ */
219
+ webBotAuth: JwsArtifactWebBotAuthOptions & {
220
+ archive: GovernanceKeyArchiveResolver;
221
+ };
222
+ }
223
+ export type HistoricalGovernanceContextResult = {
224
+ ok: true;
225
+ claims: GovernanceAuthorizationClaims;
226
+ protectedHeader: Record<string, unknown>;
227
+ /** The key was in the issuer's current directory, or only in its archive. */
228
+ keySource: 'directory' | 'archive';
229
+ /**
230
+ * The issuer origin's revocation status for the signing key: `current`
231
+ * (listed, not revoked), `absent` (the origin serves no list) or
232
+ * `revoked`. Online verification rejects every token a revoked key
233
+ * signed; an audit decides what a revocation means for a retained token
234
+ * from its detail (R9: only `keyCompromise` exposes, from its
235
+ * `invalidity_date`).
236
+ */
237
+ revocation: 'current' | 'absent' | 'revoked';
238
+ revocationDetail?: RevocationDetail;
239
+ } | (GovernanceAuthorizationFailure & {
240
+ /** Set when the issuer is on the 3.2 profile, which this function does not verify. */
241
+ profile?: '3.2';
242
+ });
243
+ /**
244
+ * Historical (audit) verification of a retained governance context from a
245
+ * Web Bot Auth identity (R4): the key comes from the issuer origin's key
246
+ * directory or, once rotated out, from its governance key archive. Checks the
247
+ * token's shape, `typ`, `crit`, issuer, `alg` `EdDSA`, `kid` thumbprint and
248
+ * signature, and reports the origin's revocation status and detail for the
249
+ * key instead of rejecting a revoked one. It does not check expiry, audience,
250
+ * replay or the request binding, which only an online verification can.
251
+ *
252
+ * Profile selection is the same as online: an issuer whose origin no trust
253
+ * record lists is on the 3.2 profile, reported as `profile: '3.2'`.
254
+ */
255
+ export declare function verifyHistoricalGovernanceContext(options: VerifyHistoricalGovernanceContextOptions): Promise<HistoricalGovernanceContextResult>;
190
256
  export declare class GovernanceAuthorizationError extends Error {
191
257
  readonly code: GovernanceAuthorizationErrorCode;
192
258
  constructor(result: GovernanceAuthorizationFailure);
@@ -12,7 +12,9 @@ import type { CheckGovernanceRequest, GovernancePhase, PlannedDelivery } from '.
12
12
  import type { JwksResolver } from '../signing/jwks';
13
13
  import type { ReplayStore } from '../signing/replay';
14
14
  import type { RevocationStore } from '../signing/revocation';
15
+ import type { RevocationDetail } from '../identity-3.3/types';
15
16
  import { type HttpsJwksResolverOptions } from '../signing/jwks-https';
17
+ import { type GovernanceKeyArchiveResolver, type JwsArtifactWebBotAuthOptions } from '../identity-3.3/jws-artifacts';
16
18
  export declare const GOVERNANCE_AUTHORIZATION_CRITICAL_CLAIMS: readonly ["authorized_commitment", "authorized_task", "authorized_payload_hash"];
17
19
  export type GovernanceAuthorizationCriticalClaim = (typeof GOVERNANCE_AUTHORIZATION_CRITICAL_CLAIMS)[number];
18
20
  export type GovernanceEnforcementMode = 'signed_context' | 'online_execution_check';
@@ -51,6 +53,12 @@ export interface GovernanceAuthorizationFailure {
51
53
  ok: false;
52
54
  error: GovernanceAuthorizationErrorCode;
53
55
  message: string;
56
+ /**
57
+ * Set when the failure is an outage (an issuer trust record, key directory,
58
+ * archive or revocation list could not be read), not a verdict on the
59
+ * token: the same token may verify on retry.
60
+ */
61
+ retryable?: true;
54
62
  }
55
63
  export type GovernanceAuthorizationResult = GovernanceAuthorizationSuccess | GovernanceAuthorizationFailure;
56
64
  export interface GovernanceReplayStore {
@@ -127,6 +135,18 @@ export interface VerifyGovernanceAuthorizationOptions {
127
135
  jwks?: JwksResolver;
128
136
  /** When supplied, discover keys from this exact authenticated buyer record. */
129
137
  buyerIdentity?: GovernanceBuyerIdentity;
138
+ /**
139
+ * AdCP 3.3 (`specs/agent-identity-3-3.md` R4): when the trust record derived
140
+ * from the issuer's origin lists that origin, verify the token with a key
141
+ * from the origin's Web Bot Auth key directory (`alg` `EdDSA`, `kid` the
142
+ * key's JWK thumbprint, no `use`/`key_ops`/`adcp_use` checks) and check
143
+ * revocation against the origin's list, never under the 3.2 profile. A
144
+ * trust-record fetch failure rejects the token; it never falls back to 3.2.
145
+ * An issuer no trust record lists stays on the 3.2 profile (`jwks` or
146
+ * `buyerIdentity`). Every other check is unchanged, including the
147
+ * `buyerIdentity` cross-check that the issuer is the buyer's governance agent.
148
+ */
149
+ webBotAuth?: JwsArtifactWebBotAuthOptions;
130
150
  replayStore: GovernanceReplayStore;
131
151
  /** Optional governance-key revocation source. */
132
152
  revocationStore?: RevocationStore;
@@ -187,6 +207,52 @@ export declare function targetDeclaresGovernanceEnforcement(capabilities: unknow
187
207
  export declare function targetDeclaresLegacyGovernanceAwareness(capabilities: unknown, task: string): boolean;
188
208
  /** Verify the published compact-JWS authorization profile and all service bindings. */
189
209
  export declare function verifyGovernanceAuthorization(options: VerifyGovernanceAuthorizationOptions): Promise<GovernanceAuthorizationResult>;
210
+ export interface VerifyHistoricalGovernanceContextOptions {
211
+ token: unknown;
212
+ /** The governance agent the verifier expected to sign, from its own records. */
213
+ expectedIssuer: string;
214
+ /**
215
+ * The WBA components plus the governance key archive at the issuer's
216
+ * origin (`{origin}/.well-known/jwks-archive.json`, `security.mdx`
217
+ * §Revocation), which holds keys rotated out of the directory.
218
+ */
219
+ webBotAuth: JwsArtifactWebBotAuthOptions & {
220
+ archive: GovernanceKeyArchiveResolver;
221
+ };
222
+ }
223
+ export type HistoricalGovernanceContextResult = {
224
+ ok: true;
225
+ claims: GovernanceAuthorizationClaims;
226
+ protectedHeader: Record<string, unknown>;
227
+ /** The key was in the issuer's current directory, or only in its archive. */
228
+ keySource: 'directory' | 'archive';
229
+ /**
230
+ * The issuer origin's revocation status for the signing key: `current`
231
+ * (listed, not revoked), `absent` (the origin serves no list) or
232
+ * `revoked`. Online verification rejects every token a revoked key
233
+ * signed; an audit decides what a revocation means for a retained token
234
+ * from its detail (R9: only `keyCompromise` exposes, from its
235
+ * `invalidity_date`).
236
+ */
237
+ revocation: 'current' | 'absent' | 'revoked';
238
+ revocationDetail?: RevocationDetail;
239
+ } | (GovernanceAuthorizationFailure & {
240
+ /** Set when the issuer is on the 3.2 profile, which this function does not verify. */
241
+ profile?: '3.2';
242
+ });
243
+ /**
244
+ * Historical (audit) verification of a retained governance context from a
245
+ * Web Bot Auth identity (R4): the key comes from the issuer origin's key
246
+ * directory or, once rotated out, from its governance key archive. Checks the
247
+ * token's shape, `typ`, `crit`, issuer, `alg` `EdDSA`, `kid` thumbprint and
248
+ * signature, and reports the origin's revocation status and detail for the
249
+ * key instead of rejecting a revoked one. It does not check expiry, audience,
250
+ * replay or the request binding, which only an online verification can.
251
+ *
252
+ * Profile selection is the same as online: an issuer whose origin no trust
253
+ * record lists is on the 3.2 profile, reported as `profile: '3.2'`.
254
+ */
255
+ export declare function verifyHistoricalGovernanceContext(options: VerifyHistoricalGovernanceContextOptions): Promise<HistoricalGovernanceContextResult>;
190
256
  export declare class GovernanceAuthorizationError extends Error {
191
257
  readonly code: GovernanceAuthorizationErrorCode;
192
258
  constructor(result: GovernanceAuthorizationFailure);