deepline 0.3.289 → 0.3.291
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/client.ts +8 -6
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/overview.ts +16 -6
- package/dist/bundling-sources/sdk/src/types.ts +21 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +41 -11
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +317 -63
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -2
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +6 -5
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-event-catalog.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +74 -33
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +73 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +39 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +72 -9
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +77 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool-options.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +17 -9
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -139
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +19 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +176 -18
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +7 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1404 -478
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +55 -25
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +97 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +34 -15
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +558 -57
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +10 -1
- package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-admission-batching.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +22 -5
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +35 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -8
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +75 -28
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +2 -0
- package/dist/cli/index.js +195 -582
- package/dist/cli/index.mjs +195 -582
- package/dist/{client-FR_u0dxT.d.ts → client-Ct4jmvAg.d.ts} +29 -8
- package/dist/{client-phK1Ixcl.d.mts → client-Ct9GKrwu.d.mts} +29 -8
- package/dist/{compiler-manifest-9SQtUuKb.d.mts → compiler-manifest-qtTNYvBl.d.mts} +2 -0
- package/dist/{compiler-manifest-9SQtUuKb.d.ts → compiler-manifest-qtTNYvBl.d.ts} +2 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +14 -5
- package/dist/index.mjs +14 -5
- package/dist/install-integrity.json +7 -4
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +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 +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -216,7 +216,11 @@ import {
|
|
|
216
216
|
ToolExecuteAuthScopeChangedError,
|
|
217
217
|
type ToolExecuteHttpFailureOutcome,
|
|
218
218
|
} from './tool-execute-retry-policy';
|
|
219
|
-
import {
|
|
219
|
+
import {
|
|
220
|
+
createToolHttpError,
|
|
221
|
+
attachToolCallFailureEvidenceFromResponse,
|
|
222
|
+
readToolCallFailureEvidence,
|
|
223
|
+
} from './tool-http-errors';
|
|
220
224
|
import {
|
|
221
225
|
describeTransportError,
|
|
222
226
|
transportGatewayOriginForDiagnostic,
|
|
@@ -255,6 +259,7 @@ import {
|
|
|
255
259
|
} from './play-artifact-runtime-contract';
|
|
256
260
|
import {
|
|
257
261
|
LOG_LEVELS,
|
|
262
|
+
classifyLogLine,
|
|
258
263
|
formatCtxFetchHttpFailureDiagnostic,
|
|
259
264
|
tagLogEntry,
|
|
260
265
|
tagLogLevel,
|
|
@@ -446,7 +451,11 @@ import {
|
|
|
446
451
|
RunLogBuffer,
|
|
447
452
|
RUNNER_LOG_CONTEXT_MAX_BYTES,
|
|
448
453
|
} from './run-log-buffer';
|
|
449
|
-
import {
|
|
454
|
+
import {
|
|
455
|
+
normalizePlayLogContext,
|
|
456
|
+
type PlayLogContext,
|
|
457
|
+
type PlayLogMetadata,
|
|
458
|
+
} from './log-context';
|
|
450
459
|
import type { AsyncPlayRunRef } from '../plays/authoring-contract';
|
|
451
460
|
import { admitAsyncPlayLineage } from './async-play-invocation';
|
|
452
461
|
import {
|
|
@@ -536,6 +545,11 @@ type InlineCompositionStore = {
|
|
|
536
545
|
};
|
|
537
546
|
const inlineCompositionContext =
|
|
538
547
|
new AsyncLocalStorage<InlineCompositionStore>();
|
|
548
|
+
// Authored scope is async-local: concurrent steps and rows cannot steal attribution.
|
|
549
|
+
const authoredStepContext = new AsyncLocalStorage<{
|
|
550
|
+
context: PlayContextImpl;
|
|
551
|
+
stepId: string;
|
|
552
|
+
}>();
|
|
539
553
|
const toolExecutionOverrides = new AsyncLocalStorage<{ force: boolean }>();
|
|
540
554
|
const PROGRESS_HEARTBEAT_INTERVAL_MS = 1_000;
|
|
541
555
|
const PURE_JS_HEARTBEAT_ROW_INTERVAL = 250;
|
|
@@ -1806,7 +1820,27 @@ type NativeBatchBodyAttribution = {
|
|
|
1806
1820
|
release(): void;
|
|
1807
1821
|
};
|
|
1808
1822
|
|
|
1823
|
+
type ToolBatchAdmissionObserver = {
|
|
1824
|
+
pending(requests: readonly ToolCallRequest[]): void;
|
|
1825
|
+
dispatched(requests: readonly ToolCallRequest[]): void;
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
function settleToolBatchGroups(
|
|
1829
|
+
groups: ToolCallRequest[][],
|
|
1830
|
+
execute: (requests: ToolCallRequest[]) => Promise<void>,
|
|
1831
|
+
observer?: ToolBatchAdmissionObserver,
|
|
1832
|
+
): Promise<PromiseSettledResult<void>[]> {
|
|
1833
|
+
return Promise.allSettled(
|
|
1834
|
+
groups.map((requests) =>
|
|
1835
|
+
execute(requests).finally(() => observer?.dispatched(requests)),
|
|
1836
|
+
),
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1809
1840
|
type ToolExecutionApiOptions = {
|
|
1841
|
+
/** Runner-local scheduling signals; never part of a provider/wire payload. */
|
|
1842
|
+
onProviderAdmissionPending?: () => void;
|
|
1843
|
+
onProviderDispatched?: () => void;
|
|
1810
1844
|
timeoutMs?: number;
|
|
1811
1845
|
abortSignal?: AbortSignal;
|
|
1812
1846
|
durableCallReceiptKey?: string | null;
|
|
@@ -6367,11 +6401,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
6367
6401
|
? {
|
|
6368
6402
|
relayReceiptOutput: {
|
|
6369
6403
|
receiptKey: input.resultReceiptKey,
|
|
6370
|
-
execution:
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6404
|
+
execution: {
|
|
6405
|
+
...toolExecutionMetadataForOutcome({
|
|
6406
|
+
kind: 'live',
|
|
6407
|
+
cacheKey: input.request.cacheKey,
|
|
6408
|
+
receiptKey: input.request.receiptKey,
|
|
6409
|
+
}),
|
|
6410
|
+
resultReceiptKey: input.resultReceiptKey,
|
|
6411
|
+
},
|
|
6375
6412
|
metadata: await this.resolveToolResultMetadata(
|
|
6376
6413
|
input.dispatchToolId,
|
|
6377
6414
|
),
|
|
@@ -6411,11 +6448,16 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
6411
6448
|
meta: execution.meta,
|
|
6412
6449
|
billing: execution.billing,
|
|
6413
6450
|
requestInput: input.dispatchPayload,
|
|
6414
|
-
execution:
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6451
|
+
execution: {
|
|
6452
|
+
...toolExecutionMetadataForOutcome({
|
|
6453
|
+
kind: 'live',
|
|
6454
|
+
cacheKey: input.request.cacheKey,
|
|
6455
|
+
receiptKey: input.request.receiptKey,
|
|
6456
|
+
}),
|
|
6457
|
+
...(!alwaysFresh && input.resultReceiptKey
|
|
6458
|
+
? { resultReceiptKey: input.resultReceiptKey }
|
|
6459
|
+
: {}),
|
|
6460
|
+
},
|
|
6419
6461
|
});
|
|
6420
6462
|
// First sub-phase of `resultDelivery`: the body was parsed above, and
|
|
6421
6463
|
// this is the instant the row's result actually exists. Everything
|
|
@@ -7032,7 +7074,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
7032
7074
|
|
|
7033
7075
|
private async serializeCellValue(value: unknown): Promise<unknown> {
|
|
7034
7076
|
const serialized = isToolExecuteResult(value)
|
|
7035
|
-
? serializeToolExecuteResult(value)
|
|
7077
|
+
? serializeToolExecuteResult(value, { includeCallEvidence: false })
|
|
7036
7078
|
: await this.serializeDatasetCells(value);
|
|
7037
7079
|
try {
|
|
7038
7080
|
stringifyPostgresJson(serialized);
|
|
@@ -7053,7 +7095,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
7053
7095
|
const visiting = new WeakSet<object>();
|
|
7054
7096
|
const visit = async (current: unknown): Promise<unknown> => {
|
|
7055
7097
|
if (isToolExecuteResult(current)) {
|
|
7056
|
-
return serializeToolExecuteResult(current
|
|
7098
|
+
return serializeToolExecuteResult(current, {
|
|
7099
|
+
includeCallEvidence: false,
|
|
7100
|
+
});
|
|
7057
7101
|
}
|
|
7058
7102
|
if (isPlayDataset(current)) {
|
|
7059
7103
|
return await serializePlayDatasetCell(current, visit);
|
|
@@ -7359,7 +7403,20 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
7359
7403
|
input.metadata ??
|
|
7360
7404
|
(await this.resolveToolResultMetadata(input.toolId)),
|
|
7361
7405
|
execution: input.execution,
|
|
7362
|
-
meta:
|
|
7406
|
+
meta: {
|
|
7407
|
+
...input.meta,
|
|
7408
|
+
...(input.execution.source === 'live' &&
|
|
7409
|
+
input.requestInput &&
|
|
7410
|
+
!input.meta?.callEvidence
|
|
7411
|
+
? {
|
|
7412
|
+
callEvidence: {
|
|
7413
|
+
input: this.secretRedactor.redact(input.requestInput),
|
|
7414
|
+
providerRequestsUnavailable:
|
|
7415
|
+
'This response did not retain provider HTTP evidence.',
|
|
7416
|
+
},
|
|
7417
|
+
}
|
|
7418
|
+
: {}),
|
|
7419
|
+
},
|
|
7363
7420
|
billing: input.billing,
|
|
7364
7421
|
}),
|
|
7365
7422
|
);
|
|
@@ -11829,8 +11886,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11829
11886
|
request.executionAuthScopeDigest ?? '',
|
|
11830
11887
|
String(strategy.toBucketKey(request.input)),
|
|
11831
11888
|
].join('\u0000'),
|
|
11832
|
-
readyCount:
|
|
11833
|
-
|
|
11889
|
+
readyCount: Math.min(
|
|
11890
|
+
strategy.maxBatchSize,
|
|
11891
|
+
this.governor.policy.concurrency.toolCalls,
|
|
11892
|
+
),
|
|
11893
|
+
maxGroupSize: Math.min(
|
|
11894
|
+
strategy.maxBatchSize,
|
|
11895
|
+
this.governor.policy.concurrency.toolCalls,
|
|
11896
|
+
),
|
|
11834
11897
|
coalesceWindowMs: this.toolCoalesceWindowMs(
|
|
11835
11898
|
this.#options.toolBatchCoalesceWindowMs,
|
|
11836
11899
|
TOOL_BATCH_COALESCE_WINDOW_MS,
|
|
@@ -12030,6 +12093,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12030
12093
|
|
|
12031
12094
|
const requests: ToolCallRequest[] = [];
|
|
12032
12095
|
const remaining: ToolCallRequest[] = [];
|
|
12096
|
+
const remainingLaneKeys = new Set<string>();
|
|
12033
12097
|
const selectedByLane = new Map<string, number>();
|
|
12034
12098
|
for (const request of this.toolCallQueue) {
|
|
12035
12099
|
const lane = this.toolDispatchLane(request);
|
|
@@ -12043,12 +12107,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12043
12107
|
selectedByLane.set(lane.key, selectedCount + 1);
|
|
12044
12108
|
} else {
|
|
12045
12109
|
remaining.push(request);
|
|
12110
|
+
remainingLaneKeys.add(lane.key);
|
|
12046
12111
|
}
|
|
12047
12112
|
}
|
|
12048
12113
|
this.toolCallQueue = remaining;
|
|
12049
12114
|
const selectedLaneKeys = new Set(selectedByLane.keys());
|
|
12050
12115
|
for (const laneKey of selectedLaneKeys) {
|
|
12051
|
-
|
|
12116
|
+
if (!remainingLaneKeys.has(laneKey)) {
|
|
12117
|
+
this.toolDispatchQueuedAtByLane.delete(laneKey);
|
|
12118
|
+
}
|
|
12052
12119
|
}
|
|
12053
12120
|
return { requests, laneKeys: selectedLaneKeys, nextDeadlineMs };
|
|
12054
12121
|
}
|
|
@@ -12258,6 +12325,33 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12258
12325
|
// it never waits for unrelated sibling calls from the previous column.
|
|
12259
12326
|
const inFlightToolExecutions = new Map<Promise<void>, ToolCallRequest[]>();
|
|
12260
12327
|
const activeGroupsByLane = new Map<string, number>();
|
|
12328
|
+
// Keep a native batch lane together while its physical request is
|
|
12329
|
+
// preparing or waiting for provider admission. Release it at dispatch, so
|
|
12330
|
+
// provider residence does not serialize the next batch.
|
|
12331
|
+
const pendingAdmissionByLane = new Map<string, Set<string>>();
|
|
12332
|
+
const admissionObserver: ToolBatchAdmissionObserver = {
|
|
12333
|
+
pending: (requests) => {
|
|
12334
|
+
for (const request of requests) {
|
|
12335
|
+
if (!this.#options.getBatchOperationStrategy?.(request.toolId))
|
|
12336
|
+
continue;
|
|
12337
|
+
const key = this.toolDispatchLane(request).key;
|
|
12338
|
+
const pending = pendingAdmissionByLane.get(key) ?? new Set<string>();
|
|
12339
|
+
pending.add(request.callId);
|
|
12340
|
+
pendingAdmissionByLane.set(key, pending);
|
|
12341
|
+
}
|
|
12342
|
+
},
|
|
12343
|
+
dispatched: (requests) => {
|
|
12344
|
+
let changed = false;
|
|
12345
|
+
for (const request of requests) {
|
|
12346
|
+
const key = this.toolDispatchLane(request).key;
|
|
12347
|
+
const pending = pendingAdmissionByLane.get(key);
|
|
12348
|
+
if (!pending?.delete(request.callId)) continue;
|
|
12349
|
+
changed = true;
|
|
12350
|
+
if (pending.size === 0) pendingAdmissionByLane.delete(key);
|
|
12351
|
+
}
|
|
12352
|
+
if (changed) this.wakeToolDispatcher();
|
|
12353
|
+
},
|
|
12354
|
+
};
|
|
12261
12355
|
const maxInFlightGroups = Math.max(
|
|
12262
12356
|
1,
|
|
12263
12357
|
Math.floor(
|
|
@@ -12319,6 +12413,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12319
12413
|
.filter(([, count]) => count >= maxInFlightGroupsPerLane)
|
|
12320
12414
|
.map(([laneKey]) => laneKey),
|
|
12321
12415
|
);
|
|
12416
|
+
for (const key of pendingAdmissionByLane.keys()) {
|
|
12417
|
+
blockedLaneKeys.add(key);
|
|
12418
|
+
}
|
|
12322
12419
|
const dispatchable =
|
|
12323
12420
|
inFlightToolExecutions.size >= maxInFlightGroups
|
|
12324
12421
|
? {
|
|
@@ -12361,7 +12458,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12361
12458
|
(activeGroupsByLane.get(laneKey) ?? 0) + 1,
|
|
12362
12459
|
);
|
|
12363
12460
|
}
|
|
12364
|
-
|
|
12461
|
+
admissionObserver.pending(dispatchable.requests);
|
|
12462
|
+
const tracked = this.executeBatchedToolCalls(
|
|
12463
|
+
dispatchable.requests,
|
|
12464
|
+
admissionObserver,
|
|
12465
|
+
)
|
|
12365
12466
|
.catch(async (error) => {
|
|
12366
12467
|
this.toolDispatcherFailure ??= error;
|
|
12367
12468
|
this.rejectQueuedToolCalls(error);
|
|
@@ -12371,6 +12472,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12371
12472
|
);
|
|
12372
12473
|
})
|
|
12373
12474
|
.finally(() => {
|
|
12475
|
+
admissionObserver.dispatched(dispatchable.requests);
|
|
12374
12476
|
this.observeToolDispatchGroupRoundTrip(
|
|
12375
12477
|
this.toolBatchDispatcherNowMs() - launchedAtMs,
|
|
12376
12478
|
);
|
|
@@ -12517,6 +12619,54 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12517
12619
|
};
|
|
12518
12620
|
}
|
|
12519
12621
|
|
|
12622
|
+
private async saveToolFailureEvidence(input: {
|
|
12623
|
+
toolId: string;
|
|
12624
|
+
callKey: string;
|
|
12625
|
+
input: Record<string, unknown>;
|
|
12626
|
+
error: unknown;
|
|
12627
|
+
context: PlayLogContext;
|
|
12628
|
+
}): Promise<string | undefined> {
|
|
12629
|
+
if (!this.#options.completeRuntimeStepReceipt) return;
|
|
12630
|
+
try {
|
|
12631
|
+
// An immutable diagnostic record in the SAME receipt store, discovered
|
|
12632
|
+
// through runtime_observability.events. It is never an executable result
|
|
12633
|
+
// cache and cannot change provider admission or replay semantics.
|
|
12634
|
+
const receipt = await this.completeRuntimeStepReceipt(
|
|
12635
|
+
`${buildDurableToolReceiptPrefix({
|
|
12636
|
+
orgId: this.#options.orgId ?? 'unknown-org',
|
|
12637
|
+
toolId: 'call-evidence',
|
|
12638
|
+
})}${stableDigest(`${this.currentRunId}:${crypto.randomUUID()}`)}`,
|
|
12639
|
+
this.currentRunId,
|
|
12640
|
+
this.secretRedactor.redact({
|
|
12641
|
+
kind: 'tool_call_evidence',
|
|
12642
|
+
toolId: input.toolId,
|
|
12643
|
+
callKey: input.callKey,
|
|
12644
|
+
status: 'failed',
|
|
12645
|
+
context: input.context,
|
|
12646
|
+
callEvidence: readToolCallFailureEvidence(input.error) ?? {
|
|
12647
|
+
input: input.input,
|
|
12648
|
+
providerRequestsUnavailable:
|
|
12649
|
+
'This failure did not retain provider HTTP evidence.',
|
|
12650
|
+
},
|
|
12651
|
+
error:
|
|
12652
|
+
serializeToolExecutionFailure(input.error) ??
|
|
12653
|
+
this.formatRuntimeError(input.error),
|
|
12654
|
+
}),
|
|
12655
|
+
COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
|
|
12656
|
+
);
|
|
12657
|
+
if (!receipt)
|
|
12658
|
+
throw new Error('Saved failure evidence was not acknowledged.');
|
|
12659
|
+
return receipt.key;
|
|
12660
|
+
} catch (error) {
|
|
12661
|
+
// Keep the original failure and never repeat a paid operation just to
|
|
12662
|
+
// repair observability. A missing record is explicit in retained logs.
|
|
12663
|
+
this.runtimeLog(
|
|
12664
|
+
`Saved call details unavailable: ${this.formatRuntimeError(error)}`,
|
|
12665
|
+
{ level: 'warn' },
|
|
12666
|
+
);
|
|
12667
|
+
}
|
|
12668
|
+
}
|
|
12669
|
+
|
|
12520
12670
|
private async executeTool(
|
|
12521
12671
|
key: string,
|
|
12522
12672
|
toolId: string,
|
|
@@ -12545,13 +12695,31 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12545
12695
|
!isPlayRowExecutionSuspendedError(error) &&
|
|
12546
12696
|
!(error instanceof PlayRowProviderBackpressureError)
|
|
12547
12697
|
) {
|
|
12698
|
+
const scope = authoredStepContext.getStore();
|
|
12699
|
+
const savedKey = await this.saveToolFailureEvidence({
|
|
12700
|
+
toolId,
|
|
12701
|
+
callKey: context.call_key,
|
|
12702
|
+
context: {
|
|
12703
|
+
callKey: context.call_key,
|
|
12704
|
+
toolId,
|
|
12705
|
+
callStatus: 'failed',
|
|
12706
|
+
...(scope?.context === this ? { stepId: scope.stepId } : {}),
|
|
12707
|
+
...(context.dataset ? { dataset: context.dataset } : {}),
|
|
12708
|
+
...(context.column ? { column: context.column } : {}),
|
|
12709
|
+
...(context.row_key ? { rowKey: context.row_key } : {}),
|
|
12710
|
+
},
|
|
12711
|
+
input,
|
|
12712
|
+
error,
|
|
12713
|
+
});
|
|
12548
12714
|
this.runtimeLog(
|
|
12549
12715
|
JSON.stringify({
|
|
12550
12716
|
event: 'tool_call_failed',
|
|
12551
12717
|
...context,
|
|
12718
|
+
...(savedKey ? { receipt_key: savedKey } : {}),
|
|
12552
12719
|
status: 'failed',
|
|
12553
12720
|
duration_ms: Math.max(0, Date.now() - startedAt),
|
|
12554
12721
|
}),
|
|
12722
|
+
{ level: 'error' },
|
|
12555
12723
|
);
|
|
12556
12724
|
}
|
|
12557
12725
|
throw error;
|
|
@@ -12562,7 +12730,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12562
12730
|
JSON.stringify({
|
|
12563
12731
|
event: 'tool_call_completed',
|
|
12564
12732
|
...context,
|
|
12565
|
-
...(execution?.
|
|
12733
|
+
...(execution?.resultReceiptKey || execution?.receiptKey
|
|
12734
|
+
? { receipt_key: execution.resultReceiptKey ?? execution.receiptKey }
|
|
12735
|
+
: {}),
|
|
12566
12736
|
...(execution?.source ? { execution_source: execution.source } : {}),
|
|
12567
12737
|
status: toolResult?.status ?? 'completed',
|
|
12568
12738
|
// Caller wall time includes queueing/reuse and result delivery. It is
|
|
@@ -14222,7 +14392,21 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14222
14392
|
): void {
|
|
14223
14393
|
const boundedLine = boundRunLogLine(line);
|
|
14224
14394
|
const omittedBefore = this.logBuffer.snapshot().omittedLines;
|
|
14225
|
-
|
|
14395
|
+
const scope = authoredStepContext.getStore();
|
|
14396
|
+
const classified = classifyLogLine(line);
|
|
14397
|
+
const timestamp = classified.line.match(/^\[([^\]]+)\]/)?.[1];
|
|
14398
|
+
const parsedAt = timestamp ? Date.parse(timestamp) : NaN;
|
|
14399
|
+
const metadata: PlayLogMetadata = {
|
|
14400
|
+
level: classified.level,
|
|
14401
|
+
occurredAt: Number.isFinite(parsedAt) ? parsedAt : Date.now(),
|
|
14402
|
+
...(scope?.context === this ? { stepId: scope.stepId } : {}),
|
|
14403
|
+
};
|
|
14404
|
+
for (const retained of this.logBuffer.append(
|
|
14405
|
+
line,
|
|
14406
|
+
terminal,
|
|
14407
|
+
context,
|
|
14408
|
+
metadata,
|
|
14409
|
+
)) {
|
|
14226
14410
|
const originalLineWasRetained =
|
|
14227
14411
|
retained === boundedLine &&
|
|
14228
14412
|
this.logBuffer.snapshot().omittedLines === omittedBefore;
|
|
@@ -14231,6 +14415,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14231
14415
|
originalLineWasRetained
|
|
14232
14416
|
? (this.logBuffer.contexts.at(-1) ?? undefined)
|
|
14233
14417
|
: undefined,
|
|
14418
|
+
this.logBuffer.ids.at(-1),
|
|
14419
|
+
originalLineWasRetained
|
|
14420
|
+
? (this.logBuffer.metadata.at(-1) ?? undefined)
|
|
14421
|
+
: undefined,
|
|
14234
14422
|
);
|
|
14235
14423
|
if (this.#options.verbose) console.log(retained);
|
|
14236
14424
|
}
|
|
@@ -14894,42 +15082,80 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14894
15082
|
return output;
|
|
14895
15083
|
};
|
|
14896
15084
|
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
this.
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
14905
|
-
|
|
14906
|
-
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
15085
|
+
const observe = !rowStore;
|
|
15086
|
+
const startedAt = Date.now();
|
|
15087
|
+
if (observe)
|
|
15088
|
+
this.emitExecutionEvent({
|
|
15089
|
+
type: 'step.started',
|
|
15090
|
+
stepId: normalizedKey,
|
|
15091
|
+
at: startedAt,
|
|
15092
|
+
});
|
|
15093
|
+
try {
|
|
15094
|
+
const output = await authoredStepContext.run(
|
|
15095
|
+
{ context: this, stepId: normalizedKey },
|
|
15096
|
+
() =>
|
|
15097
|
+
this.executeWithRuntimeReceipt<T>(
|
|
15098
|
+
'step',
|
|
15099
|
+
normalizedKey,
|
|
15100
|
+
this.currentRunId,
|
|
15101
|
+
{
|
|
15102
|
+
semanticKey: rowStore
|
|
15103
|
+
? stableDigest(
|
|
15104
|
+
stableStringify({
|
|
15105
|
+
scope: 'row',
|
|
15106
|
+
tableNamespace: rowStore.tableNamespace ?? null,
|
|
15107
|
+
rowKey: rowStore.rowKey ?? null,
|
|
15108
|
+
rowId: rowStore.rowKey ? null : rowStore.rowId,
|
|
15109
|
+
fieldName: rowStore.fieldName ?? null,
|
|
15110
|
+
callIndex,
|
|
15111
|
+
// Row scope prevents unrelated map cells from sharing a
|
|
15112
|
+
// checkpoint. A supplied semantic key identifies the actual
|
|
15113
|
+
// work (for example, ctx.runSteps hashes its current input),
|
|
15114
|
+
// so it must refine row scope rather than being discarded.
|
|
15115
|
+
semanticKey: options?.semanticKey ?? null,
|
|
15116
|
+
}),
|
|
15117
|
+
)
|
|
15118
|
+
: options?.semanticKey,
|
|
15119
|
+
staleAfterSeconds: options?.staleAfterSeconds,
|
|
15120
|
+
force: this.#options.cachePolicy?.forceStepRefresh === true,
|
|
15121
|
+
markSkipped: (output) => {
|
|
15122
|
+
assertJsonSerializableStepOutput(normalizedKey, output);
|
|
15123
|
+
},
|
|
15124
|
+
execute: executeStep,
|
|
15125
|
+
},
|
|
15126
|
+
),
|
|
15127
|
+
);
|
|
15128
|
+
if (observe)
|
|
15129
|
+
this.emitExecutionEvent({
|
|
15130
|
+
type: 'step.completed',
|
|
15131
|
+
stepId: normalizedKey,
|
|
15132
|
+
at: Date.now(),
|
|
15133
|
+
});
|
|
15134
|
+
return output;
|
|
15135
|
+
} catch (error) {
|
|
15136
|
+
if (observe && !isControlledPlaySuspension(error))
|
|
15137
|
+
this.emitExecutionEvent({
|
|
15138
|
+
type: 'step.failed',
|
|
15139
|
+
stepId: normalizedKey,
|
|
15140
|
+
at: Date.now(),
|
|
15141
|
+
error: buildPlayDocflowNodeErrorPreview(error, this.secretRedactor),
|
|
15142
|
+
});
|
|
15143
|
+
throw error;
|
|
15144
|
+
}
|
|
14927
15145
|
}
|
|
14928
15146
|
|
|
14929
15147
|
getLogs(): string[] {
|
|
14930
15148
|
return this.logBuffer.lines;
|
|
14931
15149
|
}
|
|
14932
15150
|
|
|
15151
|
+
getLogMetadata(): Array<PlayLogMetadata | null> {
|
|
15152
|
+
return this.logBuffer.metadata.slice();
|
|
15153
|
+
}
|
|
15154
|
+
|
|
15155
|
+
getLogIds(): string[] {
|
|
15156
|
+
return this.logBuffer.ids.slice();
|
|
15157
|
+
}
|
|
15158
|
+
|
|
14933
15159
|
getLogContexts(): Array<PlayLogContext | null> {
|
|
14934
15160
|
return this.logBuffer.contexts.slice();
|
|
14935
15161
|
}
|
|
@@ -15512,15 +15738,16 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15512
15738
|
source: 'cache' | 'owner',
|
|
15513
15739
|
): Promise<void> => {
|
|
15514
15740
|
const receiptKey = request.receiptKey!;
|
|
15515
|
-
const finalResult = cloneToolExecuteResultWithExecution(
|
|
15516
|
-
|
|
15517
|
-
toolExecutionMetadataForOutcome({
|
|
15741
|
+
const finalResult = cloneToolExecuteResultWithExecution(result, {
|
|
15742
|
+
...toolExecutionMetadataForOutcome({
|
|
15518
15743
|
kind: source === 'owner' ? 'live' : source,
|
|
15519
15744
|
cacheKey: request.cacheKey,
|
|
15520
15745
|
receiptKey,
|
|
15521
15746
|
...(source === 'owner' ? {} : { attachedToReceiptKey: receiptKey }),
|
|
15522
15747
|
}),
|
|
15523
|
-
|
|
15748
|
+
resultReceiptKey:
|
|
15749
|
+
this.mappedToolResultReceiptPolicy(request).resultReceiptKey,
|
|
15750
|
+
});
|
|
15524
15751
|
this.cacheToolResult(toolId, request.cacheKey, finalResult);
|
|
15525
15752
|
const resolver = this.toolCallResolvers.get(request.callId);
|
|
15526
15753
|
if (resolver) {
|
|
@@ -15676,6 +15903,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15676
15903
|
toolId: string;
|
|
15677
15904
|
requests: ToolCallRequest[];
|
|
15678
15905
|
strategy: AnyBatchOperationStrategy;
|
|
15906
|
+
admissionObserver?: ToolBatchAdmissionObserver;
|
|
15679
15907
|
}): Promise<ToolCallRequest[]> {
|
|
15680
15908
|
const { toolId, requests, strategy } = input;
|
|
15681
15909
|
if (requests.length === 0) return [];
|
|
@@ -15952,6 +16180,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
15952
16180
|
),
|
|
15953
16181
|
executionAuthScopeDigest:
|
|
15954
16182
|
detail.members[0]?.executionAuthScopeDigest ?? null,
|
|
16183
|
+
onProviderAdmissionPending: () =>
|
|
16184
|
+
input.admissionObserver?.pending(batch.memberRequests),
|
|
16185
|
+
onProviderDispatched: () =>
|
|
16186
|
+
input.admissionObserver?.dispatched(
|
|
16187
|
+
batch.memberRequests,
|
|
16188
|
+
),
|
|
15955
16189
|
providerIdempotencyKey: detail.providerOperationKey,
|
|
15956
16190
|
providerIdempotencyReceiptKey:
|
|
15957
16191
|
compatibility.resultReceiptKey,
|
|
@@ -16199,6 +16433,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
16199
16433
|
});
|
|
16200
16434
|
return { batch, result };
|
|
16201
16435
|
} finally {
|
|
16436
|
+
input.admissionObserver?.dispatched(batch.memberRequests);
|
|
16202
16437
|
// A dispatch that never reached its split — a provider failure, a
|
|
16203
16438
|
// cohort throw — must not leave the body registered against
|
|
16204
16439
|
// nobody for the rest of the run.
|
|
@@ -16290,16 +16525,23 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
16290
16525
|
|
|
16291
16526
|
private async executeBatchedToolCalls(
|
|
16292
16527
|
queuedToolCalls: ToolCallRequest[],
|
|
16528
|
+
admissionObserver?: ToolBatchAdmissionObserver,
|
|
16293
16529
|
): Promise<void> {
|
|
16294
|
-
//
|
|
16530
|
+
// Preserve authentication partitions through physical batch compilation.
|
|
16295
16531
|
const byTool = new Map<string, ToolCallRequest[]>();
|
|
16296
16532
|
for (const req of queuedToolCalls) {
|
|
16297
|
-
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16533
|
+
const key = JSON.stringify([
|
|
16534
|
+
req.toolId,
|
|
16535
|
+
req.executionAuthScopeDigest ?? '',
|
|
16536
|
+
]);
|
|
16537
|
+
if (!byTool.has(key)) byTool.set(key, []);
|
|
16538
|
+
byTool.get(key)!.push(req);
|
|
16539
|
+
}
|
|
16540
|
+
const toolGroups = [...byTool.values()];
|
|
16541
|
+
const toolSettlements = await settleToolBatchGroups(
|
|
16542
|
+
toolGroups,
|
|
16543
|
+
async (requests) => {
|
|
16544
|
+
const toolId = requests[0]!.toolId;
|
|
16303
16545
|
this.runtimeLog(
|
|
16304
16546
|
`Executing tool batch ${toolId}: ${requests.length} calls`,
|
|
16305
16547
|
);
|
|
@@ -16430,6 +16672,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
16430
16672
|
toolId,
|
|
16431
16673
|
requests: cohortRequests,
|
|
16432
16674
|
strategy: operationStrategy,
|
|
16675
|
+
admissionObserver,
|
|
16433
16676
|
})
|
|
16434
16677
|
: await this.executeCacheableNonNativeMapToolCallCohort({
|
|
16435
16678
|
toolId,
|
|
@@ -16524,6 +16767,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
16524
16767
|
providerOperationKey: aggregateProviderOperationKey,
|
|
16525
16768
|
apiOptions: {
|
|
16526
16769
|
durableCallReceiptKey: aggregateReceiptKey,
|
|
16770
|
+
onProviderAdmissionPending: () =>
|
|
16771
|
+
admissionObserver?.pending(batch.memberRequests),
|
|
16772
|
+
onProviderDispatched: () =>
|
|
16773
|
+
admissionObserver?.dispatched(batch.memberRequests),
|
|
16527
16774
|
legacyProviderRetryIdentities: [
|
|
16528
16775
|
{
|
|
16529
16776
|
transportReceiptKey:
|
|
@@ -16785,7 +17032,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
16785
17032
|
this.#options.onBatchComplete?.(this.checkpoint);
|
|
16786
17033
|
}
|
|
16787
17034
|
}
|
|
16788
|
-
}
|
|
17035
|
+
},
|
|
17036
|
+
admissionObserver,
|
|
16789
17037
|
);
|
|
16790
17038
|
const rejected = toolSettlements.find(
|
|
16791
17039
|
(settlement): settlement is PromiseRejectedResult =>
|
|
@@ -17944,6 +18192,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
17944
18192
|
let providerTelemetryKey: string | null = null;
|
|
17945
18193
|
let providerRunInFlightAtDispatch: number | null = null;
|
|
17946
18194
|
const acquireProviderPermit = async (signal?: AbortSignal) => {
|
|
18195
|
+
options?.onProviderAdmissionPending?.();
|
|
17947
18196
|
logProvider429RetryPhase('provider_permit_wait_started');
|
|
17948
18197
|
const permit = await this.resourceGovernor.acquireProviderPermit({
|
|
17949
18198
|
toolId,
|
|
@@ -18094,6 +18343,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18094
18343
|
acquireProviderPermit: async ({ signal }) =>
|
|
18095
18344
|
await acquireProviderPermit(signal),
|
|
18096
18345
|
tryAcquireProviderPermit: () => {
|
|
18346
|
+
options?.onProviderAdmissionPending?.();
|
|
18097
18347
|
logProvider429RetryPhase(
|
|
18098
18348
|
'provider_permit_acquire_attempted',
|
|
18099
18349
|
);
|
|
@@ -18146,6 +18396,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18146
18396
|
> | null;
|
|
18147
18397
|
} = { current: null };
|
|
18148
18398
|
const beginProviderExecution = () => {
|
|
18399
|
+
if (providerExecution.current === null) {
|
|
18400
|
+
options?.onProviderDispatched?.();
|
|
18401
|
+
}
|
|
18149
18402
|
const execution =
|
|
18150
18403
|
providerExecution.current ??
|
|
18151
18404
|
this.resourceGovernor.beginProviderExecution({
|
|
@@ -19209,6 +19462,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19209
19462
|
`gateway=${transportGatewayOriginForDiagnostic(url)} attempt=${httpFailureAttempt})`,
|
|
19210
19463
|
{ level: 'error' },
|
|
19211
19464
|
);
|
|
19465
|
+
attachToolCallFailureEvidenceFromResponse(surfacedError, text);
|
|
19212
19466
|
throw surfacedError;
|
|
19213
19467
|
}
|
|
19214
19468
|
|
|
@@ -11,7 +11,7 @@ import type { PlayRunContractSnapshot } from '../plays/contracts';
|
|
|
11
11
|
import type { PlayDatasetColumnTypes } from '../play-data-plane/contracts';
|
|
12
12
|
import type { PlayStructuredDefinition } from '../plays/definition';
|
|
13
13
|
import type { PlayStaticPipeline } from '../plays/static-pipeline';
|
|
14
|
-
import type { PlayLogContext } from './log-context';
|
|
14
|
+
import type { PlayLogContext, PlayLogMetadata } from './log-context';
|
|
15
15
|
import type {
|
|
16
16
|
AsyncPlayRunRef,
|
|
17
17
|
DurableCallStaleAfterSeconds,
|
|
@@ -514,6 +514,9 @@ export interface MapExecutionScope {
|
|
|
514
514
|
}
|
|
515
515
|
|
|
516
516
|
export type PlayExecutionEvent =
|
|
517
|
+
| { type: 'step.started'; stepId: string; at: number }
|
|
518
|
+
| { type: 'step.completed'; stepId: string; at: number }
|
|
519
|
+
| { type: 'step.failed'; stepId: string; error: string; at: number }
|
|
517
520
|
| PlayActivityEvent
|
|
518
521
|
| {
|
|
519
522
|
type: 'docflow.node.hit';
|
|
@@ -851,7 +854,12 @@ export interface ContextOptions {
|
|
|
851
854
|
/** Called after each durable batch completes for scheduler checkpointing. */
|
|
852
855
|
onBatchComplete?: (checkpoint: PlayCheckpoint) => void;
|
|
853
856
|
/** Called when the runtime emits a new execution log line. */
|
|
854
|
-
onLog?: (
|
|
857
|
+
onLog?: (
|
|
858
|
+
line: string,
|
|
859
|
+
context?: PlayLogContext,
|
|
860
|
+
entryId?: string,
|
|
861
|
+
metadata?: PlayLogMetadata,
|
|
862
|
+
) => void;
|
|
855
863
|
/**
|
|
856
864
|
* Runtime-provided HTTP transport for ctx.fetch. Node runtimes should provide
|
|
857
865
|
* a transport that validates resolved IP addresses at connect time.
|
|
@@ -84,6 +84,7 @@ const FIXTURE_MODE_BYPASS_PROVIDERS = new Set([
|
|
|
84
84
|
'test',
|
|
85
85
|
'test_high',
|
|
86
86
|
'test_low',
|
|
87
|
+
'test_batching',
|
|
87
88
|
// Dedicated stateful failure fixtures must reach their local custom handlers
|
|
88
89
|
// even when the surrounding Play runs in fixture mode.
|
|
89
90
|
'test_receipt',
|