deepline 0.3.19 → 0.3.21
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/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +123 -29
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-response-contract.ts +51 -4
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +29 -2
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +60 -2
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +36 -5
- package/dist/cli/index.js +55 -5
- package/dist/cli/index.mjs +55 -5
- package/dist/{compiler-manifest-Bd0O94yZ.d.mts → compiler-manifest-DwYe2C2S.d.mts} +24 -1
- package/dist/{compiler-manifest-Bd0O94yZ.d.ts → compiler-manifest-DwYe2C2S.d.ts} +24 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/install-integrity.json +2 -2
- package/dist/plays/bundle-play-file.d.mts +9 -3
- package/dist/plays/bundle-play-file.d.ts +9 -3
- package/dist/plays/bundle-play-file.mjs +85 -7
- package/package.json +1 -1
|
@@ -192,7 +192,7 @@ export const SDK_RELEASE = {
|
|
|
192
192
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
193
193
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
194
194
|
// getters keep their established compatibility behavior.
|
|
195
|
-
version: '0.3.
|
|
195
|
+
version: '0.3.21',
|
|
196
196
|
updateSummary:
|
|
197
197
|
'New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.',
|
|
198
198
|
contracts: {
|
|
@@ -376,8 +376,10 @@ type InlineCompositionStore = {
|
|
|
376
376
|
toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion;
|
|
377
377
|
/** Immutable authoring semantics pinned by the child play artifact. */
|
|
378
378
|
authoringContractEdition: PlayAuthoringContractEdition;
|
|
379
|
-
/**
|
|
379
|
+
/** Execute response contract pinned by the child artifact. */
|
|
380
380
|
toolResponseContract: ToolResponseContract;
|
|
381
|
+
/** Explicit response-transform revision used for durable receipt identity. */
|
|
382
|
+
toolResponseReceiptRevision?: string;
|
|
381
383
|
};
|
|
382
384
|
const inlineCompositionContext =
|
|
383
385
|
new AsyncLocalStorage<InlineCompositionStore>();
|
|
@@ -1340,6 +1342,12 @@ type ToolExecutionApiOptions = {
|
|
|
1340
1342
|
timeoutMs?: number;
|
|
1341
1343
|
durableCallReceiptKey?: string | null;
|
|
1342
1344
|
executionAuthScopeDigest?: string | null;
|
|
1345
|
+
/**
|
|
1346
|
+
* Historical receipt identity used for the API's provider/billing
|
|
1347
|
+
* idempotency receipt. This can differ from the worker-owned durable cache
|
|
1348
|
+
* receipt while a response contract migrates.
|
|
1349
|
+
*/
|
|
1350
|
+
providerIdempotencyReceiptKey?: string | null;
|
|
1343
1351
|
providerIdempotencyKey?: string | null;
|
|
1344
1352
|
receiptLeaseExpiresAt?: string | null;
|
|
1345
1353
|
beforeProviderCall?: () => Promise<void> | void;
|
|
@@ -2648,6 +2656,18 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2648
2656
|
);
|
|
2649
2657
|
}
|
|
2650
2658
|
|
|
2659
|
+
/**
|
|
2660
|
+
* Only explicitly declared response transformations affect receipt reuse.
|
|
2661
|
+
* Missing preserves historical artifact/receipt identity, while the public
|
|
2662
|
+
* response header still normalizes it to the legacy V2 behavior above.
|
|
2663
|
+
*/
|
|
2664
|
+
private get currentToolResponseReceiptRevision(): string | undefined {
|
|
2665
|
+
if (this.activeInlineComposition) {
|
|
2666
|
+
return this.activeInlineComposition.toolResponseReceiptRevision;
|
|
2667
|
+
}
|
|
2668
|
+
return this.#options.toolResponseReceiptRevision;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2651
2671
|
private get currentAuthoringContractEdition(): PlayAuthoringContractEdition {
|
|
2652
2672
|
return (
|
|
2653
2673
|
this.activeInlineComposition?.authoringContractEdition ??
|
|
@@ -4026,13 +4046,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
4026
4046
|
);
|
|
4027
4047
|
}
|
|
4028
4048
|
|
|
4029
|
-
private async durableToolCallCacheKeyForScope(
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4049
|
+
private async durableToolCallCacheKeyForScope(
|
|
4050
|
+
input: {
|
|
4051
|
+
toolId: string;
|
|
4052
|
+
requestInput: Record<string, unknown>;
|
|
4053
|
+
executionAuthScopeDigest?: string | null;
|
|
4054
|
+
staleAfterSeconds?: number | null;
|
|
4055
|
+
playLocalScope?: string | null;
|
|
4056
|
+
},
|
|
4057
|
+
toolResponseReceiptRevision: string | null | undefined = this
|
|
4058
|
+
.currentToolResponseReceiptRevision,
|
|
4059
|
+
): Promise<string> {
|
|
4036
4060
|
const providerActionVersion =
|
|
4037
4061
|
(await this.#options.getToolActionCacheVersion?.(input.toolId))?.trim() ??
|
|
4038
4062
|
'';
|
|
@@ -4051,21 +4075,29 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
4051
4075
|
executionAuthScopeDigest,
|
|
4052
4076
|
}),
|
|
4053
4077
|
providerActionVersion,
|
|
4078
|
+
toolResponseReceiptRevision,
|
|
4054
4079
|
staleAfterSeconds: input.staleAfterSeconds,
|
|
4055
4080
|
cacheEpochMs: this.durableCallCacheEpochMs,
|
|
4056
4081
|
});
|
|
4057
4082
|
}
|
|
4058
4083
|
|
|
4059
|
-
private async durableToolCallCacheKey(
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4084
|
+
private async durableToolCallCacheKey(
|
|
4085
|
+
input: {
|
|
4086
|
+
toolId: string;
|
|
4087
|
+
requestInput: Record<string, unknown>;
|
|
4088
|
+
executionAuthScopeDigest?: string | null;
|
|
4089
|
+
staleAfterSeconds?: number | null;
|
|
4090
|
+
},
|
|
4091
|
+
toolResponseReceiptRevision: string | null | undefined = this
|
|
4092
|
+
.currentToolResponseReceiptRevision,
|
|
4093
|
+
): Promise<string> {
|
|
4094
|
+
return await this.durableToolCallCacheKeyForScope(
|
|
4095
|
+
{
|
|
4096
|
+
...input,
|
|
4097
|
+
playLocalScope: this.currentGovernance.currentPlayId,
|
|
4098
|
+
},
|
|
4099
|
+
toolResponseReceiptRevision,
|
|
4100
|
+
);
|
|
4069
4101
|
}
|
|
4070
4102
|
|
|
4071
4103
|
private async resolveToolAuthScopeDigest(
|
|
@@ -8968,6 +9000,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8968
9000
|
executionAuthScopeDigest,
|
|
8969
9001
|
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
8970
9002
|
});
|
|
9003
|
+
let providerIdempotencyKeyBase = await this.durableToolCallCacheKey(
|
|
9004
|
+
{
|
|
9005
|
+
toolId,
|
|
9006
|
+
requestInput: input,
|
|
9007
|
+
executionAuthScopeDigest,
|
|
9008
|
+
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
9009
|
+
},
|
|
9010
|
+
null,
|
|
9011
|
+
);
|
|
8971
9012
|
const checkpointCacheKeys = [durableCacheKey];
|
|
8972
9013
|
|
|
8973
9014
|
const executeTool = async (context?: {
|
|
@@ -9091,9 +9132,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9091
9132
|
callKey: normalizedKey,
|
|
9092
9133
|
}),
|
|
9093
9134
|
executionAuthScopeDigest,
|
|
9135
|
+
providerIdempotencyReceiptKey: cacheableToolResult
|
|
9136
|
+
? providerIdempotencyKeyBase
|
|
9137
|
+
: physicalDirectKey,
|
|
9094
9138
|
providerIdempotencyKey: cacheableToolResult
|
|
9095
9139
|
? this.providerIdempotencyKeyForToolCall({
|
|
9096
|
-
cacheKey:
|
|
9140
|
+
cacheKey: providerIdempotencyKeyBase,
|
|
9097
9141
|
force: toolCachePolicy.force,
|
|
9098
9142
|
leaseId: directReceiptLeaseId,
|
|
9099
9143
|
logicalCallId,
|
|
@@ -9241,6 +9285,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9241
9285
|
this.enqueueToolCall({
|
|
9242
9286
|
callId,
|
|
9243
9287
|
cacheKey: toolResultCacheKey,
|
|
9288
|
+
providerIdempotencyKeyBase,
|
|
9244
9289
|
cacheable: cacheableToolResult,
|
|
9245
9290
|
receiptKey: cacheableToolResult ? durableCacheKey : null,
|
|
9246
9291
|
executionAuthScopeDigest,
|
|
@@ -9361,6 +9406,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9361
9406
|
executionAuthScopeDigest,
|
|
9362
9407
|
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
9363
9408
|
});
|
|
9409
|
+
providerIdempotencyKeyBase = await this.durableToolCallCacheKey(
|
|
9410
|
+
{
|
|
9411
|
+
toolId,
|
|
9412
|
+
requestInput: input,
|
|
9413
|
+
executionAuthScopeDigest,
|
|
9414
|
+
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
9415
|
+
},
|
|
9416
|
+
null,
|
|
9417
|
+
);
|
|
9364
9418
|
checkpointCacheKeys[0] = durableCacheKey;
|
|
9365
9419
|
}
|
|
9366
9420
|
}
|
|
@@ -9523,14 +9577,18 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9523
9577
|
`Unable to resolve play "${resolvedName}" for ctx.runPlay(...).`,
|
|
9524
9578
|
);
|
|
9525
9579
|
}
|
|
9526
|
-
const
|
|
9580
|
+
const childCompatibilitySnapshot =
|
|
9527
9581
|
resolvedPlay.contractSnapshot?.compatibility ??
|
|
9528
|
-
|
|
9529
|
-
|
|
9582
|
+
resolvedPlay.artifact?.compatibility ??
|
|
9583
|
+
buildPlayContractCompatibility();
|
|
9584
|
+
const childCompatibility = normalizePlayContractCompatibility(
|
|
9585
|
+
childCompatibilitySnapshot,
|
|
9530
9586
|
);
|
|
9531
9587
|
const childToolErrorSchemaVersion =
|
|
9532
9588
|
childCompatibility.toolErrorSchemaVersion;
|
|
9533
9589
|
const childToolResponseContract = childCompatibility.toolResponseContract;
|
|
9590
|
+
const childToolResponseReceiptRevision =
|
|
9591
|
+
childCompatibility.toolResponseReceiptRevision;
|
|
9534
9592
|
const childExecutionDecision = resolveChildExecutionStrategy({
|
|
9535
9593
|
pipeline: resolvedPlay.staticPipeline,
|
|
9536
9594
|
timeoutMs: options?.timeoutMs,
|
|
@@ -9604,6 +9662,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9604
9662
|
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
9605
9663
|
toolErrorSchemaVersion: childToolErrorSchemaVersion,
|
|
9606
9664
|
toolResponseContract: childToolResponseContract,
|
|
9665
|
+
toolResponseReceiptRevision: childToolResponseReceiptRevision,
|
|
9607
9666
|
authoringContractEdition:
|
|
9608
9667
|
childCompatibility.authoringContractEdition,
|
|
9609
9668
|
},
|
|
@@ -10882,12 +10941,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10882
10941
|
durableCallReceiptKey: receiptKey,
|
|
10883
10942
|
playNodeScope: playNodeScopeForToolCallRequest(owner),
|
|
10884
10943
|
executionAuthScopeDigest: owner.executionAuthScopeDigest,
|
|
10944
|
+
providerIdempotencyReceiptKey:
|
|
10945
|
+
owner.providerIdempotencyKeyBase ?? owner.cacheKey,
|
|
10885
10946
|
receiptLeaseExpiresAt: owner.receiptLeaseExpiresAt,
|
|
10886
10947
|
heartbeatReceipt: () =>
|
|
10887
10948
|
this.renewRuntimeToolReceiptOwnership([owner]),
|
|
10888
10949
|
providerIdempotencyKey:
|
|
10889
10950
|
this.providerIdempotencyKeyForToolCall({
|
|
10890
|
-
cacheKey:
|
|
10951
|
+
cacheKey:
|
|
10952
|
+
owner.providerIdempotencyKeyBase ?? owner.cacheKey,
|
|
10891
10953
|
force: owner.force === true,
|
|
10892
10954
|
leaseId: owner.receiptLeaseId,
|
|
10893
10955
|
}),
|
|
@@ -11219,6 +11281,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11219
11281
|
const receiptKeys = batch.memberRequests.map(
|
|
11220
11282
|
(request) => request.cacheKey,
|
|
11221
11283
|
);
|
|
11284
|
+
const providerIdempotencyReceiptKeys =
|
|
11285
|
+
batch.memberRequests.map(
|
|
11286
|
+
(request) =>
|
|
11287
|
+
request.providerIdempotencyKeyBase ?? request.cacheKey,
|
|
11288
|
+
);
|
|
11222
11289
|
const aggregateReceiptKey =
|
|
11223
11290
|
buildDurableToolAggregateReceiptKey({
|
|
11224
11291
|
receiptKeys,
|
|
@@ -11228,6 +11295,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11228
11295
|
toolId: batch.batchOperation,
|
|
11229
11296
|
}),
|
|
11230
11297
|
});
|
|
11298
|
+
const aggregateProviderIdempotencyReceiptKey =
|
|
11299
|
+
buildDurableToolAggregateReceiptKey({
|
|
11300
|
+
receiptKeys: providerIdempotencyReceiptKeys,
|
|
11301
|
+
prefix: 'batch',
|
|
11302
|
+
aggregateReceiptPrefix: buildDurableToolReceiptPrefix({
|
|
11303
|
+
orgId: this.#options.orgId,
|
|
11304
|
+
toolId: batch.batchOperation,
|
|
11305
|
+
}),
|
|
11306
|
+
});
|
|
11231
11307
|
let releaseToolSlot: () => void = () => undefined;
|
|
11232
11308
|
try {
|
|
11233
11309
|
const execution = await this.callToolExecutionAPI(
|
|
@@ -11247,14 +11323,19 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11247
11323
|
executionAuthScopeDigest:
|
|
11248
11324
|
batch.memberRequests[0]?.executionAuthScopeDigest ??
|
|
11249
11325
|
null,
|
|
11326
|
+
providerIdempotencyReceiptKey:
|
|
11327
|
+
aggregateProviderIdempotencyReceiptKey,
|
|
11250
11328
|
providerIdempotencyKey:
|
|
11251
11329
|
buildDurableToolAggregateProviderIdempotencyKey({
|
|
11252
|
-
aggregateReceiptKey
|
|
11253
|
-
|
|
11330
|
+
aggregateReceiptKey:
|
|
11331
|
+
aggregateProviderIdempotencyReceiptKey,
|
|
11332
|
+
receiptKeys: providerIdempotencyReceiptKeys,
|
|
11254
11333
|
providerIdempotencyKeys: batch.memberRequests.map(
|
|
11255
11334
|
(request) =>
|
|
11256
11335
|
this.providerIdempotencyKeyForToolCall({
|
|
11257
|
-
cacheKey:
|
|
11336
|
+
cacheKey:
|
|
11337
|
+
request.providerIdempotencyKeyBase ??
|
|
11338
|
+
request.cacheKey,
|
|
11258
11339
|
force: request.force === true,
|
|
11259
11340
|
leaseId: request.receiptLeaseId,
|
|
11260
11341
|
}),
|
|
@@ -11481,9 +11562,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11481
11562
|
durableCallReceiptKey: request.receiptKey,
|
|
11482
11563
|
executionAuthScopeDigest:
|
|
11483
11564
|
request.executionAuthScopeDigest,
|
|
11565
|
+
providerIdempotencyReceiptKey:
|
|
11566
|
+
request.providerIdempotencyKeyBase ??
|
|
11567
|
+
request.receiptKey,
|
|
11484
11568
|
providerIdempotencyKey:
|
|
11485
11569
|
this.providerIdempotencyKeyForToolCall({
|
|
11486
|
-
cacheKey:
|
|
11570
|
+
cacheKey:
|
|
11571
|
+
request.providerIdempotencyKeyBase ??
|
|
11572
|
+
request.receiptKey,
|
|
11487
11573
|
force: request.force === true,
|
|
11488
11574
|
leaseId: request.receiptLeaseId,
|
|
11489
11575
|
}),
|
|
@@ -11783,6 +11869,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11783
11869
|
: ((await this.resolveToolAuthScopeDigest(toolId))?.trim() ?? null);
|
|
11784
11870
|
const providerIdempotencyKey =
|
|
11785
11871
|
options?.providerIdempotencyKey?.trim() || durableCallReceiptKey;
|
|
11872
|
+
const providerIdempotencyReceiptKey =
|
|
11873
|
+
options?.providerIdempotencyReceiptKey?.trim() ||
|
|
11874
|
+
durableCallReceiptKey;
|
|
11786
11875
|
// Correlation identity is stable across every transport retry,
|
|
11787
11876
|
// including calls without a durable receipt.
|
|
11788
11877
|
const deeplineRequestId = providerIdempotencyKey
|
|
@@ -11802,9 +11891,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11802
11891
|
...(requestsDurableInvocationFence
|
|
11803
11892
|
? { invocation_fence_version: 1 }
|
|
11804
11893
|
: {}),
|
|
11805
|
-
|
|
11894
|
+
// This is deliberately the provider receipt, rather than the
|
|
11895
|
+
// worker-owned response cache receipt. It keeps a newer
|
|
11896
|
+
// worker wire-compatible with an already active Vercel app
|
|
11897
|
+
// while its runtime cache can still partition by response
|
|
11898
|
+
// representation.
|
|
11899
|
+
...(providerIdempotencyReceiptKey
|
|
11806
11900
|
? {
|
|
11807
|
-
durable_call_receipt_key:
|
|
11901
|
+
durable_call_receipt_key: providerIdempotencyReceiptKey,
|
|
11808
11902
|
...(executionAuthScopeDigest
|
|
11809
11903
|
? {
|
|
11810
11904
|
execution_auth_scope_digest:
|
|
@@ -60,6 +60,8 @@ export interface RowState {
|
|
|
60
60
|
export interface ToolCallRequest {
|
|
61
61
|
callId: string;
|
|
62
62
|
cacheKey: string;
|
|
63
|
+
/** Stable external-operation identity, independent of receipt serialization. */
|
|
64
|
+
providerIdempotencyKeyBase?: string | null;
|
|
63
65
|
cacheable?: boolean;
|
|
64
66
|
receiptKey?: string | null;
|
|
65
67
|
executionAuthScopeDigest?: string | null;
|
|
@@ -575,6 +577,8 @@ export interface ContextOptions {
|
|
|
575
577
|
toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
|
|
576
578
|
/** Execute-result contract pinned by the immutable Play artifact. */
|
|
577
579
|
toolResponseContract?: ToolResponseContract;
|
|
580
|
+
/** Explicit response-transform revision used for durable receipt identity. */
|
|
581
|
+
toolResponseReceiptRevision?: string;
|
|
578
582
|
/** Short-lived HMAC-signed internal token for tool callbacks. Required for cloud execution. */
|
|
579
583
|
executorToken?: string;
|
|
580
584
|
baseUrl?: string;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
// resolve.
|
|
8
8
|
} from '../plays/row-identity';
|
|
9
9
|
import { resolveDurableCallCachePolicy } from './durable-call-policy';
|
|
10
|
+
import { normalizeToolResponseReceiptRevision } from './tool-response-contract';
|
|
10
11
|
|
|
11
12
|
export {
|
|
12
13
|
DURABLE_CALL_STALE_AFTER_SECONDS_ERROR,
|
|
@@ -45,6 +46,11 @@ export function buildDurableToolCallCacheKey(input: {
|
|
|
45
46
|
/** Run-stable clock used to choose the stale bucket. */
|
|
46
47
|
cacheEpochMs?: number;
|
|
47
48
|
playLocalScope?: string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Explicit revision for a response transformation. Bump it only when the
|
|
51
|
+
* serialized tool output changes; omit it for historical artifacts.
|
|
52
|
+
*/
|
|
53
|
+
toolResponseReceiptRevision?: string | null;
|
|
48
54
|
}): string {
|
|
49
55
|
const orgId = input.orgId?.trim() || 'org';
|
|
50
56
|
const toolId = input.toolId.trim();
|
|
@@ -58,6 +64,11 @@ export function buildDurableToolCallCacheKey(input: {
|
|
|
58
64
|
);
|
|
59
65
|
}
|
|
60
66
|
const playLocalScope = input.playLocalScope?.trim() || 'play';
|
|
67
|
+
// Keep historical digests byte-for-byte stable when no explicit revision
|
|
68
|
+
// exists on the stored artifact.
|
|
69
|
+
const toolResponseReceiptRevision = normalizeToolResponseReceiptRevision(
|
|
70
|
+
input.toolResponseReceiptRevision,
|
|
71
|
+
);
|
|
61
72
|
const digest = sha256Hex(
|
|
62
73
|
stableStringify({
|
|
63
74
|
kind: 'tool' satisfies DurableCallKind,
|
|
@@ -70,6 +81,7 @@ export function buildDurableToolCallCacheKey(input: {
|
|
|
70
81
|
providerActionVersion,
|
|
71
82
|
cachePolicyVersion:
|
|
72
83
|
input.cachePolicyVersion ?? DURABLE_CALL_CACHE_POLICY_VERSION,
|
|
84
|
+
...(toolResponseReceiptRevision ? { toolResponseReceiptRevision } : {}),
|
|
73
85
|
staleBucket: durableCacheStaleBucket({
|
|
74
86
|
staleAfterSeconds: input.staleAfterSeconds,
|
|
75
87
|
nowMs: input.cacheEpochMs,
|
|
@@ -141,6 +141,8 @@ export interface PlayRunnerContextConfig {
|
|
|
141
141
|
toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
|
|
142
142
|
/** Immutable execute response contract copied from the Play artifact. */
|
|
143
143
|
toolResponseContract?: ToolResponseContract;
|
|
144
|
+
/** Explicit response-transform revision participating in receipt identity. */
|
|
145
|
+
toolResponseReceiptRevision?: string;
|
|
144
146
|
orgId?: string;
|
|
145
147
|
workflowId?: string;
|
|
146
148
|
playId?: string;
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Public tool-response contracts shared by the API, SDK, Play bundler, and
|
|
3
|
-
* runtime.
|
|
3
|
+
* runtime. This is a versioned response transformation contract: a change to
|
|
4
|
+
* the persisted public tool-result shape must introduce a new value here and
|
|
5
|
+
* have newly built Play artifacts select it. Receipt-cache revisions are
|
|
6
|
+
* intentionally separate: ordinary response-contract changes do not refresh
|
|
7
|
+
* durable receipts unless the response transformation changed as well.
|
|
4
8
|
*/
|
|
5
9
|
export const V2_TOOL_RESPONSE_CONTRACT = 'v2-tool-response' as const;
|
|
6
10
|
export const RAW_V2_TOOL_RESPONSE_CONTRACT = 'raw-v2' as const;
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Deliberate durable-receipt boundary for the raw-v2 response transformation.
|
|
14
|
+
* Bump only when that transformation changes serialized tool output. Do not
|
|
15
|
+
* bump for ordinary protocol, authoring, or transport contract changes.
|
|
16
|
+
*/
|
|
17
|
+
export const RAW_V2_TOOL_RESPONSE_RECEIPT_REVISION = 'raw-v2-receipt-v1';
|
|
18
|
+
const TOOL_RESPONSE_RECEIPT_REVISION_PATTERN =
|
|
19
|
+
/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/;
|
|
20
|
+
|
|
8
21
|
export type ToolResponseContract =
|
|
9
22
|
| typeof V2_TOOL_RESPONSE_CONTRACT
|
|
10
23
|
| typeof RAW_V2_TOOL_RESPONSE_CONTRACT;
|
|
@@ -20,6 +33,19 @@ export function isToolResponseContract(
|
|
|
20
33
|
);
|
|
21
34
|
}
|
|
22
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Preserves the distinction between an old artifact with no declared response
|
|
38
|
+
* contract and an artifact that explicitly selected the legacy V2 contract.
|
|
39
|
+
* Receipt reuse is controlled separately by `toolResponseReceiptRevision`.
|
|
40
|
+
*/
|
|
41
|
+
export function declaredToolResponseContract(
|
|
42
|
+
value: unknown,
|
|
43
|
+
): ToolResponseContract | undefined {
|
|
44
|
+
if (value == null) return undefined;
|
|
45
|
+
if (isToolResponseContract(value)) return value;
|
|
46
|
+
throw new UnsupportedToolResponseContractError(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
export class UnsupportedToolResponseContractError extends Error {
|
|
24
50
|
constructor(value: unknown) {
|
|
25
51
|
super(
|
|
@@ -29,13 +55,34 @@ export class UnsupportedToolResponseContractError extends Error {
|
|
|
29
55
|
}
|
|
30
56
|
}
|
|
31
57
|
|
|
58
|
+
export class InvalidToolResponseReceiptRevisionError extends Error {
|
|
59
|
+
constructor(value: unknown) {
|
|
60
|
+
super(
|
|
61
|
+
`Tool response receipt revision must be a non-empty static identifier (letters, numbers, '.', '_', or '-'); received ${String(value)}.`,
|
|
62
|
+
);
|
|
63
|
+
this.name = 'InvalidToolResponseReceiptRevisionError';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Missing means a historical artifact and preserves its original receipt key. */
|
|
68
|
+
export function normalizeToolResponseReceiptRevision(
|
|
69
|
+
value: unknown,
|
|
70
|
+
): string | undefined {
|
|
71
|
+
if (value == null) return undefined;
|
|
72
|
+
if (
|
|
73
|
+
typeof value === 'string' &&
|
|
74
|
+
TOOL_RESPONSE_RECEIPT_REVISION_PATTERN.test(value)
|
|
75
|
+
) {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
throw new InvalidToolResponseReceiptRevisionError(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
32
81
|
/** Missing artifact compatibility predates canonical bodies and stays V2. */
|
|
33
82
|
export function normalizeToolResponseContract(
|
|
34
83
|
value: unknown,
|
|
35
84
|
): ToolResponseContract {
|
|
36
|
-
|
|
37
|
-
if (isToolResponseContract(value)) return value;
|
|
38
|
-
throw new UnsupportedToolResponseContractError(value);
|
|
85
|
+
return declaredToolResponseContract(value) ?? V2_TOOL_RESPONSE_CONTRACT;
|
|
39
86
|
}
|
|
40
87
|
|
|
41
88
|
export function legacyRawFromToolResponseRawV2(
|
|
@@ -31,6 +31,8 @@ export type PlayArtifactCompatibility = {
|
|
|
31
31
|
authoringContractEdition?: PlayAuthoringContractEdition;
|
|
32
32
|
/** Missing preserves the raw-only V2 execute response contract. */
|
|
33
33
|
toolResponseContract?: ToolResponseContract;
|
|
34
|
+
/** Missing preserves the receipt namespace of artifacts created before this revision existed. */
|
|
35
|
+
toolResponseReceiptRevision?: string;
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
/** The only executable Play artifact contract. */
|
|
@@ -344,7 +344,12 @@ export type PlayAuthoringAstBindings = {
|
|
|
344
344
|
export type PlayAuthoringBindings = {
|
|
345
345
|
description?: string;
|
|
346
346
|
compatibility?: {
|
|
347
|
-
toolErrorSchemaVersion
|
|
347
|
+
toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
|
|
348
|
+
/**
|
|
349
|
+
* Bump only when a Play's response transformation changes serialized tool
|
|
350
|
+
* output and requires fresh durable tool receipts.
|
|
351
|
+
*/
|
|
352
|
+
toolResponseReceiptRevision?: string;
|
|
348
353
|
};
|
|
349
354
|
inline?: boolean;
|
|
350
355
|
billing?: {
|
|
@@ -1103,6 +1108,28 @@ export const PLAY_AUTHORING_FIELD_REGISTRY = {
|
|
|
1103
1108
|
errorMessage:
|
|
1104
1109
|
'compatibility.toolErrorSchemaVersion must be the static literal 0 or 1.',
|
|
1105
1110
|
},
|
|
1111
|
+
'compatibility.toolResponseReceiptRevision': {
|
|
1112
|
+
schema: Type.String({
|
|
1113
|
+
minLength: 1,
|
|
1114
|
+
maxLength: 64,
|
|
1115
|
+
pattern: '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$',
|
|
1116
|
+
}),
|
|
1117
|
+
fixtures: {
|
|
1118
|
+
valid: 'raw-v2-receipt-v1',
|
|
1119
|
+
invalid: 'has spaces',
|
|
1120
|
+
absent: undefined,
|
|
1121
|
+
unresolved: { expression: 'revision' },
|
|
1122
|
+
edition1: undefined,
|
|
1123
|
+
},
|
|
1124
|
+
referenceType: 'string',
|
|
1125
|
+
required: false,
|
|
1126
|
+
resolution: 'static-required',
|
|
1127
|
+
issueCode: 'play_authoring_binding_invalid',
|
|
1128
|
+
description:
|
|
1129
|
+
'Explicit durable-receipt revision for a response transformation; bump only when serialized tool output changes.',
|
|
1130
|
+
errorMessage:
|
|
1131
|
+
'compatibility.toolResponseReceiptRevision must be a non-empty static identifier using letters, numbers, dots, underscores, or hyphens.',
|
|
1132
|
+
},
|
|
1106
1133
|
inline: {
|
|
1107
1134
|
schema: Type.Boolean(),
|
|
1108
1135
|
fixtures: {
|
|
@@ -2488,7 +2515,7 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
|
|
|
2488
2515
|
'};',
|
|
2489
2516
|
'export type PlayBindings = {',
|
|
2490
2517
|
` description?: ${cloudReferenceType('description')};`,
|
|
2491
|
-
` compatibility?: { toolErrorSchemaVersion
|
|
2518
|
+
` compatibility?: { toolErrorSchemaVersion?: ${cloudReferenceType('compatibility.toolErrorSchemaVersion')}; toolResponseReceiptRevision?: ${cloudReferenceType('compatibility.toolResponseReceiptRevision')} };`,
|
|
2492
2519
|
` inline?: ${cloudReferenceType('inline')};`,
|
|
2493
2520
|
` billing?: { maxCreditsPerRun?: ${cloudReferenceType('billing.maxCreditsPerRun')} };`,
|
|
2494
2521
|
` runtime?: { timeout?: ${cloudReferenceType('runtime.timeout')}; size?: ${cloudReferenceType('runtime.size')} };`,
|
|
@@ -66,7 +66,7 @@ import { PLAY_AUTHORING_CONTRACT_EDITION } from '../authoring-contract';
|
|
|
66
66
|
// of the artifact bytes. Do not reuse a local bundle cached before either
|
|
67
67
|
// compatibility selection or docflow instrumentation entered graph analysis.
|
|
68
68
|
// Keep this aligned with the app and SDK adapters' cache namespace.
|
|
69
|
-
const PLAY_BUNDLE_CACHE_VERSION =
|
|
69
|
+
const PLAY_BUNDLE_CACHE_VERSION = 36;
|
|
70
70
|
const PLAY_ARTIFACT_CACHE_DIR = join(
|
|
71
71
|
tmpdir(),
|
|
72
72
|
`deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`,
|
|
@@ -205,6 +205,7 @@ type SourceGraphAnalysis = {
|
|
|
205
205
|
playDescription: string | null;
|
|
206
206
|
sandboxRuntimeDeclaration: PlaySandboxRuntimeDeclaration | null;
|
|
207
207
|
toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion | null;
|
|
208
|
+
toolResponseReceiptRevision: string | null;
|
|
208
209
|
importedPlayDependencies: ImportedPlayDependency[];
|
|
209
210
|
};
|
|
210
211
|
|
|
@@ -583,6 +584,8 @@ type ExtractedPlayMetadata = {
|
|
|
583
584
|
sandboxRuntimeDeclaration: PlaySandboxRuntimeDeclaration | null;
|
|
584
585
|
toolErrorSchemaVersion: ToolExecutionErrorSchemaVersion | null;
|
|
585
586
|
toolErrorSchemaVersionUnknown: boolean;
|
|
587
|
+
toolResponseReceiptRevision: string | null;
|
|
588
|
+
toolResponseReceiptRevisionUnknown: boolean;
|
|
586
589
|
};
|
|
587
590
|
|
|
588
591
|
function parsePlaySourceAst(sourceCode: string): AstNode | null {
|
|
@@ -1020,12 +1023,47 @@ function toolErrorSchemaVersionFromOptions(
|
|
|
1020
1023
|
'toolErrorSchemaVersion',
|
|
1021
1024
|
context,
|
|
1022
1025
|
);
|
|
1026
|
+
if (schema.kind === 'absent') return undefined;
|
|
1023
1027
|
if (schema.kind !== 'found') return null;
|
|
1024
1028
|
const schemaVersion = staticNumberFromExpression(schema.value, context);
|
|
1025
1029
|
if (schemaVersion === 0 || schemaVersion === 1) return schemaVersion;
|
|
1026
1030
|
return null;
|
|
1027
1031
|
}
|
|
1028
1032
|
|
|
1033
|
+
function toolResponseReceiptRevisionFromOptions(
|
|
1034
|
+
node: AstNode | null | undefined,
|
|
1035
|
+
context: PlayMetadataExtractionContext,
|
|
1036
|
+
): string | null | undefined {
|
|
1037
|
+
const directCompatibility = resolveStaticProperty(
|
|
1038
|
+
node,
|
|
1039
|
+
'compatibility',
|
|
1040
|
+
context,
|
|
1041
|
+
);
|
|
1042
|
+
if (directCompatibility.kind === 'unknown') return null;
|
|
1043
|
+
|
|
1044
|
+
const bindings = resolveStaticProperty(node, 'bindings', context);
|
|
1045
|
+
if (bindings.kind === 'unknown') return null;
|
|
1046
|
+
const bindingCompatibility =
|
|
1047
|
+
bindings.kind === 'found'
|
|
1048
|
+
? resolveStaticProperty(bindings.value, 'compatibility', context)
|
|
1049
|
+
: ({ kind: 'absent' } satisfies StaticPropertyResolution);
|
|
1050
|
+
if (bindingCompatibility.kind === 'unknown') return null;
|
|
1051
|
+
|
|
1052
|
+
const compatibility =
|
|
1053
|
+
directCompatibility.kind === 'found'
|
|
1054
|
+
? directCompatibility
|
|
1055
|
+
: bindingCompatibility;
|
|
1056
|
+
if (compatibility.kind === 'absent') return undefined;
|
|
1057
|
+
const revision = resolveStaticProperty(
|
|
1058
|
+
compatibility.value,
|
|
1059
|
+
'toolResponseReceiptRevision',
|
|
1060
|
+
context,
|
|
1061
|
+
);
|
|
1062
|
+
if (revision.kind === 'absent') return undefined;
|
|
1063
|
+
if (revision.kind !== 'found') return null;
|
|
1064
|
+
return staticStringFromExpression(revision.value, context);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1029
1067
|
function sandboxRuntimeDeclarationFromOptions(
|
|
1030
1068
|
node: AstNode | null | undefined,
|
|
1031
1069
|
context: PlayMetadataExtractionContext,
|
|
@@ -1169,12 +1207,20 @@ function playMetadataFromDefinePlayCall(
|
|
|
1169
1207
|
context,
|
|
1170
1208
|
);
|
|
1171
1209
|
const toolErrorSchemaVersionUnknown = toolErrorSchemaVersion === null;
|
|
1210
|
+
const toolResponseReceiptRevision = toolResponseReceiptRevisionFromOptions(
|
|
1211
|
+
options,
|
|
1212
|
+
context,
|
|
1213
|
+
);
|
|
1214
|
+
const toolResponseReceiptRevisionUnknown =
|
|
1215
|
+
toolResponseReceiptRevision === null;
|
|
1172
1216
|
|
|
1173
1217
|
if (
|
|
1174
1218
|
!name &&
|
|
1175
1219
|
!description &&
|
|
1176
1220
|
toolErrorSchemaVersion === undefined &&
|
|
1177
|
-
!toolErrorSchemaVersionUnknown
|
|
1221
|
+
!toolErrorSchemaVersionUnknown &&
|
|
1222
|
+
toolResponseReceiptRevision === undefined &&
|
|
1223
|
+
!toolResponseReceiptRevisionUnknown
|
|
1178
1224
|
) {
|
|
1179
1225
|
return null;
|
|
1180
1226
|
}
|
|
@@ -1184,6 +1230,8 @@ function playMetadataFromDefinePlayCall(
|
|
|
1184
1230
|
sandboxRuntimeDeclaration,
|
|
1185
1231
|
toolErrorSchemaVersion: toolErrorSchemaVersion ?? null,
|
|
1186
1232
|
toolErrorSchemaVersionUnknown,
|
|
1233
|
+
toolResponseReceiptRevision: toolResponseReceiptRevision ?? null,
|
|
1234
|
+
toolResponseReceiptRevisionUnknown,
|
|
1187
1235
|
};
|
|
1188
1236
|
}
|
|
1189
1237
|
|
|
@@ -2039,6 +2087,11 @@ async function analyzeSourceGraph(
|
|
|
2039
2087
|
'definePlay compatibility.toolErrorSchemaVersion must be the static literal 0 or 1.',
|
|
2040
2088
|
);
|
|
2041
2089
|
}
|
|
2090
|
+
if (metadata?.toolResponseReceiptRevisionUnknown) {
|
|
2091
|
+
throw new Error(
|
|
2092
|
+
'definePlay compatibility.toolResponseReceiptRevision must be a static non-empty string.',
|
|
2093
|
+
);
|
|
2094
|
+
}
|
|
2042
2095
|
const playName = metadata?.name ?? null;
|
|
2043
2096
|
const playDescription = metadata?.description ?? null;
|
|
2044
2097
|
const sandboxRuntimeDeclaration = metadata?.sandboxRuntimeDeclaration ?? null;
|
|
@@ -2063,6 +2116,7 @@ async function analyzeSourceGraph(
|
|
|
2063
2116
|
playDescription,
|
|
2064
2117
|
sandboxRuntimeDeclaration,
|
|
2065
2118
|
toolErrorSchemaVersion: metadata?.toolErrorSchemaVersion ?? null,
|
|
2119
|
+
toolResponseReceiptRevision: metadata?.toolResponseReceiptRevision ?? null,
|
|
2066
2120
|
importedPlayDependencies: [...importedPlayDependencies.values()].sort(
|
|
2067
2121
|
(left, right) => left.filePath.localeCompare(right.filePath),
|
|
2068
2122
|
),
|
|
@@ -2340,6 +2394,8 @@ export async function bundlePlayFile(
|
|
|
2340
2394
|
compatibility: buildPlayContractCompatibility({
|
|
2341
2395
|
toolErrorSchemaVersion:
|
|
2342
2396
|
analysis.toolErrorSchemaVersion ?? undefined,
|
|
2397
|
+
toolResponseReceiptRevision:
|
|
2398
|
+
analysis.toolResponseReceiptRevision ?? undefined,
|
|
2343
2399
|
}),
|
|
2344
2400
|
cacheHit: true,
|
|
2345
2401
|
},
|
|
@@ -2408,6 +2464,8 @@ export async function bundlePlayFile(
|
|
|
2408
2464
|
importPolicy,
|
|
2409
2465
|
compatibility: buildPlayContractCompatibility({
|
|
2410
2466
|
toolErrorSchemaVersion: analysis.toolErrorSchemaVersion ?? undefined,
|
|
2467
|
+
toolResponseReceiptRevision:
|
|
2468
|
+
analysis.toolResponseReceiptRevision ?? undefined,
|
|
2411
2469
|
}),
|
|
2412
2470
|
generatedAt: Date.now(),
|
|
2413
2471
|
cacheHit: false,
|