deepline 0.3.393 → 0.3.394
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 +477 -10
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +5 -1
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-policy.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/freshness-receipt-execution.ts +251 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +25 -16
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +69 -22
- package/dist/bundling-sources/shared_libs/plays/core.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +18 -0
- package/dist/cli/auth-IHKISLJQ.js +39 -0
- package/dist/cli/{auth-5PXHA232.mjs → auth-OSXDAVCF.mjs} +4 -4
- package/dist/cli/{billing-JFB2XLSO.js → billing-EKLYOXUY.js} +75 -75
- package/dist/cli/{billing-Z2N7ZAGM.mjs → billing-ZNNKX3UR.mjs} +4 -4
- package/dist/cli/{chunk-OXNOAD6Y.js → chunk-2MDDA7A4.js} +26 -26
- package/dist/cli/{chunk-PTDECJGP.js → chunk-354JCXA2.js} +50 -50
- package/dist/cli/{chunk-RKMB3N7K.mjs → chunk-3HU47EGZ.mjs} +48 -9
- package/dist/cli/{chunk-6I73X254.mjs → chunk-3HWALOL2.mjs} +1 -1
- package/dist/cli/{chunk-VIQJIX2P.js → chunk-4P27AXEA.js} +65 -65
- package/dist/cli/{chunk-UOGZUA24.mjs → chunk-5BB2RGAX.mjs} +18 -8
- package/dist/cli/{chunk-RWHWF5ZJ.js → chunk-6YAUMG27.js} +1 -1
- package/dist/cli/{chunk-HLEESTCH.mjs → chunk-BP33SIZF.mjs} +1 -1
- package/dist/cli/{chunk-CDFH2DYL.js → chunk-CS6RAIVY.js} +4 -4
- package/dist/cli/{chunk-2HAD745V.js → chunk-E23BQ5SC.js} +9 -9
- package/dist/cli/{chunk-LMMEAMA7.mjs → chunk-FJGW5GXU.mjs} +1 -1
- package/dist/cli/{chunk-Q26LUSQ2.mjs → chunk-JHIJIHFB.mjs} +1 -1
- package/dist/cli/{chunk-PWSWAN7Q.js → chunk-KB4SDX5S.js} +48 -9
- package/dist/cli/{chunk-M7Y7JYZX.js → chunk-KGNTKMRG.js} +6 -6
- package/dist/cli/{chunk-4XX6443T.js → chunk-KI635JN4.js} +59 -59
- package/dist/cli/{chunk-Z3OOZM5L.js → chunk-KIUMEOM5.js} +8 -8
- package/dist/cli/{chunk-KIYU2TGL.mjs → chunk-M57J2SK4.mjs} +2 -2
- package/dist/cli/{chunk-FQ6VHPW4.mjs → chunk-MFB2VKBO.mjs} +2 -2
- package/dist/cli/{chunk-MS6GUPBB.js → chunk-MH73HMPV.js} +3 -3
- package/dist/cli/{chunk-AARX5L4B.js → chunk-MLPU5SMZ.js} +2 -2
- package/dist/cli/{chunk-5PRVMXKB.mjs → chunk-NR3DFKNF.mjs} +1 -1
- package/dist/cli/{chunk-W4WOMXR3.js → chunk-QIQBYKLB.js} +15 -15
- package/dist/cli/{chunk-VIWE6WRK.js → chunk-RBJYWRCF.js} +260 -250
- package/dist/cli/{chunk-LZPCDB7U.mjs → chunk-TJI2EN6K.mjs} +1 -1
- package/dist/cli/{chunk-4TCT77UF.js → chunk-URWF7NGQ.js} +6 -1
- package/dist/cli/{chunk-5KWKZIQR.mjs → chunk-UXFSWG3Q.mjs} +4 -4
- package/dist/cli/{chunk-L2SYLGRP.js → chunk-VK6FYZLP.js} +8 -8
- package/dist/cli/{chunk-7HR7BGZL.mjs → chunk-X63FDZJZ.mjs} +3 -3
- package/dist/cli/{chunk-4PT2VA3A.mjs → chunk-XRQ2AQL7.mjs} +2 -2
- package/dist/cli/{chunk-STNYIMIV.mjs → chunk-XXUDVEXK.mjs} +6 -6
- package/dist/cli/{chunk-PQSA443V.mjs → chunk-ZI2ZNDJK.mjs} +6 -1
- package/dist/cli/{chunk-2LGC4EFT.mjs → chunk-ZYBGA4AR.mjs} +3 -3
- package/dist/cli/command-docs.js +2 -2
- package/dist/cli/command-docs.mjs +1 -1
- package/dist/cli/{csv-KJWZHKCH.mjs → csv-5AXRB35H.mjs} +3 -3
- package/dist/cli/{csv-P3Z3YE7H.js → csv-LU7O75WE.js} +12 -12
- package/dist/cli/{db-NCM4CQIS.mjs → db-7XP5UWEW.mjs} +4 -4
- package/dist/cli/{db-NUZGJNQC.js → db-F7YTFKVM.js} +29 -29
- package/dist/cli/{describe-YGSEN5WC.js → describe-FRH32RDV.js} +20 -20
- package/dist/cli/{describe-WT6MM2GO.mjs → describe-K4ES5LIL.mjs} +10 -10
- package/dist/cli/{executions-XS2IPJ4W.mjs → executions-IEHEHJUO.mjs} +4 -4
- package/dist/cli/{executions-PCOLHN7Q.js → executions-RZV5XPT2.js} +6 -6
- package/dist/cli/{feedback-4MSH7TFU.js → feedback-3P65QTX3.js} +7 -7
- package/dist/cli/{feedback-CCLTS5SV.mjs → feedback-FPUVZBAN.mjs} +3 -3
- package/dist/cli/{git-K2DHPMPC.mjs → git-GLVVP7TY.mjs} +4 -4
- package/dist/cli/git-MFFAWVVX.js +23 -0
- package/dist/cli/index.js +95 -95
- package/dist/cli/index.mjs +33 -33
- package/dist/cli/{legacy-noops-B5S4AR3P.mjs → legacy-noops-3MOI6NBM.mjs} +4 -4
- package/dist/cli/{legacy-noops-GPNOPFXV.js → legacy-noops-XKSGZR7Q.js} +7 -7
- package/dist/cli/{monitors-JLMAIPRG.mjs → monitors-FTBGNEFG.mjs} +4 -4
- package/dist/cli/{monitors-ZAWD7NSN.js → monitors-NZ2DYNST.js} +85 -85
- package/dist/cli/{org-P2KN4CES.js → org-MCKDQXIV.js} +64 -64
- package/dist/cli/{org-T3JTHP4G.mjs → org-V2J5RBCN.mjs} +4 -4
- package/dist/cli/{play-NK6BLRGL.mjs → play-AJFGME3B.mjs} +9 -9
- package/dist/cli/play-WLSN2EO2.js +158 -0
- package/dist/cli/{providers-5QRZN3DT.js → providers-2JEZ2264.js} +7 -7
- package/dist/cli/{providers-3NQRA7IT.mjs → providers-I6RQJZFZ.mjs} +4 -4
- package/dist/cli/{quickstart-PIA2JGPV.mjs → quickstart-BIDQDMO4.mjs} +3 -3
- package/dist/cli/{quickstart-R7NUZGNW.js → quickstart-GJ2RPDU3.js} +5 -5
- package/dist/cli/search-5VN4YD4I.js +27 -0
- package/dist/cli/{search-AIDIIGLY.mjs → search-ELRXFWG5.mjs} +5 -5
- package/dist/cli/{secrets-BT3PGMWN.js → secrets-53IT6VXD.js} +13 -13
- package/dist/cli/{secrets-OQNHL45O.mjs → secrets-SQXGZXIC.mjs} +4 -4
- package/dist/cli/{sessions-SF7WJPJP.mjs → sessions-2X5QF7SL.mjs} +4 -4
- package/dist/cli/{sessions-EYXWOSWK.js → sessions-CAV5M77Y.js} +5 -5
- package/dist/cli/{settings-BA3GHBOD.js → settings-532WNAZO.js} +74 -74
- package/dist/cli/{settings-Y57JTFDD.mjs → settings-7P67VGCJ.mjs} +7 -7
- package/dist/cli/{setup-RERXYC66.js → setup-6L2VSUWJ.js} +41 -41
- package/dist/cli/{setup-7GR5GBHA.mjs → setup-QGGBV2LE.mjs} +6 -6
- package/dist/cli/{skills-SJWIQAV5.mjs → skills-3FE62WBG.mjs} +4 -4
- package/dist/cli/skills-S6NPZAN7.js +28 -0
- package/dist/cli/tools-647RLXQS.js +41 -0
- package/dist/cli/{tools-ZF3JXPNL.mjs → tools-DMR25DEF.mjs} +7 -7
- package/dist/cli/{update-FFFYQAOJ.mjs → update-6RZMHJZP.mjs} +6 -6
- package/dist/cli/update-WVTSC3EM.js +28 -0
- package/dist/cli/{workflow-XVFEEGAS.mjs → workflow-L2DQRE6R.mjs} +5 -5
- package/dist/cli/{workflow-SZJVQH4T.js → workflow-TX4F2PIX.js} +15 -15
- package/dist/{client-DU5dtHDy.d.mts → client-BktIPdIj.d.mts} +1 -1
- package/dist/{client-mCE88hC4.d.ts → client-C3O1Ipf3.d.ts} +1 -1
- package/dist/{compiler-manifest-DE4RdAiY.d.ts → compiler-manifest-DLxUY5kI.d.mts} +65 -11
- package/dist/{compiler-manifest-DE4RdAiY.d.mts → compiler-manifest-DLxUY5kI.d.ts} +65 -11
- package/dist/{index-Bi-f7HFT.d.ts → index-B_GM9E3q.d.ts} +1 -1
- package/dist/{index-BI6nWgaV.d.mts → index-SCb0NvDb.d.mts} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -7
- package/dist/index.mjs +4 -4
- package/dist/install-integrity.json +85 -84
- package/dist/plays/bundle-play-file.d.mts +3 -3
- package/dist/plays/bundle-play-file.d.ts +3 -3
- package/dist/plays/bundle-play-file.mjs +2 -2
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli/auth-P3MVDTXM.js +0 -39
- package/dist/cli/git-RNM45FX4.js +0 -23
- package/dist/cli/play-PDXR6JFB.js +0 -158
- package/dist/cli/search-QRWL7MFM.js +0 -27
- package/dist/cli/skills-V7EY75HB.js +0 -28
- package/dist/cli/tools-H5HLBCHH.js +0 -41
- package/dist/cli/update-472HA3LT.js +0 -28
|
@@ -202,7 +202,7 @@ export const SDK_RELEASE = {
|
|
|
202
202
|
// getters keep their established compatibility behavior.
|
|
203
203
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
204
204
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
205
|
-
version: '0.3.
|
|
205
|
+
version: '0.3.394',
|
|
206
206
|
updateSummary:
|
|
207
207
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
208
208
|
packageCapabilities: {
|
|
@@ -195,6 +195,7 @@ import {
|
|
|
195
195
|
type ToolExecuteResult,
|
|
196
196
|
type ToolResultExecutionMetadata,
|
|
197
197
|
type ToolResultMetadataInput,
|
|
198
|
+
type CallFreshnessMetadata,
|
|
198
199
|
} from './tool-result';
|
|
199
200
|
import {
|
|
200
201
|
EXECUTION_GATEWAY_RECEIPT_OUTPUT_REFERENCE_HEADER,
|
|
@@ -269,9 +270,12 @@ import {
|
|
|
269
270
|
scopeToolReceiptToInvocation,
|
|
270
271
|
resolveDurableCallCachePolicy,
|
|
271
272
|
} from './durable-call-cache';
|
|
273
|
+
import { resolveExplicitDurableCallPolicy } from './durable-call-policy';
|
|
274
|
+
import { executeWithFreshnessReceipt } from './freshness-receipt-execution';
|
|
272
275
|
import {
|
|
273
276
|
CtxFetchHttpError,
|
|
274
277
|
PLAY_AUTHORING_CONTRACT_EDITION,
|
|
278
|
+
PlayAuthoringFieldValidationError,
|
|
275
279
|
normalizePlayAuthoringCustomerDbStatement,
|
|
276
280
|
validateOptionalPlayAuthoringField,
|
|
277
281
|
validatePlayAuthoringField,
|
|
@@ -639,6 +643,7 @@ const inlineCompositionContext =
|
|
|
639
643
|
const authoredStepContext = new AsyncLocalStorage<{
|
|
640
644
|
context: PlayContextImpl;
|
|
641
645
|
stepId: string;
|
|
646
|
+
invocationId?: string;
|
|
642
647
|
}>();
|
|
643
648
|
const toolExecutionOverrides = new AsyncLocalStorage<{ force: boolean }>();
|
|
644
649
|
const PROGRESS_HEARTBEAT_INTERVAL_MS = 1_000;
|
|
@@ -2931,6 +2936,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2931
2936
|
private mapInvocationIndex = 0;
|
|
2932
2937
|
private readonly stepCallIndexByKey = new Map<string, number>();
|
|
2933
2938
|
private readonly toolCallIndexByKey = new Map<string, number>();
|
|
2939
|
+
private readonly freshnessCallIndexByKey = new Map<string, number>();
|
|
2934
2940
|
/**
|
|
2935
2941
|
* Parent-level inline-child aggregates. Maintained only by the single-writer
|
|
2936
2942
|
* progress path (never per child) so concurrent fan-out cannot contend. See
|
|
@@ -3208,7 +3214,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3208
3214
|
key: string,
|
|
3209
3215
|
toolId: string,
|
|
3210
3216
|
input: Record<string, unknown>,
|
|
3211
|
-
options?: {
|
|
3217
|
+
options?: {
|
|
3218
|
+
description?: string;
|
|
3219
|
+
staleAfterSeconds?: number | null;
|
|
3220
|
+
},
|
|
3212
3221
|
): Promise<ToolExecuteResult<TOutput>> {
|
|
3213
3222
|
validatePlayAuthoringField('ctx.tool.key', key);
|
|
3214
3223
|
validatePlayAuthoringField('ctx.tool.tool', toolId);
|
|
@@ -3224,6 +3233,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3224
3233
|
tool: toolId,
|
|
3225
3234
|
input,
|
|
3226
3235
|
...(options?.description ? { description: options.description } : {}),
|
|
3236
|
+
...(options?.staleAfterSeconds !== undefined
|
|
3237
|
+
? { staleAfterSeconds: options.staleAfterSeconds }
|
|
3238
|
+
: {}),
|
|
3227
3239
|
})) as ToolExecuteResult<TOutput>;
|
|
3228
3240
|
}
|
|
3229
3241
|
|
|
@@ -3233,9 +3245,41 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3233
3245
|
readonly customerDb = {
|
|
3234
3246
|
query: async <TRow extends object = Record<string, unknown>>(
|
|
3235
3247
|
statement: PlaySqlQuery | string,
|
|
3236
|
-
options?: {
|
|
3248
|
+
options?: {
|
|
3249
|
+
id?: string;
|
|
3250
|
+
staleAfterSeconds?: number | null;
|
|
3251
|
+
maxRows?: number;
|
|
3252
|
+
timeoutMs?: number;
|
|
3253
|
+
},
|
|
3237
3254
|
): Promise<TRow[]> => {
|
|
3238
3255
|
const sql = normalizePlayAuthoringCustomerDbStatement(statement);
|
|
3256
|
+
if (this.usesExplicitFreshness) {
|
|
3257
|
+
validatePlayAuthoringField(
|
|
3258
|
+
'ctx.customerDb.query.options.id',
|
|
3259
|
+
options?.id,
|
|
3260
|
+
);
|
|
3261
|
+
} else if (options?.id !== undefined) {
|
|
3262
|
+
validatePlayAuthoringField(
|
|
3263
|
+
'ctx.customerDb.query.options.id',
|
|
3264
|
+
options.id,
|
|
3265
|
+
);
|
|
3266
|
+
}
|
|
3267
|
+
if (options?.staleAfterSeconds !== undefined) {
|
|
3268
|
+
validatePlayAuthoringField(
|
|
3269
|
+
'ctx.customerDb.query.options.staleAfterSeconds',
|
|
3270
|
+
options.staleAfterSeconds,
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3273
|
+
if (
|
|
3274
|
+
this.usesExplicitFreshness &&
|
|
3275
|
+
options?.staleAfterSeconds === undefined
|
|
3276
|
+
) {
|
|
3277
|
+
throw new PlayAuthoringFieldValidationError(
|
|
3278
|
+
'ctx.customerDb.query.options.staleAfterSeconds',
|
|
3279
|
+
'play_authoring_durable_policy_invalid',
|
|
3280
|
+
'Edition 11 Customer DB queries require staleAfterSeconds: 0.',
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3239
3283
|
if (options?.maxRows !== undefined) {
|
|
3240
3284
|
validatePlayAuthoringField(
|
|
3241
3285
|
'ctx.customerDb.query.options.maxRows',
|
|
@@ -3249,7 +3293,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3249
3293
|
);
|
|
3250
3294
|
}
|
|
3251
3295
|
const result = (await this.tools.execute({
|
|
3252
|
-
id: `customer_db_query_${this.customerDbQueryIndex++}`,
|
|
3296
|
+
id: options?.id ?? `customer_db_query_${this.customerDbQueryIndex++}`,
|
|
3253
3297
|
tool: 'query_customer_db',
|
|
3254
3298
|
input: {
|
|
3255
3299
|
sql,
|
|
@@ -3260,7 +3304,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3260
3304
|
description: 'Customer DB query',
|
|
3261
3305
|
// The direct executor never reused a prior SQL result. Preserve that
|
|
3262
3306
|
// imperative behavior for reads and mutations alike.
|
|
3263
|
-
|
|
3307
|
+
...(this.usesExplicitFreshness
|
|
3308
|
+
? { staleAfterSeconds: options?.staleAfterSeconds }
|
|
3309
|
+
: { force: true }),
|
|
3264
3310
|
...(options?.timeoutMs !== undefined
|
|
3265
3311
|
? { timeoutMs: options.timeoutMs }
|
|
3266
3312
|
: {}),
|
|
@@ -3866,6 +3912,95 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3866
3912
|
return this.currentRuntimeContract.semantics;
|
|
3867
3913
|
}
|
|
3868
3914
|
|
|
3915
|
+
private get usesExplicitFreshness(): boolean {
|
|
3916
|
+
return (
|
|
3917
|
+
this.currentRuntimeSemantics.freshness ===
|
|
3918
|
+
'actual_age_with_operation_recovery'
|
|
3919
|
+
);
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3922
|
+
private freshnessInvocationId(operation: string, id: string): string {
|
|
3923
|
+
const row = rowContext.getStore();
|
|
3924
|
+
const step = authoredStepContext.getStore();
|
|
3925
|
+
const scope = stableStringify({
|
|
3926
|
+
namespace: this.currentExecutionScope.receipt.namespace,
|
|
3927
|
+
operation,
|
|
3928
|
+
id,
|
|
3929
|
+
step: step?.context === this ? (step.invocationId ?? step.stepId) : null,
|
|
3930
|
+
row: row
|
|
3931
|
+
? {
|
|
3932
|
+
table: row.tableNamespace ?? null,
|
|
3933
|
+
key: row.rowKey ?? row.rowId,
|
|
3934
|
+
field: row.fieldName ?? null,
|
|
3935
|
+
}
|
|
3936
|
+
: null,
|
|
3937
|
+
});
|
|
3938
|
+
const freshIndex = this.freshnessCallIndexByKey.get(scope) ?? 0;
|
|
3939
|
+
this.freshnessCallIndexByKey.set(scope, freshIndex + 1);
|
|
3940
|
+
// Invocation identity follows authored scope and call order. Keep it
|
|
3941
|
+
// separate from the request/auth/action fingerprint used by reusable
|
|
3942
|
+
// result caching, so credential or response-contract changes cannot mint
|
|
3943
|
+
// a second provider operation key for the same authored effect.
|
|
3944
|
+
// A mapped row can be re-entered after provider backpressure; its replay
|
|
3945
|
+
// cursor restores the original call position.
|
|
3946
|
+
const index = nextReplayableCallIndex(`freshness:${scope}`, freshIndex);
|
|
3947
|
+
return sha256Hex(`${scope}:${index}`);
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
private async executeFreshnessControlledCall<T>(input: {
|
|
3951
|
+
operation: DurableCtxOperation;
|
|
3952
|
+
id: string;
|
|
3953
|
+
semanticKey: string;
|
|
3954
|
+
invocationId: string;
|
|
3955
|
+
policy: { requested: number | null; effective: number | null };
|
|
3956
|
+
execute: () => Promise<T>;
|
|
3957
|
+
completedAt?: (value: T) => number | undefined;
|
|
3958
|
+
onOutcome?: (value: T, metadata: CallFreshnessMetadata) => T;
|
|
3959
|
+
}): Promise<T> {
|
|
3960
|
+
const orgId = this.#options.orgId?.trim();
|
|
3961
|
+
if (!orgId) {
|
|
3962
|
+
throw new Error(
|
|
3963
|
+
'Freshness-controlled calls require a resolved organization identity.',
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3966
|
+
return executeWithFreshnessReceipt({
|
|
3967
|
+
orgId,
|
|
3968
|
+
semanticKey: input.semanticKey,
|
|
3969
|
+
invocationId: input.invocationId,
|
|
3970
|
+
runId: this.currentRunId,
|
|
3971
|
+
requestedStaleAfterSeconds: input.policy.requested,
|
|
3972
|
+
effectiveStaleAfterSeconds: input.policy.effective,
|
|
3973
|
+
store: this.durableReceiptExecutionStore(),
|
|
3974
|
+
completedAt: input.completedAt,
|
|
3975
|
+
withHydration: (read) =>
|
|
3976
|
+
this.withRuntimeReceiptHydrationTurn(
|
|
3977
|
+
(turn) =>
|
|
3978
|
+
read((receipt) =>
|
|
3979
|
+
turn.settleBytes(
|
|
3980
|
+
runtimeStepReceiptPayloadBytes(receipt ? [receipt] : []),
|
|
3981
|
+
),
|
|
3982
|
+
),
|
|
3983
|
+
{ source: 'durable_operation' },
|
|
3984
|
+
),
|
|
3985
|
+
execute: async () => {
|
|
3986
|
+
const value = await input.execute();
|
|
3987
|
+
this.assertNoPendingDurableSuspension();
|
|
3988
|
+
return value;
|
|
3989
|
+
},
|
|
3990
|
+
onOutcome: (value, metadata) => {
|
|
3991
|
+
this.runtimeLog(
|
|
3992
|
+
JSON.stringify({
|
|
3993
|
+
event: 'call_freshness',
|
|
3994
|
+
operation: input.operation,
|
|
3995
|
+
call_key: input.id,
|
|
3996
|
+
...metadata,
|
|
3997
|
+
}),
|
|
3998
|
+
);
|
|
3999
|
+
return input.onOutcome ? input.onOutcome(value, metadata) : value;
|
|
4000
|
+
},
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
|
|
3869
4004
|
private recordPlayCallStep(input: {
|
|
3870
4005
|
playId: string;
|
|
3871
4006
|
execution?: 'inline';
|
|
@@ -6297,6 +6432,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
6297
6432
|
force?: boolean;
|
|
6298
6433
|
receiptKey?: string | null;
|
|
6299
6434
|
semanticKey?: string | null;
|
|
6435
|
+
invocationId?: string;
|
|
6300
6436
|
staleAfterSeconds?: number | null;
|
|
6301
6437
|
repairRunningReceiptForSameRun?: boolean;
|
|
6302
6438
|
repairRunningReceiptForSameRunAfterWaitTimeout?: boolean;
|
|
@@ -6340,6 +6476,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
6340
6476
|
},
|
|
6341
6477
|
});
|
|
6342
6478
|
if (opts.transient === true) {
|
|
6479
|
+
if (
|
|
6480
|
+
this.usesExplicitFreshness &&
|
|
6481
|
+
opts.staleAfterSeconds !== undefined &&
|
|
6482
|
+
opts.staleAfterSeconds !== 0
|
|
6483
|
+
) {
|
|
6484
|
+
throw new Error(
|
|
6485
|
+
'Transient fetch does not support retained result reuse; omit freshness or choose staleAfterSeconds: 0.',
|
|
6486
|
+
);
|
|
6487
|
+
}
|
|
6343
6488
|
try {
|
|
6344
6489
|
return await execute(null);
|
|
6345
6490
|
} finally {
|
|
@@ -6347,6 +6492,44 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
6347
6492
|
release?.();
|
|
6348
6493
|
}
|
|
6349
6494
|
}
|
|
6495
|
+
if (this.usesExplicitFreshness && operation === 'fetch') {
|
|
6496
|
+
const policy = resolveExplicitDurableCallPolicy(opts);
|
|
6497
|
+
const semanticKey = stableStringify({
|
|
6498
|
+
orgId: this.#options.orgId,
|
|
6499
|
+
namespace: this.currentExecutionScope.receipt.namespace,
|
|
6500
|
+
operation,
|
|
6501
|
+
id,
|
|
6502
|
+
semanticKey: opts.semanticKey,
|
|
6503
|
+
});
|
|
6504
|
+
try {
|
|
6505
|
+
return await this.executeFreshnessControlledCall({
|
|
6506
|
+
operation,
|
|
6507
|
+
id,
|
|
6508
|
+
semanticKey,
|
|
6509
|
+
invocationId:
|
|
6510
|
+
opts.invocationId ?? this.freshnessInvocationId(operation, id),
|
|
6511
|
+
policy,
|
|
6512
|
+
execute: () => execute(null),
|
|
6513
|
+
onOutcome: (value, freshness) => {
|
|
6514
|
+
const output =
|
|
6515
|
+
opts.onRecovered && freshness.source !== 'executed'
|
|
6516
|
+
? opts.onRecovered(
|
|
6517
|
+
value,
|
|
6518
|
+
{
|
|
6519
|
+
key: freshness.operationReceiptKey!,
|
|
6520
|
+
status: 'completed',
|
|
6521
|
+
},
|
|
6522
|
+
'cache',
|
|
6523
|
+
)
|
|
6524
|
+
: value;
|
|
6525
|
+
return { ...output, freshness };
|
|
6526
|
+
},
|
|
6527
|
+
});
|
|
6528
|
+
} finally {
|
|
6529
|
+
const release = releaseExternalCallSlot as (() => void) | null;
|
|
6530
|
+
release?.();
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6350
6533
|
const stalePolicy = resolveDurableCallCachePolicy(
|
|
6351
6534
|
opts.staleAfterSeconds,
|
|
6352
6535
|
operation === 'step'
|
|
@@ -11662,6 +11845,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11662
11845
|
stepPath,
|
|
11663
11846
|
}),
|
|
11664
11847
|
),
|
|
11848
|
+
...(step.staleAfterSeconds !== undefined
|
|
11849
|
+
? { staleAfterSeconds: step.staleAfterSeconds }
|
|
11850
|
+
: {}),
|
|
11665
11851
|
},
|
|
11666
11852
|
)
|
|
11667
11853
|
: await this.executeStepProgramStep(
|
|
@@ -12951,6 +13137,165 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12951
13137
|
toolId: string,
|
|
12952
13138
|
input: Record<string, unknown>,
|
|
12953
13139
|
options?: ToolCallOptions,
|
|
13140
|
+
): Promise<unknown> {
|
|
13141
|
+
if (!this.usesExplicitFreshness) {
|
|
13142
|
+
return this.executeToolLegacyInternal(key, toolId, input, options);
|
|
13143
|
+
}
|
|
13144
|
+
const policy = resolveExplicitDurableCallPolicy(options ?? {});
|
|
13145
|
+
if (
|
|
13146
|
+
this.#options.cachePolicy?.forceToolRefresh === true ||
|
|
13147
|
+
toolExecutionOverrides.getStore()?.force === true
|
|
13148
|
+
) {
|
|
13149
|
+
policy.effective = 0;
|
|
13150
|
+
}
|
|
13151
|
+
const normalizedKey = this.normalizeContextKey(key, 'tool');
|
|
13152
|
+
// Reserve operation identity before asynchronous metadata lookup. Two
|
|
13153
|
+
// concurrent calls may share an authored id; assigning their identity
|
|
13154
|
+
// after auth/action metadata resolves would make recovery depend on lookup
|
|
13155
|
+
// completion order instead of authored call order.
|
|
13156
|
+
const invocationId = this.freshnessInvocationId('tool', normalizedKey);
|
|
13157
|
+
const sqlTool = isCustomerDbDatasetTool(toolId);
|
|
13158
|
+
const scalarCustomerDbQuery = sqlTool && input.result_mode === 'rows';
|
|
13159
|
+
const liveDataset =
|
|
13160
|
+
isQueryResultDatasetReadRequest(toolId, input) && !scalarCustomerDbQuery;
|
|
13161
|
+
const sqlMutation =
|
|
13162
|
+
sqlTool &&
|
|
13163
|
+
!isQueryResultDatasetReadRequest(toolId, {
|
|
13164
|
+
...input,
|
|
13165
|
+
result_mode: undefined,
|
|
13166
|
+
});
|
|
13167
|
+
const [eventWait, invocationScoped, retryPolicy, reusableSupport] =
|
|
13168
|
+
policy.effective === 0
|
|
13169
|
+
? ([null, false, null, null] as const)
|
|
13170
|
+
: await Promise.all([
|
|
13171
|
+
this.#options.getIntegrationEventWaitHandler?.(toolId) ?? null,
|
|
13172
|
+
this.#options
|
|
13173
|
+
.getToolIdempotencyScope?.(toolId)
|
|
13174
|
+
.then((scope) => scope === 'invocation') ?? false,
|
|
13175
|
+
this.#options.getToolRetryPolicy?.(toolId, input) ?? null,
|
|
13176
|
+
this.#options.getToolReusableResultSupport?.(toolId) ?? null,
|
|
13177
|
+
]);
|
|
13178
|
+
const requiresExecutionFence = retryPolicy?.requiresExecutionFence === true;
|
|
13179
|
+
if (policy.effective !== 0 && reusableSupport?.supported !== true) {
|
|
13180
|
+
throw new Error(
|
|
13181
|
+
`Tool ${toolId} does not support reusable freshness; use staleAfterSeconds: 0. ${reusableSupport?.unavailableReason ?? 'No read-result reuse capability was supplied by the action owner.'}`,
|
|
13182
|
+
);
|
|
13183
|
+
}
|
|
13184
|
+
if (
|
|
13185
|
+
policy.effective !== 0 &&
|
|
13186
|
+
(eventWait ||
|
|
13187
|
+
invocationScoped ||
|
|
13188
|
+
requiresExecutionFence ||
|
|
13189
|
+
liveDataset ||
|
|
13190
|
+
sqlMutation ||
|
|
13191
|
+
isAlwaysFreshIntegrationTool(toolId))
|
|
13192
|
+
) {
|
|
13193
|
+
throw new Error(
|
|
13194
|
+
`Tool ${toolId} does not support reusable freshness; use staleAfterSeconds: 0. Live datasets, event waits, invocation-scoped actions, writes, and Workspace operations cannot reuse an older result.`,
|
|
13195
|
+
);
|
|
13196
|
+
}
|
|
13197
|
+
|
|
13198
|
+
const authScope = await this.resolveToolAuthScopeDigest(toolId);
|
|
13199
|
+
const actionVersion =
|
|
13200
|
+
(await this.#options.getToolActionCacheVersion?.(toolId))?.trim() ?? '';
|
|
13201
|
+
if (!actionVersion) {
|
|
13202
|
+
throw new Error(`Tool ${toolId} has no action identity for freshness.`);
|
|
13203
|
+
}
|
|
13204
|
+
const semanticKey = stableStringify({
|
|
13205
|
+
kind: 'tool',
|
|
13206
|
+
orgId: this.#options.orgId,
|
|
13207
|
+
toolId,
|
|
13208
|
+
input,
|
|
13209
|
+
authScope,
|
|
13210
|
+
actionVersion,
|
|
13211
|
+
responseContract: this.currentToolResponseContract,
|
|
13212
|
+
responseRevision: this.currentToolResponseReceiptRevision,
|
|
13213
|
+
});
|
|
13214
|
+
if (sqlTool && input.operation_key !== undefined) {
|
|
13215
|
+
throw new Error(
|
|
13216
|
+
'Play SQL operation identity is assigned by the runtime. Use the authored call id to identify the operation.',
|
|
13217
|
+
);
|
|
13218
|
+
}
|
|
13219
|
+
const requestInput =
|
|
13220
|
+
sqlTool && (sqlMutation || scalarCustomerDbQuery)
|
|
13221
|
+
? { ...input, operation_key: `${this.currentRunId}:${invocationId}` }
|
|
13222
|
+
: input;
|
|
13223
|
+
|
|
13224
|
+
if (liveDataset || isAlwaysFreshIntegrationTool(toolId)) {
|
|
13225
|
+
const value = await this.executeToolLegacyInternal(
|
|
13226
|
+
key,
|
|
13227
|
+
toolId,
|
|
13228
|
+
requestInput,
|
|
13229
|
+
{ ...options, force: true, staleAfterSeconds: 0 },
|
|
13230
|
+
);
|
|
13231
|
+
const freshness: CallFreshnessMetadata = {
|
|
13232
|
+
requestedStaleAfterSeconds: policy.requested,
|
|
13233
|
+
effectiveStaleAfterSeconds: 0,
|
|
13234
|
+
source: 'live_observation',
|
|
13235
|
+
resultAgeMs: 0,
|
|
13236
|
+
completedAt: Date.now(),
|
|
13237
|
+
unavailableReason: liveDataset
|
|
13238
|
+
? 'Dataset rows are live observations; the dataset owner controls pagination and recovery.'
|
|
13239
|
+
: 'Workspace reviewer edits and conflict checks must be observed on every invocation.',
|
|
13240
|
+
};
|
|
13241
|
+
return isToolExecuteResult(value)
|
|
13242
|
+
? cloneToolExecuteResultWithExecution(value, {
|
|
13243
|
+
...value._metadata.execution,
|
|
13244
|
+
freshness,
|
|
13245
|
+
})
|
|
13246
|
+
: value;
|
|
13247
|
+
}
|
|
13248
|
+
|
|
13249
|
+
return this.executeFreshnessControlledCall({
|
|
13250
|
+
operation: 'tool',
|
|
13251
|
+
id: normalizedKey,
|
|
13252
|
+
semanticKey,
|
|
13253
|
+
invocationId,
|
|
13254
|
+
policy,
|
|
13255
|
+
completedAt: (value) => {
|
|
13256
|
+
if (!isToolExecuteResult(value)) return undefined;
|
|
13257
|
+
const raw = value.toolResponse.raw as {
|
|
13258
|
+
operation?: { completed_at_ms?: unknown };
|
|
13259
|
+
} | null;
|
|
13260
|
+
const completedAt = raw?.operation?.completed_at_ms;
|
|
13261
|
+
return typeof completedAt === 'number' &&
|
|
13262
|
+
Number.isSafeInteger(completedAt)
|
|
13263
|
+
? completedAt
|
|
13264
|
+
: value._metadata.execution.completedAt;
|
|
13265
|
+
},
|
|
13266
|
+
execute: async () => {
|
|
13267
|
+
const value = await this.executeToolLegacyInternal(
|
|
13268
|
+
key,
|
|
13269
|
+
toolId,
|
|
13270
|
+
requestInput,
|
|
13271
|
+
{ ...options, force: true, staleAfterSeconds: 0 },
|
|
13272
|
+
);
|
|
13273
|
+
if ((await this.resolveToolAuthScopeDigest(toolId)) !== authScope) {
|
|
13274
|
+
throw new Error(
|
|
13275
|
+
'Tool credential identity changed during execution. Resume under the current credential identity before retaining this result.',
|
|
13276
|
+
);
|
|
13277
|
+
}
|
|
13278
|
+
return value;
|
|
13279
|
+
},
|
|
13280
|
+
onOutcome: (value, freshness) =>
|
|
13281
|
+
isToolExecuteResult(value)
|
|
13282
|
+
? cloneToolExecuteResultWithExecution(value, {
|
|
13283
|
+
...value._metadata.execution,
|
|
13284
|
+
cached: freshness.source !== 'executed',
|
|
13285
|
+
source: freshness.source === 'executed' ? 'live' : 'cache',
|
|
13286
|
+
resultReceiptKey: freshness.operationReceiptKey,
|
|
13287
|
+
freshness,
|
|
13288
|
+
completedAt: freshness.completedAt,
|
|
13289
|
+
})
|
|
13290
|
+
: value,
|
|
13291
|
+
});
|
|
13292
|
+
}
|
|
13293
|
+
|
|
13294
|
+
private async executeToolLegacyInternal(
|
|
13295
|
+
key: string,
|
|
13296
|
+
toolId: string,
|
|
13297
|
+
input: Record<string, unknown>,
|
|
13298
|
+
options?: ToolCallOptions,
|
|
12954
13299
|
): Promise<unknown> {
|
|
12955
13300
|
if (!rowContext.getStore()) this.assertRowAdmissionOpen();
|
|
12956
13301
|
this.assertNoPendingDurableSuspension();
|
|
@@ -15085,6 +15430,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15085
15430
|
if (!rowContext.getStore()) this.assertRowAdmissionOpen();
|
|
15086
15431
|
validatePlayAuthoringField('ctx.fetch.key', key);
|
|
15087
15432
|
const normalizedKey = this.normalizeContextKey(key, 'fetch');
|
|
15433
|
+
const invocationId =
|
|
15434
|
+
this.usesExplicitFreshness && options?.transient !== true
|
|
15435
|
+
? this.freshnessInvocationId('fetch', normalizedKey)
|
|
15436
|
+
: undefined;
|
|
15088
15437
|
const rowStore = rowContext.getStore();
|
|
15089
15438
|
const rowFetchScope = rowStore
|
|
15090
15439
|
? {
|
|
@@ -15098,6 +15447,30 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15098
15447
|
const url = input.toString();
|
|
15099
15448
|
const parsedUrl = new URL(url);
|
|
15100
15449
|
const method = (init.method ?? 'GET').toUpperCase();
|
|
15450
|
+
const reusableReadMethod = ['GET', 'HEAD', 'OPTIONS'].includes(method);
|
|
15451
|
+
if (this.usesExplicitFreshness && options?.transient === true) {
|
|
15452
|
+
if (!reusableReadMethod) {
|
|
15453
|
+
throw new Error(
|
|
15454
|
+
'Edition 11 transient fetches are limited to read-only HTTP methods.',
|
|
15455
|
+
);
|
|
15456
|
+
}
|
|
15457
|
+
if (
|
|
15458
|
+
options.staleAfterSeconds !== undefined &&
|
|
15459
|
+
options.staleAfterSeconds !== 0
|
|
15460
|
+
) {
|
|
15461
|
+
throw new Error(
|
|
15462
|
+
'Transient fetch does not support retained result reuse; omit freshness or choose staleAfterSeconds: 0.',
|
|
15463
|
+
);
|
|
15464
|
+
}
|
|
15465
|
+
} else if (
|
|
15466
|
+
this.usesExplicitFreshness &&
|
|
15467
|
+
!reusableReadMethod &&
|
|
15468
|
+
options?.staleAfterSeconds !== 0
|
|
15469
|
+
) {
|
|
15470
|
+
throw new Error(
|
|
15471
|
+
'Edition 11 mutating or non-read-only fetches require staleAfterSeconds: 0.',
|
|
15472
|
+
);
|
|
15473
|
+
}
|
|
15101
15474
|
const urlContainsResolvedSecret =
|
|
15102
15475
|
this.secretRedactor.containsRegisteredSecret(url, {
|
|
15103
15476
|
includeEncoded: true,
|
|
@@ -15187,6 +15560,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15187
15560
|
: {}),
|
|
15188
15561
|
}),
|
|
15189
15562
|
),
|
|
15563
|
+
invocationId,
|
|
15190
15564
|
staleAfterSeconds: options?.staleAfterSeconds,
|
|
15191
15565
|
transient: options?.transient === true,
|
|
15192
15566
|
onRecovered: (output) => {
|
|
@@ -15238,6 +15612,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15238
15612
|
},
|
|
15239
15613
|
body: receiptBody,
|
|
15240
15614
|
row: rowFetchScope,
|
|
15615
|
+
...(invocationId ? { invocationId } : {}),
|
|
15241
15616
|
}),
|
|
15242
15617
|
)}`,
|
|
15243
15618
|
);
|
|
@@ -15386,6 +15761,24 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15386
15761
|
'ctx.step.semanticKey',
|
|
15387
15762
|
options?.semanticKey,
|
|
15388
15763
|
);
|
|
15764
|
+
const freshnessPolicy = this.usesExplicitFreshness
|
|
15765
|
+
? resolveExplicitDurableCallPolicy(options ?? {})
|
|
15766
|
+
: null;
|
|
15767
|
+
if (
|
|
15768
|
+
freshnessPolicy &&
|
|
15769
|
+
this.#options.cachePolicy?.forceStepRefresh === true
|
|
15770
|
+
) {
|
|
15771
|
+
freshnessPolicy.effective = 0;
|
|
15772
|
+
}
|
|
15773
|
+
if (
|
|
15774
|
+
freshnessPolicy &&
|
|
15775
|
+
freshnessPolicy.requested !== 0 &&
|
|
15776
|
+
!options?.semanticKey?.trim()
|
|
15777
|
+
) {
|
|
15778
|
+
throw new Error(
|
|
15779
|
+
'Reusable ctx.step results require a stable semanticKey.',
|
|
15780
|
+
);
|
|
15781
|
+
}
|
|
15389
15782
|
const normalizedKey = this.normalizeContextKey(key, 'step');
|
|
15390
15783
|
if (!normalizedKey.trim()) {
|
|
15391
15784
|
throw new Error('ctx.step(key, fn) requires a non-empty stable step id.');
|
|
@@ -15393,7 +15786,26 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15393
15786
|
|
|
15394
15787
|
const rowStore = rowContext.getStore();
|
|
15395
15788
|
const scope = rowStore ? `row-${rowStore.rowId}` : 'workflow';
|
|
15396
|
-
const
|
|
15789
|
+
const parentStep = authoredStepContext.getStore();
|
|
15790
|
+
const explicitStepScope = freshnessPolicy
|
|
15791
|
+
? stableDigest(
|
|
15792
|
+
stableStringify({
|
|
15793
|
+
semanticKey: options?.semanticKey ?? null,
|
|
15794
|
+
parent:
|
|
15795
|
+
parentStep?.context === this
|
|
15796
|
+
? (parentStep.invocationId ?? parentStep.stepId)
|
|
15797
|
+
: null,
|
|
15798
|
+
row: rowStore
|
|
15799
|
+
? {
|
|
15800
|
+
table: rowStore.tableNamespace,
|
|
15801
|
+
key: rowStore.rowKey ?? rowStore.rowId,
|
|
15802
|
+
field: rowStore.fieldName,
|
|
15803
|
+
}
|
|
15804
|
+
: null,
|
|
15805
|
+
}),
|
|
15806
|
+
)
|
|
15807
|
+
: null;
|
|
15808
|
+
const callIndexKey = `${this.currentExecutionScope.receipt.namespace}:${scope}:${normalizedKey}${explicitStepScope ? `:${explicitStepScope}` : ''}`;
|
|
15397
15809
|
const freshCallIndex = this.stepCallIndexByKey.get(callIndexKey) ?? 0;
|
|
15398
15810
|
const callIndex = nextReplayableCallIndex(
|
|
15399
15811
|
`step:${callIndexKey}`,
|
|
@@ -15405,7 +15817,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15405
15817
|
);
|
|
15406
15818
|
const boundarySuffix = callIndex === 0 ? '' : `:${callIndex}`;
|
|
15407
15819
|
const boundaryId = this.durableBoundaryId(
|
|
15408
|
-
`step-${scope}:${normalizedKey}${boundarySuffix}`,
|
|
15820
|
+
`step-${scope}:${normalizedKey}${explicitStepScope ? `:${explicitStepScope}` : ''}${boundarySuffix}`,
|
|
15409
15821
|
);
|
|
15410
15822
|
const advanceRecoveredSleepCursor = async (
|
|
15411
15823
|
receipt?: RuntimeStepReceipt,
|
|
@@ -15488,9 +15900,63 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15488
15900
|
});
|
|
15489
15901
|
try {
|
|
15490
15902
|
const output = await authoredStepContext.run(
|
|
15491
|
-
{
|
|
15492
|
-
|
|
15493
|
-
|
|
15903
|
+
{
|
|
15904
|
+
context: this,
|
|
15905
|
+
stepId: normalizedKey,
|
|
15906
|
+
...(freshnessPolicy ? { invocationId: boundaryId } : {}),
|
|
15907
|
+
},
|
|
15908
|
+
async () => {
|
|
15909
|
+
if (freshnessPolicy) {
|
|
15910
|
+
const semanticKey = stableStringify({
|
|
15911
|
+
operation: 'step',
|
|
15912
|
+
orgId: this.#options.orgId,
|
|
15913
|
+
namespace: this.currentExecutionScope.receipt.namespace,
|
|
15914
|
+
id: normalizedKey,
|
|
15915
|
+
semanticKey: options?.semanticKey,
|
|
15916
|
+
row: rowStore
|
|
15917
|
+
? {
|
|
15918
|
+
table: rowStore.tableNamespace,
|
|
15919
|
+
key: rowStore.rowKey ?? rowStore.rowId,
|
|
15920
|
+
field: rowStore.fieldName,
|
|
15921
|
+
}
|
|
15922
|
+
: null,
|
|
15923
|
+
});
|
|
15924
|
+
const completed = await this.executeFreshnessControlledCall({
|
|
15925
|
+
operation: 'step',
|
|
15926
|
+
id: normalizedKey,
|
|
15927
|
+
semanticKey,
|
|
15928
|
+
invocationId: boundaryId,
|
|
15929
|
+
policy: freshnessPolicy,
|
|
15930
|
+
completedAt: (value) => value.completedAt,
|
|
15931
|
+
execute: async () => {
|
|
15932
|
+
const before = this.sleepBoundaryIndex;
|
|
15933
|
+
const value = await executeStep();
|
|
15934
|
+
const boundary =
|
|
15935
|
+
this.checkpoint.resolvedBoundaries?.[boundaryId];
|
|
15936
|
+
return {
|
|
15937
|
+
output: value === undefined ? null : value,
|
|
15938
|
+
undefinedOutput: value === undefined,
|
|
15939
|
+
sleepCount: this.sleepBoundaryIndex - before,
|
|
15940
|
+
completedAt:
|
|
15941
|
+
(boundary?.kind === 'step'
|
|
15942
|
+
? boundary.completedAt
|
|
15943
|
+
: undefined) ?? Date.now(),
|
|
15944
|
+
};
|
|
15945
|
+
},
|
|
15946
|
+
onOutcome: (value, freshness) => {
|
|
15947
|
+
if (freshness.source !== 'executed') {
|
|
15948
|
+
this.sleepBoundaryIndex += value.sleepCount;
|
|
15949
|
+
}
|
|
15950
|
+
return value;
|
|
15951
|
+
},
|
|
15952
|
+
});
|
|
15953
|
+
const result = completed.undefinedOutput
|
|
15954
|
+
? undefined
|
|
15955
|
+
: completed.output;
|
|
15956
|
+
assertJsonSerializableStepOutput(normalizedKey, result);
|
|
15957
|
+
return result as T;
|
|
15958
|
+
}
|
|
15959
|
+
return await this.executeWithRuntimeReceipt<T>(
|
|
15494
15960
|
'step',
|
|
15495
15961
|
normalizedKey,
|
|
15496
15962
|
this.currentRunId,
|
|
@@ -15520,7 +15986,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15520
15986
|
},
|
|
15521
15987
|
execute: executeStep,
|
|
15522
15988
|
},
|
|
15523
|
-
)
|
|
15989
|
+
);
|
|
15990
|
+
},
|
|
15524
15991
|
);
|
|
15525
15992
|
if (observe)
|
|
15526
15993
|
this.emitExecutionEvent({
|
|
@@ -338,7 +338,7 @@ export interface RuntimeDatasetOptions<TItem = Record<string, unknown>> {
|
|
|
338
338
|
* Set staleAfterSeconds to rerun after that many seconds while retries/replays
|
|
339
339
|
* inside the same window still hit the cache. Daily refreshes use 86400.
|
|
340
340
|
*/
|
|
341
|
-
staleAfterSeconds?: number;
|
|
341
|
+
staleAfterSeconds?: number | null;
|
|
342
342
|
/**
|
|
343
343
|
* Optional stable key per row. When provided, row identity is derived from
|
|
344
344
|
* the selected input field(s) or returned value instead of hashing the full
|
|
@@ -1083,6 +1083,9 @@ export interface ContextOptions {
|
|
|
1083
1083
|
/** Identity policy is required before reading receipts; lookup errors are
|
|
1084
1084
|
* fatal, unlike the legacy best-effort retry advice below. */
|
|
1085
1085
|
getToolIdempotencyScope?: (toolId: string) => Promise<'invocation' | null>;
|
|
1086
|
+
getToolReusableResultSupport?: (
|
|
1087
|
+
toolId: string,
|
|
1088
|
+
) => Promise<{ supported: boolean; unavailableReason: string | null }>;
|
|
1086
1089
|
getToolRetryPolicy?: (
|
|
1087
1090
|
toolId: string,
|
|
1088
1091
|
input: Record<string, unknown>,
|
|
@@ -1365,6 +1368,7 @@ export type PlayFetchResponse = {
|
|
|
1365
1368
|
headers: Record<string, string>;
|
|
1366
1369
|
bodyText: string;
|
|
1367
1370
|
json: unknown | null;
|
|
1371
|
+
freshness?: import('../plays/tool-result-types').CallFreshnessMetadata;
|
|
1368
1372
|
};
|
|
1369
1373
|
|
|
1370
1374
|
export interface BatchResult {
|