deepline 0.1.79 → 0.1.81

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.
Files changed (29) hide show
  1. package/README.md +2 -1
  2. package/dist/cli/index.js +76 -42
  3. package/dist/cli/index.mjs +76 -42
  4. package/dist/index.d.mts +9 -1
  5. package/dist/index.d.ts +9 -1
  6. package/dist/index.js +13 -10
  7. package/dist/index.mjs +13 -10
  8. package/dist/repo/apps/play-runner-workers/src/child-play-await.ts +192 -0
  9. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +1103 -1617
  10. package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +506 -654
  11. package/dist/repo/apps/play-runner-workers/src/entry.ts +1148 -598
  12. package/dist/repo/apps/play-runner-workers/src/runtime/tool-http-errors.ts +43 -1
  13. package/dist/repo/apps/play-runner-workers/src/workflow-retry-state.ts +8 -2
  14. package/dist/repo/sdk/src/client.ts +15 -8
  15. package/dist/repo/sdk/src/release.ts +2 -2
  16. package/dist/repo/sdk/src/types.ts +5 -0
  17. package/dist/repo/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +231 -0
  18. package/dist/repo/shared_libs/play-runtime/governor/governor.ts +376 -0
  19. package/dist/repo/shared_libs/play-runtime/governor/policy.ts +179 -0
  20. package/dist/repo/shared_libs/play-runtime/governor/rate-state-backend.ts +87 -0
  21. package/dist/repo/shared_libs/play-runtime/run-failure.ts +12 -0
  22. package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +24 -0
  23. package/dist/repo/shared_libs/play-runtime/submit-limits.ts +35 -0
  24. package/dist/repo/shared_libs/plays/bundling/index.ts +4 -12
  25. package/dist/repo/shared_libs/plays/bundling/limits.ts +29 -0
  26. package/dist/repo/shared_libs/plays/static-pipeline.ts +56 -3
  27. package/dist/repo/shared_libs/temporal/constants.ts +38 -0
  28. package/package.json +1 -1
  29. package/dist/repo/shared_libs/play-runtime/tool-batch-executor.ts +0 -149
