deepline 0.1.182 → 0.1.183
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/apps/play-runner-workers/src/child-play-await.ts +203 -6
- package/dist/bundling-sources/apps/play-runner-workers/src/child-play-submit.ts +8 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +633 -107
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +3 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +1747 -1890
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +116 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +542 -78
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +292 -11
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +519 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +2381 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +18 -5
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +130 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +39 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +2 -0
- package/dist/bundling-sources/sdk/src/client.ts +41 -0
- package/dist/bundling-sources/sdk/src/http.ts +23 -8
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +56 -3
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +132 -10
- package/dist/bundling-sources/shared_libs/play-runtime/auth-scope-resolver.ts +92 -0
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +4 -4
- package/dist/bundling-sources/shared_libs/play-runtime/batching-types.ts +8 -0
- package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +101 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1605 -281
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +105 -6
- package/dist/bundling-sources/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +116 -16
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +186 -20
- package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +133 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +245 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -5
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +14 -0
- package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +124 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +6 -2
- package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +48 -2
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +158 -15
- package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +45 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2037 -262
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +183 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-errors.ts +88 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +343 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +11 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +437 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +83 -6
- package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +24 -17
- package/dist/cli/index.js +76 -17
- package/dist/cli/index.mjs +76 -17
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +63 -9
- package/dist/index.mjs +63 -9
- package/dist/plays/bundle-play-file.mjs +2 -2
- package/package.json +1 -1
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
export type WorkerToolReceiptSelection = {
|
|
7
7
|
claimableReceiptKey: string | null;
|
|
8
8
|
forceDurableRefresh: boolean;
|
|
9
|
+
forceFailedDurableRefresh: boolean;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export type WorkerToolReceiptGroup<TRequest> = {
|
|
12
13
|
claimableReceiptKey: string;
|
|
13
14
|
forceDurableRefresh: boolean;
|
|
15
|
+
forceFailedDurableRefresh: boolean;
|
|
14
16
|
requests: TRequest[];
|
|
15
17
|
};
|
|
16
18
|
|
|
@@ -155,6 +157,7 @@ export function selectWorkerToolReceipt(input: {
|
|
|
155
157
|
durableReceiptKey?: string | null;
|
|
156
158
|
localRetryCacheKey: string;
|
|
157
159
|
force: boolean;
|
|
160
|
+
forceFailed?: boolean;
|
|
158
161
|
allowLocalRetryReceipts: boolean;
|
|
159
162
|
}): WorkerToolReceiptSelection {
|
|
160
163
|
const durableReceiptKey = input.durableReceiptKey?.trim() || null;
|
|
@@ -165,6 +168,10 @@ export function selectWorkerToolReceipt(input: {
|
|
|
165
168
|
return {
|
|
166
169
|
claimableReceiptKey: durableReceiptKey ?? localRetryReceiptKey,
|
|
167
170
|
forceDurableRefresh: input.force === true && durableReceiptKey !== null,
|
|
171
|
+
forceFailedDurableRefresh:
|
|
172
|
+
input.force !== true &&
|
|
173
|
+
input.forceFailed === true &&
|
|
174
|
+
durableReceiptKey !== null,
|
|
168
175
|
};
|
|
169
176
|
}
|
|
170
177
|
|
|
@@ -176,17 +183,21 @@ export function planWorkerToolReceiptGroups<TRequest>(
|
|
|
176
183
|
durableReceiptKey?: string | null;
|
|
177
184
|
localRetryCacheKey: string;
|
|
178
185
|
force: boolean;
|
|
186
|
+
forceFailed?: boolean;
|
|
179
187
|
};
|
|
180
188
|
},
|
|
181
189
|
): WorkerToolReceiptGroupPlan<TRequest> {
|
|
182
190
|
const localRequests: TRequest[] = [];
|
|
183
191
|
const groupsByKey = new Map<string, WorkerToolReceiptGroup<TRequest>>();
|
|
184
192
|
for (const request of requests) {
|
|
185
|
-
const {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
const {
|
|
194
|
+
claimableReceiptKey,
|
|
195
|
+
forceDurableRefresh,
|
|
196
|
+
forceFailedDurableRefresh,
|
|
197
|
+
} = selectWorkerToolReceipt({
|
|
198
|
+
...input.getReceiptInput(request),
|
|
199
|
+
allowLocalRetryReceipts: input.allowLocalRetryReceipts,
|
|
200
|
+
});
|
|
190
201
|
if (!claimableReceiptKey) {
|
|
191
202
|
localRequests.push(request);
|
|
192
203
|
continue;
|
|
@@ -194,10 +205,12 @@ export function planWorkerToolReceiptGroups<TRequest>(
|
|
|
194
205
|
const group = groupsByKey.get(claimableReceiptKey) ?? {
|
|
195
206
|
claimableReceiptKey,
|
|
196
207
|
forceDurableRefresh: false,
|
|
208
|
+
forceFailedDurableRefresh: false,
|
|
197
209
|
requests: [],
|
|
198
210
|
};
|
|
199
211
|
group.requests.push(request);
|
|
200
212
|
group.forceDurableRefresh ||= forceDurableRefresh;
|
|
213
|
+
group.forceFailedDurableRefresh ||= forceFailedDurableRefresh;
|
|
201
214
|
groupsByKey.set(claimableReceiptKey, group);
|
|
202
215
|
}
|
|
203
216
|
return {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { WORKER_PLATFORM_YIELD_LIMITS } from './worker-platform-budget';
|
|
2
|
+
|
|
3
|
+
export type WorkerWorkBudgetEventKind =
|
|
4
|
+
| 'tool'
|
|
5
|
+
| 'provider'
|
|
6
|
+
| 'receipt'
|
|
7
|
+
| 'heartbeat'
|
|
8
|
+
| 'sheet'
|
|
9
|
+
| 'log'
|
|
10
|
+
| 'egress';
|
|
11
|
+
|
|
12
|
+
export type WorkerWorkBudgetSnapshot = {
|
|
13
|
+
startedAt: number;
|
|
14
|
+
updatedAt: number;
|
|
15
|
+
elapsedMs: number;
|
|
16
|
+
counts: Record<WorkerWorkBudgetEventKind, number>;
|
|
17
|
+
elapsedMsByKind: Record<WorkerWorkBudgetEventKind, number>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type WorkerWorkBudgetYieldReason =
|
|
21
|
+
| 'elapsed'
|
|
22
|
+
| 'subrequest'
|
|
23
|
+
| 'provider'
|
|
24
|
+
| 'tool'
|
|
25
|
+
| 'receipt'
|
|
26
|
+
| 'heartbeat'
|
|
27
|
+
| 'sheet'
|
|
28
|
+
| 'log'
|
|
29
|
+
| 'egress';
|
|
30
|
+
|
|
31
|
+
export type WorkerWorkBudgetYieldDecision = {
|
|
32
|
+
shouldYield: boolean;
|
|
33
|
+
reason: WorkerWorkBudgetYieldReason | null;
|
|
34
|
+
snapshot: WorkerWorkBudgetSnapshot;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type WorkerWorkBudgetYieldLimits = Partial<
|
|
38
|
+
Record<WorkerWorkBudgetYieldReason, number>
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
export type WorkerWorkBudgetMeter = {
|
|
42
|
+
count(kind: WorkerWorkBudgetEventKind, amount?: number): void;
|
|
43
|
+
snapshot(): WorkerWorkBudgetSnapshot;
|
|
44
|
+
resetWindow(): void;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const WORK_BUDGET_EVENT_KINDS: WorkerWorkBudgetEventKind[] = [
|
|
48
|
+
'tool',
|
|
49
|
+
'provider',
|
|
50
|
+
'receipt',
|
|
51
|
+
'heartbeat',
|
|
52
|
+
'sheet',
|
|
53
|
+
'log',
|
|
54
|
+
'egress',
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
function zeroCounters(): Record<WorkerWorkBudgetEventKind, number> {
|
|
58
|
+
return Object.fromEntries(
|
|
59
|
+
WORK_BUDGET_EVENT_KINDS.map((kind) => [kind, 0]),
|
|
60
|
+
) as Record<WorkerWorkBudgetEventKind, number>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createWorkerWorkBudgetMeter(input?: {
|
|
64
|
+
nowMs?: () => number;
|
|
65
|
+
}): WorkerWorkBudgetMeter {
|
|
66
|
+
const nowMs = input?.nowMs ?? Date.now;
|
|
67
|
+
let startedAt = nowMs();
|
|
68
|
+
let updatedAt = startedAt;
|
|
69
|
+
let counts = zeroCounters();
|
|
70
|
+
let elapsedMsByKind = zeroCounters();
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
count(kind, amount = 1) {
|
|
74
|
+
counts[kind] += amount;
|
|
75
|
+
updatedAt = nowMs();
|
|
76
|
+
},
|
|
77
|
+
snapshot() {
|
|
78
|
+
const now = nowMs();
|
|
79
|
+
return {
|
|
80
|
+
startedAt,
|
|
81
|
+
updatedAt,
|
|
82
|
+
elapsedMs: Math.max(0, now - startedAt),
|
|
83
|
+
counts: { ...counts },
|
|
84
|
+
elapsedMsByKind: { ...elapsedMsByKind },
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
resetWindow() {
|
|
88
|
+
startedAt = nowMs();
|
|
89
|
+
updatedAt = startedAt;
|
|
90
|
+
counts = zeroCounters();
|
|
91
|
+
elapsedMsByKind = zeroCounters();
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const WORKER_WORK_BUDGET_YIELD_LIMITS = WORKER_PLATFORM_YIELD_LIMITS;
|
|
97
|
+
|
|
98
|
+
export function shouldYieldWorkerWorkBudget(input: {
|
|
99
|
+
meter: WorkerWorkBudgetMeter;
|
|
100
|
+
limits?: WorkerWorkBudgetYieldLimits;
|
|
101
|
+
projectedSubrequests?: number;
|
|
102
|
+
}): WorkerWorkBudgetYieldDecision {
|
|
103
|
+
const snapshot = input.meter.snapshot();
|
|
104
|
+
const limits = {
|
|
105
|
+
...WORKER_WORK_BUDGET_YIELD_LIMITS,
|
|
106
|
+
...(input.limits ?? {}),
|
|
107
|
+
};
|
|
108
|
+
if (snapshot.elapsedMs >= limits.elapsed) {
|
|
109
|
+
return { shouldYield: true, reason: 'elapsed', snapshot };
|
|
110
|
+
}
|
|
111
|
+
const subrequests =
|
|
112
|
+
snapshot.counts.provider +
|
|
113
|
+
snapshot.counts.receipt +
|
|
114
|
+
snapshot.counts.heartbeat +
|
|
115
|
+
snapshot.counts.sheet +
|
|
116
|
+
snapshot.counts.egress;
|
|
117
|
+
const projectedSubrequests = Math.max(
|
|
118
|
+
0,
|
|
119
|
+
Math.floor(input.projectedSubrequests ?? 0),
|
|
120
|
+
);
|
|
121
|
+
if (subrequests + projectedSubrequests >= limits.subrequest) {
|
|
122
|
+
return { shouldYield: true, reason: 'subrequest', snapshot };
|
|
123
|
+
}
|
|
124
|
+
for (const kind of WORK_BUDGET_EVENT_KINDS) {
|
|
125
|
+
if (snapshot.counts[kind] >= limits[kind]) {
|
|
126
|
+
return { shouldYield: true, reason: kind, snapshot };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { shouldYield: false, reason: null, snapshot };
|
|
130
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WorkerWorkBudgetYieldLimits } from './work-budget';
|
|
2
|
+
|
|
3
|
+
// Preview accounts can still enforce the free/internal-service subrequest
|
|
4
|
+
// ceiling even when wrangler config asks for more. This is a planner budget,
|
|
5
|
+
// not the advertised platform ceiling: unbatched tool chunks also spend
|
|
6
|
+
// subrequests on sheet writes, progress/ledger flushes, service-binding hops,
|
|
7
|
+
// and workflow replay around the visible provider calls. Individual log lines
|
|
8
|
+
// are buffered and batched into those flushes; they are not one subrequest each.
|
|
9
|
+
// Keep real headroom here;
|
|
10
|
+
// paid deployments may raise it only once the active account limit is proven by
|
|
11
|
+
// an E2E run, not by config alone.
|
|
12
|
+
export const WORKER_PLATFORM_SUBREQUEST_SOFT_LIMIT = 700;
|
|
13
|
+
|
|
14
|
+
// Fresh unbatched tool calls spend more than the visible provider RPC:
|
|
15
|
+
// durable receipt claim/complete, provider execution, route/service-binding
|
|
16
|
+
// hops, and row progress/sheet writes all share the Worker subrequest pool.
|
|
17
|
+
export const WORKER_PLATFORM_SUBREQUESTS_PER_UNBATCHED_TOOL_CALL = 8;
|
|
18
|
+
|
|
19
|
+
export const WORKER_PLATFORM_YIELD_LIMITS: Required<WorkerWorkBudgetYieldLimits> =
|
|
20
|
+
{
|
|
21
|
+
elapsed: 45_000,
|
|
22
|
+
subrequest: WORKER_PLATFORM_SUBREQUEST_SOFT_LIMIT,
|
|
23
|
+
provider: WORKER_PLATFORM_SUBREQUEST_SOFT_LIMIT,
|
|
24
|
+
tool: 1_500,
|
|
25
|
+
receipt: 2_000,
|
|
26
|
+
heartbeat: 1_000,
|
|
27
|
+
sheet: 800,
|
|
28
|
+
log: 1_500,
|
|
29
|
+
egress: 1_000,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function estimateUnbatchedToolCallSubrequests(input: {
|
|
33
|
+
toolCalls: number;
|
|
34
|
+
}): number {
|
|
35
|
+
return (
|
|
36
|
+
Math.max(0, Math.floor(input.toolCalls)) *
|
|
37
|
+
WORKER_PLATFORM_SUBREQUESTS_PER_UNBATCHED_TOOL_CALL
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -27,6 +27,7 @@ export type WorkflowRetryParamsRef = {
|
|
|
27
27
|
|
|
28
28
|
export type WorkflowRetryPlayParams = {
|
|
29
29
|
runId: string;
|
|
30
|
+
runAttempt?: number;
|
|
30
31
|
playId: string;
|
|
31
32
|
playName: string;
|
|
32
33
|
artifactStorageKey: string;
|
|
@@ -72,6 +73,7 @@ export type WorkflowRetryPlayParams = {
|
|
|
72
73
|
totalRows?: number;
|
|
73
74
|
coordinatorUrl?: string | null;
|
|
74
75
|
coordinatorInternalToken?: string | null;
|
|
76
|
+
runtimeTestFaultHeader?: string | null;
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
export type WorkflowRetryStatePayload<TParams = WorkflowRetryPlayParams> =
|
|
@@ -109,6 +109,41 @@ function resolvePlayRunIntegrationMode(
|
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
function normalizeTestPolicyOverrides(
|
|
113
|
+
value: unknown,
|
|
114
|
+
source: string,
|
|
115
|
+
): Record<string, unknown> {
|
|
116
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
117
|
+
return value as Record<string, unknown>;
|
|
118
|
+
}
|
|
119
|
+
throw new DeeplineError(
|
|
120
|
+
`${source} must be a JSON object.`,
|
|
121
|
+
undefined,
|
|
122
|
+
'INVALID_TEST_POLICY_OVERRIDES',
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseEnvTestPolicyOverrides(): Record<string, unknown> | undefined {
|
|
127
|
+
const raw =
|
|
128
|
+
typeof process !== 'undefined'
|
|
129
|
+
? process.env?.DEEPLINE_TEST_POLICY_OVERRIDES
|
|
130
|
+
: undefined;
|
|
131
|
+
const trimmed = raw?.trim();
|
|
132
|
+
if (!trimmed) return undefined;
|
|
133
|
+
let parsed: unknown;
|
|
134
|
+
try {
|
|
135
|
+
parsed = JSON.parse(trimmed);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
const detail = error instanceof Error ? ` ${error.message}` : '';
|
|
138
|
+
throw new DeeplineError(
|
|
139
|
+
`DEEPLINE_TEST_POLICY_OVERRIDES must be valid JSON.${detail}`,
|
|
140
|
+
undefined,
|
|
141
|
+
'INVALID_TEST_POLICY_OVERRIDES',
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return normalizeTestPolicyOverrides(parsed, 'DEEPLINE_TEST_POLICY_OVERRIDES');
|
|
145
|
+
}
|
|
146
|
+
|
|
112
147
|
function sleep(ms: number): Promise<void> {
|
|
113
148
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
114
149
|
}
|
|
@@ -1426,6 +1461,8 @@ export class DeeplineClient {
|
|
|
1426
1461
|
*/
|
|
1427
1462
|
async startPlayRun(request: StartPlayRunRequest): Promise<PlayRunStart> {
|
|
1428
1463
|
const integrationMode = resolvePlayRunIntegrationMode(request);
|
|
1464
|
+
const testPolicyOverrides =
|
|
1465
|
+
request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
|
|
1429
1466
|
const forceToolRefresh = request.forceToolRefresh === true;
|
|
1430
1467
|
const response = await this.http.post<Record<string, unknown>>(
|
|
1431
1468
|
'/api/v2/plays/run',
|
|
@@ -1470,6 +1507,7 @@ export class DeeplineClient {
|
|
|
1470
1507
|
// different profile pass `request.profile` explicitly.
|
|
1471
1508
|
...(request.profile ? { profile: request.profile } : {}),
|
|
1472
1509
|
...(integrationMode ? { integrationMode } : {}),
|
|
1510
|
+
...(testPolicyOverrides ? { testPolicyOverrides } : {}),
|
|
1473
1511
|
},
|
|
1474
1512
|
);
|
|
1475
1513
|
return normalizePlayRunStart(response);
|
|
@@ -1491,6 +1529,8 @@ export class DeeplineClient {
|
|
|
1491
1529
|
options?: { signal?: AbortSignal },
|
|
1492
1530
|
): AsyncGenerator<PlayLiveEvent> {
|
|
1493
1531
|
const integrationMode = resolvePlayRunIntegrationMode(request);
|
|
1532
|
+
const testPolicyOverrides =
|
|
1533
|
+
request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
|
|
1494
1534
|
const forceToolRefresh = request.forceToolRefresh === true;
|
|
1495
1535
|
const body = {
|
|
1496
1536
|
...(request.name ? { name: request.name } : {}),
|
|
@@ -1530,6 +1570,7 @@ export class DeeplineClient {
|
|
|
1530
1570
|
: {}),
|
|
1531
1571
|
...(request.profile ? { profile: request.profile } : {}),
|
|
1532
1572
|
...(integrationMode ? { integrationMode } : {}),
|
|
1573
|
+
...(testPolicyOverrides ? { testPolicyOverrides } : {}),
|
|
1533
1574
|
};
|
|
1534
1575
|
for await (const event of this.http.streamSse<PlayLiveEvent>(
|
|
1535
1576
|
'/api/v2/plays/run?stream=true',
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
SYNTHETIC_RUN_HEADER,
|
|
35
35
|
WORKER_CALLBACK_URL_OVERRIDE_HEADER,
|
|
36
36
|
} from '../../shared_libs/play-runtime/coordinator-headers.js';
|
|
37
|
+
import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '../../shared_libs/play-runtime/test-runtime-seams.js';
|
|
37
38
|
|
|
38
39
|
const MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
39
40
|
const COWORK_NETWORK_HINT =
|
|
@@ -240,16 +241,12 @@ export class HttpClient {
|
|
|
240
241
|
typeof process !== 'undefined'
|
|
241
242
|
? process.env?.DEEPLINE_COORDINATOR_URL
|
|
242
243
|
: undefined;
|
|
244
|
+
const coordinatorInternalToken =
|
|
245
|
+
typeof process !== 'undefined'
|
|
246
|
+
? process.env?.DEEPLINE_INTERNAL_TOKEN
|
|
247
|
+
: undefined;
|
|
243
248
|
if (coordinatorUrl?.trim()) {
|
|
244
249
|
headers[COORDINATOR_URL_OVERRIDE_HEADER] = coordinatorUrl.trim();
|
|
245
|
-
const coordinatorInternalToken =
|
|
246
|
-
typeof process !== 'undefined'
|
|
247
|
-
? process.env?.DEEPLINE_INTERNAL_TOKEN
|
|
248
|
-
: undefined;
|
|
249
|
-
if (coordinatorInternalToken?.trim()) {
|
|
250
|
-
headers[COORDINATOR_INTERNAL_TOKEN_HEADER] =
|
|
251
|
-
coordinatorInternalToken.trim();
|
|
252
|
-
}
|
|
253
250
|
}
|
|
254
251
|
const workerCallbackUrl =
|
|
255
252
|
typeof process !== 'undefined'
|
|
@@ -276,6 +273,24 @@ export class HttpClient {
|
|
|
276
273
|
if (syntheticRun && syntheticRun.trim() && syntheticRun.trim() !== '0') {
|
|
277
274
|
headers[SYNTHETIC_RUN_HEADER] = '1';
|
|
278
275
|
}
|
|
276
|
+
// Test-only runtime seam. The server validates both environment and value
|
|
277
|
+
// before honoring this header.
|
|
278
|
+
const runtimeTestFault =
|
|
279
|
+
typeof process !== 'undefined'
|
|
280
|
+
? process.env?.DEEPLINE_TEST_RUNTIME_FAULT
|
|
281
|
+
: undefined;
|
|
282
|
+
if (runtimeTestFault?.trim()) {
|
|
283
|
+
headers[PLAY_RUNTIME_TEST_FAULT_HEADER] = runtimeTestFault.trim();
|
|
284
|
+
}
|
|
285
|
+
if (
|
|
286
|
+
coordinatorInternalToken?.trim() &&
|
|
287
|
+
(coordinatorUrl?.trim() ||
|
|
288
|
+
workerCallbackUrl?.trim() ||
|
|
289
|
+
runtimeTestFault?.trim())
|
|
290
|
+
) {
|
|
291
|
+
headers[COORDINATOR_INTERNAL_TOKEN_HEADER] =
|
|
292
|
+
coordinatorInternalToken.trim();
|
|
293
|
+
}
|
|
279
294
|
return headers;
|
|
280
295
|
}
|
|
281
296
|
|
|
@@ -36,10 +36,18 @@ import type {
|
|
|
36
36
|
CompleteRuntimeReceiptsInput,
|
|
37
37
|
FailRuntimeReceiptInput,
|
|
38
38
|
FailRuntimeReceiptsInput,
|
|
39
|
+
HeartbeatRuntimeReceiptsInput,
|
|
40
|
+
ReleaseRuntimeReceiptInput,
|
|
39
41
|
RuntimeApiCallInput,
|
|
40
42
|
RuntimeApiCallResult,
|
|
41
43
|
RuntimeReceiptInput,
|
|
42
44
|
RuntimeReceiptsInput,
|
|
45
|
+
SheetAttemptHeartbeatInput,
|
|
46
|
+
SheetAttemptHeartbeatResult,
|
|
47
|
+
SheetAttemptReleaseInput,
|
|
48
|
+
SheetAttemptReleaseResult,
|
|
49
|
+
WorkReceiptReleaseInput,
|
|
50
|
+
WorkReceiptReleaseResult,
|
|
43
51
|
SheetDatasetRowKeysInput,
|
|
44
52
|
SheetDatasetRowKeysResult,
|
|
45
53
|
SheetDatasetRowsInput,
|
|
@@ -94,7 +102,7 @@ function requireBinding(): HarnessBinding {
|
|
|
94
102
|
if (!cachedBinding) {
|
|
95
103
|
throw new Error(
|
|
96
104
|
'[harness-stub] env.HARNESS is not wired. The coordinator must pass HARNESS in the WorkerLoader factory env. ' +
|
|
97
|
-
'See apps/play-runner-workers/src/coordinator-entry.ts
|
|
105
|
+
'See apps/play-runner-workers/src/coordinator-entry.ts -> loadDynamicPlayWorkerSync.',
|
|
98
106
|
);
|
|
99
107
|
}
|
|
100
108
|
return cachedBinding;
|
|
@@ -157,7 +165,7 @@ export async function harnessCompleteRuntimeReceipt(
|
|
|
157
165
|
|
|
158
166
|
export async function harnessCompleteRuntimeReceipts(
|
|
159
167
|
input: CompleteRuntimeReceiptsInput,
|
|
160
|
-
): Promise<WorkReceipt
|
|
168
|
+
): Promise<Array<WorkReceipt | null>> {
|
|
161
169
|
return requireBinding().completeRuntimeReceipts(input);
|
|
162
170
|
}
|
|
163
171
|
|
|
@@ -167,12 +175,24 @@ export async function harnessFailRuntimeReceipt(
|
|
|
167
175
|
return requireBinding().failRuntimeReceipt(input);
|
|
168
176
|
}
|
|
169
177
|
|
|
178
|
+
export async function harnessReleaseRuntimeReceipt(
|
|
179
|
+
input: ReleaseRuntimeReceiptInput,
|
|
180
|
+
): Promise<WorkReceipt | null> {
|
|
181
|
+
return requireBinding().releaseRuntimeReceipt(input);
|
|
182
|
+
}
|
|
183
|
+
|
|
170
184
|
export async function harnessFailRuntimeReceipts(
|
|
171
185
|
input: FailRuntimeReceiptsInput,
|
|
172
|
-
): Promise<WorkReceipt
|
|
186
|
+
): Promise<Array<WorkReceipt | null>> {
|
|
173
187
|
return requireBinding().failRuntimeReceipts(input);
|
|
174
188
|
}
|
|
175
189
|
|
|
190
|
+
export async function harnessHeartbeatRuntimeReceipts(
|
|
191
|
+
input: HeartbeatRuntimeReceiptsInput,
|
|
192
|
+
): Promise<Array<WorkReceipt | null>> {
|
|
193
|
+
return requireBinding().heartbeatRuntimeReceipts(input);
|
|
194
|
+
}
|
|
195
|
+
|
|
176
196
|
/**
|
|
177
197
|
* Read a bounded staged-file byte range through typed harness RPC. This is the
|
|
178
198
|
* only staged-file data path for per-play Workers; there is intentionally no
|
|
@@ -197,6 +217,24 @@ export async function harnessReadSheetDatasetRowKeys(
|
|
|
197
217
|
return requireBinding().readSheetDatasetRowKeys(input);
|
|
198
218
|
}
|
|
199
219
|
|
|
220
|
+
export async function harnessHeartbeatSheetAttempt(
|
|
221
|
+
input: SheetAttemptHeartbeatInput,
|
|
222
|
+
): Promise<SheetAttemptHeartbeatResult> {
|
|
223
|
+
return requireBinding().heartbeatSheetAttempt(input);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function harnessReleaseSheetAttempt(
|
|
227
|
+
input: SheetAttemptReleaseInput,
|
|
228
|
+
): Promise<SheetAttemptReleaseResult> {
|
|
229
|
+
return requireBinding().releaseSheetAttempt(input);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function harnessReleaseRuntimeReceipts(
|
|
233
|
+
input: WorkReceiptReleaseInput,
|
|
234
|
+
): Promise<WorkReceiptReleaseResult> {
|
|
235
|
+
return requireBinding().releaseRuntimeReceipts(input);
|
|
236
|
+
}
|
|
237
|
+
|
|
200
238
|
/**
|
|
201
239
|
* Warm Postgres sessions in the long-lived harness Worker. This preserves the
|
|
202
240
|
* map fast path without bundling the Neon client into every dynamic play.
|
|
@@ -224,6 +262,11 @@ export async function harnessStartSheetDataset(input: {
|
|
|
224
262
|
sheetContract: unknown;
|
|
225
263
|
rows: Array<Record<string, unknown>>;
|
|
226
264
|
runId: string;
|
|
265
|
+
attemptId?: string | null;
|
|
266
|
+
attemptOwnerRunId?: string | null;
|
|
267
|
+
attemptExpiresAt?: string | null;
|
|
268
|
+
attemptSeq?: number | null;
|
|
269
|
+
attemptLeaseTtlMs?: number | null;
|
|
227
270
|
inputOffset?: number;
|
|
228
271
|
force?: boolean;
|
|
229
272
|
userEmail?: string | null;
|
|
@@ -232,7 +275,12 @@ export async function harnessStartSheetDataset(input: {
|
|
|
232
275
|
skipped: number;
|
|
233
276
|
pendingRows: Array<Record<string, unknown>>;
|
|
234
277
|
completedRows: Array<Record<string, unknown>>;
|
|
278
|
+
blockedRows: Array<Record<string, unknown>>;
|
|
235
279
|
tableNamespace: string;
|
|
280
|
+
attemptId?: string;
|
|
281
|
+
attemptOwnerRunId?: string;
|
|
282
|
+
attemptExpiresAt?: string;
|
|
283
|
+
attemptSeq?: number;
|
|
236
284
|
timings?: Array<Record<string, unknown>>;
|
|
237
285
|
}> {
|
|
238
286
|
return requireBinding().startSheetDataset(input);
|
|
@@ -253,6 +301,11 @@ export async function harnessPersistCompletedSheetRows(input: {
|
|
|
253
301
|
rows: Array<Record<string, unknown>>;
|
|
254
302
|
outputFields: string[];
|
|
255
303
|
runId: string;
|
|
304
|
+
attemptId?: string | null;
|
|
305
|
+
attemptOwnerRunId?: string | null;
|
|
306
|
+
attemptExpiresAt?: string | null;
|
|
307
|
+
attemptSeq?: number | null;
|
|
308
|
+
forceFailedRows?: boolean;
|
|
256
309
|
userEmail?: string | null;
|
|
257
310
|
}): Promise<{ ok: true; rowsWritten: number; tableNamespace: string }> {
|
|
258
311
|
return requireBinding().persistCompletedMapRows(input);
|
|
@@ -105,10 +105,10 @@ export const SDK_RELEASE = {
|
|
|
105
105
|
// 0.1.154 removes the short-lived generated enrich StepOptions recompute
|
|
106
106
|
// fields shipped in 0.1.153.
|
|
107
107
|
// 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
|
|
108
|
-
version: '0.1.
|
|
108
|
+
version: '0.1.183',
|
|
109
109
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
110
110
|
supportPolicy: {
|
|
111
|
-
latest: '0.1.
|
|
111
|
+
latest: '0.1.183',
|
|
112
112
|
minimumSupported: '0.1.53',
|
|
113
113
|
deprecatedBelow: '0.1.53',
|
|
114
114
|
commandMinimumSupported: [
|
|
@@ -1010,6 +1010,8 @@ export interface StartPlayRunRequest {
|
|
|
1010
1010
|
profile?: string;
|
|
1011
1011
|
/** Optional per-run provider execution mode for eval/smoke runs. */
|
|
1012
1012
|
integrationMode?: 'live' | 'eval_stub' | 'fixture';
|
|
1013
|
+
/** Internal/dev-only runtime policy overrides for black-box durability tests. */
|
|
1014
|
+
testPolicyOverrides?: Record<string, unknown>;
|
|
1013
1015
|
}
|
|
1014
1016
|
|
|
1015
1017
|
/**
|