@adcp/sdk 16.0.0 → 16.1.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.
- package/README.md +52 -0
- package/bin/adcp-identity.js +3 -2
- package/dist/lib/client/core.d.mts +1 -1
- package/dist/lib/client/core.d.ts +1 -1
- package/dist/lib/core/AgentClient.d.mts +8 -1
- package/dist/lib/core/AgentClient.d.ts +8 -1
- package/dist/lib/core/AgentClient.js +10 -0
- package/dist/lib/core/AgentClient.mjs +10 -0
- package/dist/lib/core/AsyncHandler.d.mts +2 -0
- package/dist/lib/core/AsyncHandler.d.ts +2 -0
- package/dist/lib/core/AsyncHandler.js +1 -1
- package/dist/lib/core/AsyncHandler.mjs +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +32 -0
- package/dist/lib/core/ConversationTypes.d.ts +32 -0
- package/dist/lib/core/SingleAgentClient.d.mts +11 -1
- package/dist/lib/core/SingleAgentClient.d.ts +11 -1
- package/dist/lib/core/SingleAgentClient.js +25 -6
- package/dist/lib/core/SingleAgentClient.mjs +26 -7
- package/dist/lib/core/TaskExecutor.d.mts +3 -1
- package/dist/lib/core/TaskExecutor.d.ts +3 -1
- package/dist/lib/core/TaskExecutor.js +127 -34
- package/dist/lib/core/TaskExecutor.mjs +127 -34
- package/dist/lib/core/task-deadline.d.mts +2 -0
- package/dist/lib/core/task-deadline.d.ts +2 -0
- package/dist/lib/core/task-deadline.js +20 -2
- package/dist/lib/core/task-deadline.mjs +19 -2
- package/dist/lib/identity-3.3/schemas.draft.js +1 -1
- package/dist/lib/identity-3.3/schemas.draft.mjs +1 -1
- package/dist/lib/identity-3.3/trust.d.mts +8 -0
- package/dist/lib/identity-3.3/trust.d.ts +8 -0
- package/dist/lib/identity-3.3/trust.js +41 -12
- package/dist/lib/identity-3.3/trust.mjs +40 -12
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/server/account-sync-contract.js +4 -1
- package/dist/lib/server/account-sync-contract.mjs +4 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +22 -17
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +22 -17
- package/dist/lib/signing/web-bot-auth.js +20 -5
- package/dist/lib/signing/web-bot-auth.mjs +21 -6
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/v2/projection/catalog.d.mts +6 -0
- package/dist/lib/v2/projection/catalog.d.ts +6 -0
- package/dist/lib/v2/projection/catalog.js +5 -0
- package/dist/lib/v2/projection/catalog.mjs +4 -0
- package/dist/lib/v2/projection/v1-to-v2.js +3 -2
- package/dist/lib/v2/projection/v1-to-v2.mjs +4 -3
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +12 -2
- package/docs/llms.txt +3 -3
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +21 -13
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
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:
|
|
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(
|
|
2542
|
+
errorInstance: this.buildErrorInstance(correlationId, asyncResultErr),
|
|
2453
2543
|
correlationId: extractCorrelationId(status.result),
|
|
2454
2544
|
metadata: withServerTaskId(
|
|
2455
2545
|
this.buildMetadata({
|
|
2456
|
-
taskId:
|
|
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(
|
|
2566
|
+
errorInstance: this.buildErrorInstance(correlationId, asyncFailedErr),
|
|
2477
2567
|
correlationId: extractCorrelationId(status.result),
|
|
2478
2568
|
metadata: withServerTaskId(
|
|
2479
2569
|
this.buildMetadata({
|
|
2480
|
-
taskId:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
};
|
|
@@ -242,7 +242,7 @@ var schemas_draft_default = [
|
|
|
242
242
|
properties: {
|
|
243
243
|
grantee: {
|
|
244
244
|
$ref: "#/definitions/domain",
|
|
245
|
-
description: "
|
|
245
|
+
description: "Registrable domain (eTLD+1) of the organization receiving the grant, under the pinned Public Suffix List snapshot, ICANN and private sections (spec R3a step 1). Its agents resolve to this domain through its own trust.json. A host below a registrable domain (t1.vendor.example) is not one, and neither is a public suffix itself (github.io); alice.github.io is one, because github.io is in the private section. A grant whose grantee is not a registrable domain covers no claim, and its scopes still count for exclusive_grants, as L2 \xA7Grants in trust records describes (docs/building/by-layer/L2/accounts-and-agents.mdx). A pattern can't evaluate the Public Suffix List, so verifiers apply the rule. To limit a grant to some of the grantee's agents, list them in agents[]."
|
|
246
246
|
},
|
|
247
247
|
agents: {
|
|
248
248
|
type: "array",
|
|
@@ -219,7 +219,7 @@ var schemas_draft_default = [
|
|
|
219
219
|
properties: {
|
|
220
220
|
grantee: {
|
|
221
221
|
$ref: "#/definitions/domain",
|
|
222
|
-
description: "
|
|
222
|
+
description: "Registrable domain (eTLD+1) of the organization receiving the grant, under the pinned Public Suffix List snapshot, ICANN and private sections (spec R3a step 1). Its agents resolve to this domain through its own trust.json. A host below a registrable domain (t1.vendor.example) is not one, and neither is a public suffix itself (github.io); alice.github.io is one, because github.io is in the private section. A grant whose grantee is not a registrable domain covers no claim, and its scopes still count for exclusive_grants, as L2 \xA7Grants in trust records describes (docs/building/by-layer/L2/accounts-and-agents.mdx). A pattern can't evaluate the Public Suffix List, so verifiers apply the rule. To limit a grant to some of the grantee's agents, list them in agents[]."
|
|
223
223
|
},
|
|
224
224
|
agents: {
|
|
225
225
|
type: "array",
|
|
@@ -34,6 +34,14 @@ export declare class TrustMembershipError extends IdentityAuthorizationError {
|
|
|
34
34
|
}
|
|
35
35
|
/** Schema plus semantic checks; publication never includes identity keys. */
|
|
36
36
|
export declare function validateTrustRecord(value: unknown): asserts value is TrustRecord;
|
|
37
|
+
/**
|
|
38
|
+
* Publication rules beyond the schema: every grant's `grantee` is a
|
|
39
|
+
* registrable domain and every `agents[].url` parses as an agent origin. A
|
|
40
|
+
* relying party ignores a grant that breaks either, so publishing one grants
|
|
41
|
+
* nothing while still making `grants[]` exhaustive for its scopes.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertPublishableTrustRecord(record: TrustRecord): void;
|
|
44
|
+
/** Validate ({@link assertPublishableTrustRecord}) and copy a trust record for publication. */
|
|
37
45
|
export declare function createTrustRecord(record: TrustRecord): TrustRecord;
|
|
38
46
|
export type TrustFetchResult = {
|
|
39
47
|
status: 'present';
|
|
@@ -34,6 +34,14 @@ export declare class TrustMembershipError extends IdentityAuthorizationError {
|
|
|
34
34
|
}
|
|
35
35
|
/** Schema plus semantic checks; publication never includes identity keys. */
|
|
36
36
|
export declare function validateTrustRecord(value: unknown): asserts value is TrustRecord;
|
|
37
|
+
/**
|
|
38
|
+
* Publication rules beyond the schema: every grant's `grantee` is a
|
|
39
|
+
* registrable domain and every `agents[].url` parses as an agent origin. A
|
|
40
|
+
* relying party ignores a grant that breaks either, so publishing one grants
|
|
41
|
+
* nothing while still making `grants[]` exhaustive for its scopes.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertPublishableTrustRecord(record: TrustRecord): void;
|
|
44
|
+
/** Validate ({@link assertPublishableTrustRecord}) and copy a trust record for publication. */
|
|
37
45
|
export declare function createTrustRecord(record: TrustRecord): TrustRecord;
|
|
38
46
|
export type TrustFetchResult = {
|
|
39
47
|
status: 'present';
|
|
@@ -32,6 +32,7 @@ __export(trust_exports, {
|
|
|
32
32
|
TrustEvaluator: () => TrustEvaluator,
|
|
33
33
|
TrustMembershipError: () => TrustMembershipError,
|
|
34
34
|
TrustRecordUnavailableError: () => TrustRecordUnavailableError,
|
|
35
|
+
assertPublishableTrustRecord: () => assertPublishableTrustRecord,
|
|
35
36
|
authorizeDemandGrant: () => authorizeDemandGrant,
|
|
36
37
|
createTrustRecord: () => createTrustRecord,
|
|
37
38
|
validateIdentity33Schema: () => validateIdentity33Schema,
|
|
@@ -110,8 +111,34 @@ function validateTrustRecord(value) {
|
|
|
110
111
|
origins.add(origin);
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
|
-
function
|
|
114
|
+
function registrableGrantee(grantee) {
|
|
115
|
+
try {
|
|
116
|
+
return (0, import_etld.eTldPlusOne)(grantee) === grantee ? grantee : void 0;
|
|
117
|
+
} catch {
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function grantAgentOrigin(url) {
|
|
122
|
+
try {
|
|
123
|
+
return (0, import_canonicalize.canonicalAgentOrigin)(url);
|
|
124
|
+
} catch {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function assertPublishableTrustRecord(record) {
|
|
114
129
|
validateTrustRecord(record);
|
|
130
|
+
for (const grant of record.grants ?? []) {
|
|
131
|
+
if (registrableGrantee(grant.grantee) === void 0)
|
|
132
|
+
throw new TypeError(
|
|
133
|
+
`Grant grantee "${grant.grantee}" is not a registrable domain; name the organization's registrable domain and narrow with agents[]`
|
|
134
|
+
);
|
|
135
|
+
for (const agent of grant.agents ?? [])
|
|
136
|
+
if (grantAgentOrigin(agent.url) === void 0)
|
|
137
|
+
throw new TypeError(`Grant agents[] URL "${agent.url}" is not a valid agent origin`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function createTrustRecord(record) {
|
|
141
|
+
assertPublishableTrustRecord(record);
|
|
115
142
|
return structuredClone(record);
|
|
116
143
|
}
|
|
117
144
|
class TrustEvaluator {
|
|
@@ -308,13 +335,15 @@ function authorityGroup(operator) {
|
|
|
308
335
|
const provider = (0, import_tldts.parse)(operator).domain;
|
|
309
336
|
return `private:${provider ?? operator}`;
|
|
310
337
|
}
|
|
338
|
+
function grantsScope(grant, scope) {
|
|
339
|
+
return grant.scopes.some(
|
|
340
|
+
(granted) => granted === "*" || granted === scope || granted === "adcp:all" && scope.startsWith("adcp:")
|
|
341
|
+
);
|
|
342
|
+
}
|
|
311
343
|
function grantMatches(grant, target, claim, now) {
|
|
312
|
-
if ((
|
|
313
|
-
if (!grant.
|
|
314
|
-
|
|
315
|
-
))
|
|
316
|
-
return false;
|
|
317
|
-
if (grant.agents && !grant.agents.some((agent) => (0, import_canonicalize.canonicalAgentOrigin)(agent.url) === (0, import_canonicalize.canonicalAgentOrigin)(claim.signerOrigin)))
|
|
344
|
+
if (registrableGrantee(grant.grantee) !== target) return false;
|
|
345
|
+
if (!grantsScope(grant, claim.scope)) return false;
|
|
346
|
+
if (grant.agents && !grant.agents.some((agent) => grantAgentOrigin(agent.url) === (0, import_canonicalize.canonicalAgentOrigin)(claim.signerOrigin)))
|
|
318
347
|
return false;
|
|
319
348
|
const brands = grant.profiles?.adcp?.brands;
|
|
320
349
|
if (brands && !brands.includes("*") && (!claim.brand.brand_id || !brands.includes(claim.brand.brand_id)))
|
|
@@ -361,11 +390,10 @@ async function authorizeDemandGrant(claim, options) {
|
|
|
361
390
|
if (second) return result("trust", [brand, account, operator], [first[0], second]);
|
|
362
391
|
}
|
|
363
392
|
}
|
|
364
|
-
const covered = (record.record.grants ?? []).some(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
);
|
|
393
|
+
const covered = (record.record.grants ?? []).some((g) => {
|
|
394
|
+
const grantee = registrableGrantee(g.grantee);
|
|
395
|
+
return grantee !== void 0 && (grantee === account || selfOperated && grantee === operator) && grantsScope(g, claim.scope);
|
|
396
|
+
});
|
|
369
397
|
if (!covered && !record.record.exclusive_grants && await options.sellerAuthorization?.(claim))
|
|
370
398
|
return result("seller", [brand, account, operator]);
|
|
371
399
|
throw new IdentityAuthorizationError("grant_absent");
|
|
@@ -376,6 +404,7 @@ async function authorizeDemandGrant(claim, options) {
|
|
|
376
404
|
TrustEvaluator,
|
|
377
405
|
TrustMembershipError,
|
|
378
406
|
TrustRecordUnavailableError,
|
|
407
|
+
assertPublishableTrustRecord,
|
|
379
408
|
authorizeDemandGrant,
|
|
380
409
|
createTrustRecord,
|
|
381
410
|
validateIdentity33Schema,
|