package/dist/index.d.ts CHANGED
@@ -305,6 +305,10 @@ interface ToolDefinition {
305
305
  operationId?: string;
306
306
  /** Alternative names that resolve to this tool. */
307
307
  operationAliases?: string[];
308
+ /** Whether detailed input schema is available from `tools describe`. */
309
+ hasInputSchema?: boolean;
310
+ /** Whether detailed output schema is available from `tools describe`. */
311
+ hasOutputSchema?: boolean;
308
312
  /** JSON Schema describing the tool's input parameters. */
309
313
  inputSchema?: Record<string, unknown>;
310
314
  /** JSON Schema describing the tool's output shape. */
@@ -796,6 +800,7 @@ interface PlayListItem {
796
800
  currentPublishedVersion?: number | null;
797
801
  tableNamespace?: string | null;
798
802
  isDraftDirty?: boolean;
803
+ hasInputSchema?: boolean;
799
804
  inputSchema?: Record<string, unknown> | null;
800
805
  outputSchema?: Record<string, unknown> | null;
801
806
  staticPipeline?: unknown;
@@ -1263,6 +1268,7 @@ declare class DeeplineClient {
1263
1268
  categories?: string;
1264
1269
  grep?: string;
1265
1270
  grepMode?: 'all' | 'any' | 'phrase';
1271
+ compact?: boolean;
1266
1272
  }): Promise<ToolDefinition[]>;
1267
1273
  /**
1268
1274
  * Search available tools using Deepline's ranked backend search.
@@ -1676,7 +1682,9 @@ declare class DeeplineClient {
1676
1682
  * @param name - Play name
1677
1683
  * @returns Version list (newest first)
1678
1684
  */
1679
- listPlayVersions(name: string): Promise<PlayRevisionSummary[]>;
1685
+ listPlayVersions(name: string, options?: {
1686
+ full?: boolean;
1687
+ }): Promise<PlayRevisionSummary[]>;
1680
1688
  /**
1681
1689
  * Make a play revision live.
1682
1690
  *
package/dist/index.js CHANGED
@@ -241,10 +241,10 @@ var import_node_path2 = require("path");
241
241
 
242
242
  // src/release.ts
243
243
  var SDK_RELEASE = {
244
- version: "0.1.79",
244
+ version: "0.1.81",
245
245
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
246
246
  supportPolicy: {
247
- latest: "0.1.79",
247
+ latest: "0.1.81",
248
248
  minimumSupported: "0.1.53",
249
249
  deprecatedBelow: "0.1.53"
250
250
  }
@@ -881,6 +881,7 @@ var DeeplineClient = class {
881
881
  params.set("grep", options.grep.trim());
882
882
  params.set("grep_mode", options.grepMode ?? "all");
883
883
  }
884
+ params.set("compact", options?.compact === true ? "true" : "false");
884
885
  const suffix = params.toString() ? `?${params.toString()}` : "";
885
886
  const res = await this.http.get(
886
887
  `/api/v2/tools${suffix}`
@@ -1320,7 +1321,7 @@ var DeeplineClient = class {
1320
1321
  }
1321
1322
  const query = params.size > 0 ? `?${params.toString()}` : "";
1322
1323
  const response = await this.http.get(
1323
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}${query}`
1324
+ `/api/v2/runs/${encodeURIComponent(workflowId)}${query}`
1324
1325
  );
1325
1326
  return normalizePlayStatus(response);
1326
1327
  }
@@ -1332,10 +1333,10 @@ var DeeplineClient = class {
1332
1333
  */
1333
1334
  async *streamPlayRunEvents(workflowId, options) {
1334
1335
  const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
1335
- const params = new URLSearchParams({ stream: "true" });
1336
+ const params = new URLSearchParams();
1336
1337
  params.set("mode", options?.mode ?? "cli");
1337
1338
  for await (const event of this.http.streamSse(
1338
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`,
1339
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/tail?${params.toString()}`,
1339
1340
  { signal: options?.signal, headers }
1340
1341
  )) {
1341
1342
  if (event.scope === "play") {
@@ -1357,7 +1358,7 @@ var DeeplineClient = class {
1357
1358
  */
1358
1359
  async cancelPlay(workflowId) {
1359
1360
  await this.http.request(
1360
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
1361
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
1361
1362
  { method: "POST" }
1362
1363
  );
1363
1364
  }
@@ -1369,7 +1370,7 @@ var DeeplineClient = class {
1369
1370
  */
1370
1371
  async stopPlay(workflowId, options) {
1371
1372
  return this.http.post(
1372
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
1373
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
1373
1374
  options?.reason ? { reason: options.reason } : {}
1374
1375
  );
1375
1376
  }
@@ -1436,6 +1437,7 @@ var DeeplineClient = class {
1436
1437
  if (status) {
1437
1438
  params.set("status", status);
1438
1439
  }
1440
+ params.set("compact", "true");
1439
1441
  const response = await this.http.get(
1440
1442
  `/api/v2/runs?${params.toString()}`
1441
1443
  );
@@ -1488,7 +1490,7 @@ var DeeplineClient = class {
1488
1490
  * ```
1489
1491
  */
1490
1492
  async getRunLogs(runId, options) {
1491
- const status = await this.getRunStatus(runId);
1493
+ const status = await this.getRunStatus(runId, { full: true });
1492
1494
  const logs = status.progress?.logs ?? [];
1493
1495
  const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
1494
1496
  const entries = logs.slice(Math.max(0, logs.length - limit));
@@ -1600,10 +1602,11 @@ var DeeplineClient = class {
1600
1602
  * @param name - Play name
1601
1603
  * @returns Version list (newest first)
1602
1604
  */
1603
- async listPlayVersions(name) {
1605
+ async listPlayVersions(name, options) {
1604
1606
  const encodedName = encodeURIComponent(name);
1607
+ const suffix = options?.full ? "?full=true" : "";
1605
1608
  const response = await this.http.get(
1606
- `/api/v2/plays/${encodedName}/versions`
1609
+ `/api/v2/plays/${encodedName}/versions${suffix}`
1607
1610
  );
1608
1611
  return response.versions ?? [];
1609
1612
  }
package/dist/index.mjs CHANGED
@@ -179,10 +179,10 @@ import { join as join2 } from "path";
179
179
 
180
180
  // src/release.ts
181
181
  var SDK_RELEASE = {
182
- version: "0.1.79",
182
+ version: "0.1.81",
183
183
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
184
184
  supportPolicy: {
185
- latest: "0.1.79",
185
+ latest: "0.1.81",
186
186
  minimumSupported: "0.1.53",
187
187
  deprecatedBelow: "0.1.53"
188
188
  }
@@ -819,6 +819,7 @@ var DeeplineClient = class {
819
819
  params.set("grep", options.grep.trim());
820
820
  params.set("grep_mode", options.grepMode ?? "all");
821
821
  }
822
+ params.set("compact", options?.compact === true ? "true" : "false");
822
823
  const suffix = params.toString() ? `?${params.toString()}` : "";
823
824
  const res = await this.http.get(
824
825
  `/api/v2/tools${suffix}`
@@ -1258,7 +1259,7 @@ var DeeplineClient = class {
1258
1259
  }
1259
1260
  const query = params.size > 0 ? `?${params.toString()}` : "";
1260
1261
  const response = await this.http.get(
1261
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}${query}`
1262
+ `/api/v2/runs/${encodeURIComponent(workflowId)}${query}`
1262
1263
  );
1263
1264
  return normalizePlayStatus(response);
1264
1265
  }
@@ -1270,10 +1271,10 @@ var DeeplineClient = class {
1270
1271
  */
1271
1272
  async *streamPlayRunEvents(workflowId, options) {
1272
1273
  const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
1273
- const params = new URLSearchParams({ stream: "true" });
1274
+ const params = new URLSearchParams();
1274
1275
  params.set("mode", options?.mode ?? "cli");
1275
1276
  for await (const event of this.http.streamSse(
1276
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`,
1277
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/tail?${params.toString()}`,
1277
1278
  { signal: options?.signal, headers }
1278
1279
  )) {
1279
1280
  if (event.scope === "play") {
@@ -1295,7 +1296,7 @@ var DeeplineClient = class {
1295
1296
  */
1296
1297
  async cancelPlay(workflowId) {
1297
1298
  await this.http.request(
1298
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
1299
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
1299
1300
  { method: "POST" }
1300
1301
  );
1301
1302
  }
@@ -1307,7 +1308,7 @@ var DeeplineClient = class {
1307
1308
  */
1308
1309
  async stopPlay(workflowId, options) {
1309
1310
  return this.http.post(
1310
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
1311
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
1311
1312
  options?.reason ? { reason: options.reason } : {}
1312
1313
  );
1313
1314
  }
@@ -1374,6 +1375,7 @@ var DeeplineClient = class {
1374
1375
  if (status) {
1375
1376
  params.set("status", status);
1376
1377
  }
1378
+ params.set("compact", "true");
1377
1379
  const response = await this.http.get(
1378
1380
  `/api/v2/runs?${params.toString()}`
1379
1381
  );
@@ -1426,7 +1428,7 @@ var DeeplineClient = class {
1426
1428
  * ```
1427
1429
  */
1428
1430
  async getRunLogs(runId, options) {
1429
- const status = await this.getRunStatus(runId);
1431
+ const status = await this.getRunStatus(runId, { full: true });
1430
1432
  const logs = status.progress?.logs ?? [];
1431
1433
  const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
1432
1434
  const entries = logs.slice(Math.max(0, logs.length - limit));
@@ -1538,10 +1540,11 @@ var DeeplineClient = class {
1538
1540
  * @param name - Play name
1539
1541
  * @returns Version list (newest first)
1540
1542
  */
1541
- async listPlayVersions(name) {
1543
+ async listPlayVersions(name, options) {
1542
1544
  const encodedName = encodeURIComponent(name);
1545
+ const suffix = options?.full ? "?full=true" : "";
1543
1546
  const response = await this.http.get(
1544
- `/api/v2/plays/${encodedName}/versions`
1547
+ `/api/v2/plays/${encodedName}/versions${suffix}`
1545
1548
  );
1546
1549
  return response.versions ?? [];
1547
1550
  }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Child-play terminal await — the race between a Cloudflare Workflow
3
+ * `waitForEvent('child_play_terminal:...')` and a coordinator terminal-state
4
+ * poll, extracted from entry.ts so the runtime keeps a small, testable surface
5
+ * for "block until this child run reaches a terminal state".
6
+ *
7
+ * Behavior is identical to the inline implementation it replaces: arm the
8
+ * workflow event wait, race it against `pollParentChildTerminalState`, and
9
+ * resolve on whichever reports the child's terminal status first. The poll
10
+ * never resolves on a non-terminal/absent state (it returns a never-settling
11
+ * promise on timeout) so the event wait remains the primary path and the poll
12
+ * is a coordinator-side safety net for missed signals.
13
+ */
14
+
15
+ export type WorkflowStepLike = {
16
+ waitForEvent: (
17
+ name: string,
18
+ options: { type: string; timeout: string },
19
+ ) => Promise<{ payload: unknown }>;
20
+ };
21
+
22
+ export type ChildTerminalCoordinator = {
23
+ readChildTerminalState(
24
+ parentRunId: string,
25
+ eventKey: string,
26
+ timeoutMs?: number,
27
+ ): Promise<{ data?: unknown } | null>;
28
+ };
29
+
30
+ export type ChildPlayTerminalWaitResult = {
31
+ output: unknown;
32
+ source: 'workflow_event' | 'parent_child_terminal_cache';
33
+ attempts?: number;
34
+ waitMs: number;
35
+ };
36
+
37
+ export interface AwaitChildTerminalInput {
38
+ parentRunId: string;
39
+ workflowStep: WorkflowStepLike | undefined;
40
+ workflowId: string;
41
+ playName: string;
42
+ key: string;
43
+ timeoutMs: number;
44
+ /** Coordinator binding for the terminal-state poll. Null disables the poll. */
45
+ coordinator: ChildTerminalCoordinator | null;
46
+ now: () => number;
47
+ /** SHA-256 hex digest helper (same canonical hash entry.ts uses). */
48
+ hashJson: (value: unknown) => Promise<string>;
49
+ }
50
+
51
+ function isRecord(value: unknown): value is Record<string, unknown> {
52
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
53
+ }
54
+
55
+ function workflowEventType(name: string): string {
56
+ const normalized = name
57
+ .trim()
58
+ .replace(/[^A-Za-z0-9_-]+/g, '_')
59
+ .replace(/^_+|_+$/g, '')
60
+ .slice(0, 100);
61
+ return normalized || 'deepline_event';
62
+ }
63
+
64
+ function integrationEventType(eventKey: string): string {
65
+ return workflowEventType(`integration_event_${eventKey}`);
66
+ }
67
+
68
+ function workflowTimeoutFromMs(timeoutMs: number): string {
69
+ const seconds = Math.max(1, Math.ceil(timeoutMs / 1000));
70
+ return `${seconds} second${seconds === 1 ? '' : 's'}`;
71
+ }
72
+
73
+ function readChildTerminalPayload(value: unknown): Record<string, unknown> {
74
+ if (!isRecord(value)) return {};
75
+ const data = value.data;
76
+ return isRecord(data) ? data : value;
77
+ }
78
+
79
+ function extractChildPlayOutput(status: Record<string, unknown>): unknown {
80
+ const result = status.result;
81
+ if (isRecord(result) && 'output' in result) {
82
+ return result.output;
83
+ }
84
+ return result ?? null;
85
+ }
86
+
87
+ async function childPlayEventKey(input: {
88
+ key: string;
89
+ workflowId: string;
90
+ hashJson: (value: unknown) => Promise<string>;
91
+ }): Promise<string> {
92
+ const readableKey = workflowEventType(input.key).slice(0, 40);
93
+ const digest = (
94
+ await input.hashJson({ key: input.key, workflowId: input.workflowId })
95
+ ).slice(0, 32);
96
+ return `child_play_${digest}_${readableKey}`;
97
+ }
98
+
99
+ async function pollParentChildTerminalState(input: {
100
+ coordinator: ChildTerminalCoordinator | null;
101
+ parentRunId: string;
102
+ eventKey: string;
103
+ timeoutMs: number;
104
+ now: () => number;
105
+ }): Promise<{
106
+ source: 'parent_child_terminal_cache';
107
+ payload: Record<string, unknown>;
108
+ attempts: number;
109
+ }> {
110
+ const { coordinator, parentRunId } = input;
111
+ if (!coordinator?.readChildTerminalState || !parentRunId) {
112
+ return await new Promise(() => undefined);
113
+ }
114
+ const startedAt = input.now();
115
+ let attempts = 0;
116
+ while (input.now() - startedAt < input.timeoutMs) {
117
+ attempts += 1;
118
+ const remainingMs = Math.max(
119
+ 0,
120
+ input.timeoutMs - (input.now() - startedAt),
121
+ );
122
+ const waitMs = Math.min(remainingMs, 30_000);
123
+ const state = await coordinator
124
+ .readChildTerminalState(parentRunId, input.eventKey, waitMs)
125
+ .catch(() => null);
126
+ if (isRecord(state?.data)) {
127
+ return {
128
+ source: 'parent_child_terminal_cache',
129
+ payload: state.data,
130
+ attempts,
131
+ };
132
+ }
133
+ }
134
+ return await new Promise(() => undefined);
135
+ }
136
+
137
+ /**
138
+ * Block until the child run reaches a terminal state, via the workflow event
139
+ * wait raced against the coordinator terminal-state poll. Throws on a non-
140
+ * completed terminal status.
141
+ */
142
+ export async function awaitChildTerminal(
143
+ input: AwaitChildTerminalInput,
144
+ ): Promise<ChildPlayTerminalWaitResult> {
145
+ if (!input.workflowStep) {
146
+ throw new Error(
147
+ 'ctx.runPlay child waits require the cf-workflows runtime event scheduler.',
148
+ );
149
+ }
150
+ const waitStartedAt = input.now();
151
+ const eventKey = await childPlayEventKey({
152
+ key: input.key,
153
+ workflowId: input.workflowId,
154
+ hashJson: input.hashJson,
155
+ });
156
+ const eventPromise = input.workflowStep
157
+ .waitForEvent(`child_play_terminal:${eventKey}`, {
158
+ type: integrationEventType(eventKey),
159
+ timeout: workflowTimeoutFromMs(input.timeoutMs),
160
+ })
161
+ .then((event) => ({
162
+ source: 'workflow_event' as const,
163
+ payload: readChildTerminalPayload(event.payload),
164
+ attempts: undefined as number | undefined,
165
+ }));
166
+ const terminal = await Promise.race([
167
+ eventPromise,
168
+ pollParentChildTerminalState({
169
+ coordinator: input.coordinator,
170
+ parentRunId: input.parentRunId,
171
+ eventKey,
172
+ timeoutMs: Math.min(input.timeoutMs, 30_000),
173
+ now: input.now,
174
+ }),
175
+ ]);
176
+ const payload = terminal.payload;
177
+ const status = String(payload.status ?? '').toLowerCase();
178
+ if (status === 'completed') {
179
+ return {
180
+ output: extractChildPlayOutput(payload),
181
+ source: terminal.source,
182
+ attempts: terminal.attempts,
183
+ waitMs: input.now() - waitStartedAt,
184
+ };
185
+ }
186
+ const error = isRecord(payload.error) ? payload.error : null;
187
+ const message =
188
+ (typeof error?.message === 'string' && error.message.trim()) ||
189
+ (typeof payload.error === 'string' && payload.error.trim()) ||
190
+ `Child play ${input.playName} (${input.workflowId}) finished with status ${status || 'unknown'}.`;
191
+ throw new Error(message);
192
+ }