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
|
@@ -20,11 +20,15 @@ export type WorkflowStepLike = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export type ChildTerminalCoordinator = {
|
|
23
|
-
readChildTerminalState(
|
|
23
|
+
readChildTerminalState?(
|
|
24
24
|
parentRunId: string,
|
|
25
25
|
eventKey: string,
|
|
26
26
|
timeoutMs?: number,
|
|
27
27
|
): Promise<{ data?: unknown } | null>;
|
|
28
|
+
readRunTerminalState(
|
|
29
|
+
runId: string,
|
|
30
|
+
timeoutMs?: number,
|
|
31
|
+
): Promise<ChildRunTerminalSnapshotState | null>;
|
|
28
32
|
};
|
|
29
33
|
|
|
30
34
|
export type ChildPlayTerminalWaitResult = {
|
|
@@ -34,6 +38,52 @@ export type ChildPlayTerminalWaitResult = {
|
|
|
34
38
|
waitMs: number;
|
|
35
39
|
};
|
|
36
40
|
|
|
41
|
+
export type ChildRunTerminalSnapshotState = {
|
|
42
|
+
data?: unknown;
|
|
43
|
+
reason?: string;
|
|
44
|
+
status?: string;
|
|
45
|
+
runId?: string;
|
|
46
|
+
playName?: string;
|
|
47
|
+
parentRunId?: string | null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export class ChildPlayTerminalVerificationError extends Error {
|
|
51
|
+
readonly parentRunId: string;
|
|
52
|
+
readonly eventKey: string;
|
|
53
|
+
readonly cause: unknown;
|
|
54
|
+
|
|
55
|
+
constructor(input: {
|
|
56
|
+
parentRunId: string;
|
|
57
|
+
eventKey: string;
|
|
58
|
+
cause: unknown;
|
|
59
|
+
}) {
|
|
60
|
+
const causeMessage =
|
|
61
|
+
input.cause instanceof Error
|
|
62
|
+
? input.cause.message
|
|
63
|
+
: String(input.cause ?? '');
|
|
64
|
+
super(
|
|
65
|
+
`Child play terminal verification failed for parent run ${input.parentRunId} and event ${input.eventKey}.${
|
|
66
|
+
causeMessage ? ` Cause: ${causeMessage}` : ''
|
|
67
|
+
}`,
|
|
68
|
+
);
|
|
69
|
+
this.name = 'ChildPlayTerminalVerificationError';
|
|
70
|
+
this.parentRunId = input.parentRunId;
|
|
71
|
+
this.eventKey = input.eventKey;
|
|
72
|
+
this.cause = input.cause;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const CHILD_TERMINAL_DURABLE_VERIFY_GRACE_MS = 5_000;
|
|
77
|
+
const CHILD_TERMINAL_DURABLE_VERIFY_POLL_MS = 250;
|
|
78
|
+
const CHILD_TERMINAL_DURABLE_VERIFY_READ_TIMEOUT_MS = 3_000;
|
|
79
|
+
const CHILD_TERMINAL_DURABLE_VERIFY_MIN_READ_TIMEOUT_MS = 2_000;
|
|
80
|
+
const CHILD_TERMINAL_PERMANENT_RUNTIME_API_STATUS_RE =
|
|
81
|
+
/runtime API (400|401|403|404):/i;
|
|
82
|
+
const CHILD_TERMINAL_RETRYABLE_RUNTIME_API_STATUS_RE =
|
|
83
|
+
/runtime API (408|429|500|502|503|504|530):/i;
|
|
84
|
+
const CHILD_TERMINAL_RETRYABLE_ERROR_RE =
|
|
85
|
+
/timed out|timeout|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT/i;
|
|
86
|
+
|
|
37
87
|
export interface AwaitChildTerminalInput {
|
|
38
88
|
parentRunId: string;
|
|
39
89
|
workflowStep: WorkflowStepLike | undefined;
|
|
@@ -41,7 +91,7 @@ export interface AwaitChildTerminalInput {
|
|
|
41
91
|
playName: string;
|
|
42
92
|
key: string;
|
|
43
93
|
timeoutMs: number;
|
|
44
|
-
/** Coordinator binding for the terminal-state poll
|
|
94
|
+
/** Coordinator binding for the terminal-state poll and durable run verification. */
|
|
45
95
|
coordinator: ChildTerminalCoordinator | null;
|
|
46
96
|
now: () => number;
|
|
47
97
|
/** SHA-256 hex digest helper (same canonical hash entry.ts uses). */
|
|
@@ -84,6 +134,27 @@ function extractChildPlayOutput(status: Record<string, unknown>): unknown {
|
|
|
84
134
|
return result ?? null;
|
|
85
135
|
}
|
|
86
136
|
|
|
137
|
+
function terminalPayloadFromState(
|
|
138
|
+
state: { data?: unknown } | null | undefined,
|
|
139
|
+
): Record<string, unknown> | null {
|
|
140
|
+
return isRecord(state?.data) ? state.data : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isRetryableChildTerminalSnapshotError(error: unknown): boolean {
|
|
144
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
145
|
+
if (CHILD_TERMINAL_PERMANENT_RUNTIME_API_STATUS_RE.test(message)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return (
|
|
149
|
+
CHILD_TERMINAL_RETRYABLE_RUNTIME_API_STATUS_RE.test(message) ||
|
|
150
|
+
CHILD_TERMINAL_RETRYABLE_ERROR_RE.test(message)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function sleep(ms: number): Promise<void> {
|
|
155
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
156
|
+
}
|
|
157
|
+
|
|
87
158
|
async function childPlayEventKey(input: {
|
|
88
159
|
key: string;
|
|
89
160
|
workflowId: string;
|
|
@@ -120,9 +191,19 @@ async function pollParentChildTerminalState(input: {
|
|
|
120
191
|
input.timeoutMs - (input.now() - startedAt),
|
|
121
192
|
);
|
|
122
193
|
const waitMs = Math.min(remainingMs, 30_000);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
194
|
+
let state: { data?: unknown } | null;
|
|
195
|
+
try {
|
|
196
|
+
// prettier-ignore
|
|
197
|
+
state = await coordinator.readChildTerminalState(parentRunId, input.eventKey, waitMs);
|
|
198
|
+
} catch {
|
|
199
|
+
// The parent-child cache is a safety net for missed workflow events.
|
|
200
|
+
// Transient cache read failures must not beat the event wait plus durable
|
|
201
|
+
// child run verification path in the race.
|
|
202
|
+
if (remainingMs > 0) {
|
|
203
|
+
await sleep(Math.min(remainingMs, CHILD_TERMINAL_DURABLE_VERIFY_POLL_MS));
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
126
207
|
if (isRecord(state?.data)) {
|
|
127
208
|
return {
|
|
128
209
|
source: 'parent_child_terminal_cache',
|
|
@@ -134,6 +215,78 @@ async function pollParentChildTerminalState(input: {
|
|
|
134
215
|
return await new Promise(() => undefined);
|
|
135
216
|
}
|
|
136
217
|
|
|
218
|
+
async function readVerifiedChildTerminalPayload(input: {
|
|
219
|
+
coordinator: ChildTerminalCoordinator;
|
|
220
|
+
workflowId: string;
|
|
221
|
+
timeoutMs: number;
|
|
222
|
+
}): Promise<{
|
|
223
|
+
payload: Record<string, unknown> | null;
|
|
224
|
+
lastState: ChildRunTerminalSnapshotState | null;
|
|
225
|
+
lastError: unknown;
|
|
226
|
+
}> {
|
|
227
|
+
const graceMs = Math.max(
|
|
228
|
+
1,
|
|
229
|
+
Math.min(input.timeoutMs, CHILD_TERMINAL_DURABLE_VERIFY_GRACE_MS),
|
|
230
|
+
);
|
|
231
|
+
const pollMs = Math.max(
|
|
232
|
+
1,
|
|
233
|
+
Math.min(
|
|
234
|
+
CHILD_TERMINAL_DURABLE_VERIFY_POLL_MS,
|
|
235
|
+
Math.max(1, Math.floor(graceMs / 2)),
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
const deadlineMs = Date.now() + graceMs;
|
|
239
|
+
let lastState: ChildRunTerminalSnapshotState | null = null;
|
|
240
|
+
let lastError: unknown = null;
|
|
241
|
+
const minReadTimeoutMs =
|
|
242
|
+
graceMs >= CHILD_TERMINAL_DURABLE_VERIFY_MIN_READ_TIMEOUT_MS
|
|
243
|
+
? CHILD_TERMINAL_DURABLE_VERIFY_MIN_READ_TIMEOUT_MS
|
|
244
|
+
: 1;
|
|
245
|
+
while (Date.now() <= deadlineMs) {
|
|
246
|
+
const remainingMs = Math.max(0, deadlineMs - Date.now());
|
|
247
|
+
if (remainingMs < minReadTimeoutMs) break;
|
|
248
|
+
const readTimeoutMs = Math.max(
|
|
249
|
+
minReadTimeoutMs,
|
|
250
|
+
Math.min(remainingMs, CHILD_TERMINAL_DURABLE_VERIFY_READ_TIMEOUT_MS),
|
|
251
|
+
);
|
|
252
|
+
try {
|
|
253
|
+
const state = await input.coordinator.readRunTerminalState(
|
|
254
|
+
input.workflowId,
|
|
255
|
+
readTimeoutMs,
|
|
256
|
+
);
|
|
257
|
+
lastState = state ?? null;
|
|
258
|
+
const payload = terminalPayloadFromState(state);
|
|
259
|
+
if (payload) return { payload, lastState, lastError };
|
|
260
|
+
} catch (error) {
|
|
261
|
+
lastError = error;
|
|
262
|
+
if (!isRetryableChildTerminalSnapshotError(error)) {
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const sleepMs = Math.min(
|
|
267
|
+
pollMs,
|
|
268
|
+
Math.max(0, deadlineMs - Date.now() - minReadTimeoutMs),
|
|
269
|
+
);
|
|
270
|
+
if (sleepMs <= 0) break;
|
|
271
|
+
await sleep(sleepMs);
|
|
272
|
+
}
|
|
273
|
+
return { payload: null, lastState, lastError };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function describeSnapshotState(
|
|
277
|
+
state: ChildRunTerminalSnapshotState | null,
|
|
278
|
+
): string {
|
|
279
|
+
if (!state) return 'no snapshot response';
|
|
280
|
+
const parts = [
|
|
281
|
+
state.reason ? `reason=${state.reason}` : null,
|
|
282
|
+
state.status ? `status=${state.status}` : null,
|
|
283
|
+
state.runId ? `runId=${state.runId}` : null,
|
|
284
|
+
state.playName ? `playName=${state.playName}` : null,
|
|
285
|
+
state.parentRunId ? `parentRunId=${state.parentRunId}` : null,
|
|
286
|
+
].filter((part): part is string => Boolean(part));
|
|
287
|
+
return parts.length > 0 ? parts.join(' ') : 'empty snapshot response';
|
|
288
|
+
}
|
|
289
|
+
|
|
137
290
|
/**
|
|
138
291
|
* Block until the child run reaches a terminal state, via the workflow event
|
|
139
292
|
* wait raced against the coordinator terminal-state poll. Throws on a non-
|
|
@@ -153,6 +306,15 @@ export async function awaitChildTerminal(
|
|
|
153
306
|
workflowId: input.workflowId,
|
|
154
307
|
hashJson: input.hashJson,
|
|
155
308
|
});
|
|
309
|
+
if (!input.coordinator?.readRunTerminalState) {
|
|
310
|
+
throw new ChildPlayTerminalVerificationError({
|
|
311
|
+
parentRunId: input.parentRunId,
|
|
312
|
+
eventKey,
|
|
313
|
+
cause: new Error(
|
|
314
|
+
`Child run ${input.workflowId} terminal snapshot reader was not available.`,
|
|
315
|
+
),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
156
318
|
const eventPromise = input.workflowStep
|
|
157
319
|
.waitForEvent(`child_play_terminal:${eventKey}`, {
|
|
158
320
|
type: integrationEventType(eventKey),
|
|
@@ -173,7 +335,42 @@ export async function awaitChildTerminal(
|
|
|
173
335
|
now: input.now,
|
|
174
336
|
}),
|
|
175
337
|
]);
|
|
176
|
-
|
|
338
|
+
let verified:
|
|
339
|
+
| {
|
|
340
|
+
payload: Record<string, unknown> | null;
|
|
341
|
+
lastState: ChildRunTerminalSnapshotState | null;
|
|
342
|
+
lastError: unknown;
|
|
343
|
+
}
|
|
344
|
+
| null = null;
|
|
345
|
+
try {
|
|
346
|
+
verified = await readVerifiedChildTerminalPayload({
|
|
347
|
+
coordinator: input.coordinator,
|
|
348
|
+
workflowId: input.workflowId,
|
|
349
|
+
timeoutMs: input.timeoutMs,
|
|
350
|
+
});
|
|
351
|
+
} catch (error) {
|
|
352
|
+
throw new ChildPlayTerminalVerificationError({
|
|
353
|
+
parentRunId: input.parentRunId,
|
|
354
|
+
eventKey,
|
|
355
|
+
cause: error,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (!verified?.payload) {
|
|
359
|
+
const lastErrorMessage =
|
|
360
|
+
verified?.lastError instanceof Error
|
|
361
|
+
? ` lastError=${verified.lastError.message}`
|
|
362
|
+
: verified?.lastError
|
|
363
|
+
? ` lastError=${String(verified.lastError)}`
|
|
364
|
+
: '';
|
|
365
|
+
throw new ChildPlayTerminalVerificationError({
|
|
366
|
+
parentRunId: input.parentRunId,
|
|
367
|
+
eventKey,
|
|
368
|
+
cause: new Error(
|
|
369
|
+
`Child run ${input.workflowId} terminal snapshot was not available after durable verification grace; ${describeSnapshotState(verified?.lastState ?? null)}.${lastErrorMessage}`,
|
|
370
|
+
),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
const payload = verified.payload;
|
|
177
374
|
const status = String(payload.status ?? '').toLowerCase();
|
|
178
375
|
if (status === 'completed') {
|
|
179
376
|
return {
|
|
@@ -142,6 +142,12 @@ export async function submitChildPlayThroughCoordinator(
|
|
|
142
142
|
input.req.runId,
|
|
143
143
|
)}/submit-child`;
|
|
144
144
|
const serializedBody = JSON.stringify(body);
|
|
145
|
+
const requestId = input.makeRequestId();
|
|
146
|
+
const idempotencyKey =
|
|
147
|
+
typeof body.childIdempotencyKey === 'string' &&
|
|
148
|
+
body.childIdempotencyKey.trim()
|
|
149
|
+
? body.childIdempotencyKey.trim()
|
|
150
|
+
: requestId;
|
|
145
151
|
let lastError: unknown = null;
|
|
146
152
|
|
|
147
153
|
for (let attempt = 0; attempt <= retryDelaysMs.length; attempt += 1) {
|
|
@@ -150,7 +156,8 @@ export async function submitChildPlayThroughCoordinator(
|
|
|
150
156
|
response = await fetchFn(url, {
|
|
151
157
|
method: 'POST',
|
|
152
158
|
headers: {
|
|
153
|
-
'x-deepline-request-id':
|
|
159
|
+
'x-deepline-request-id': requestId,
|
|
160
|
+
'x-deepline-idempotency-key': idempotencyKey,
|
|
154
161
|
...input.coordinatorRequestHeaders({
|
|
155
162
|
runId: input.req.runId,
|
|
156
163
|
contentType: 'application/json',
|