deepline 0.1.20 → 0.1.22

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/index.js CHANGED
@@ -241,7 +241,7 @@ function resolveConfig(options) {
241
241
  }
242
242
 
243
243
  // src/version.ts
244
- var SDK_VERSION = "0.1.20";
244
+ var SDK_VERSION = "0.1.22";
245
245
  var SDK_API_CONTRACT = "2026-05-runs-v2";
246
246
 
247
247
  // ../shared_libs/play-runtime/coordinator-headers.ts
@@ -324,7 +324,7 @@ var HttpClient = class {
324
324
  const response = await fetch(candidateUrl, {
325
325
  method,
326
326
  headers,
327
- body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
327
+ body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
328
328
  signal: controller.signal
329
329
  });
330
330
  clearTimeout(timeoutId);
@@ -443,6 +443,13 @@ var HttpClient = class {
443
443
  headers
444
444
  });
445
445
  }
446
+ async postFormData(path, formData, headers) {
447
+ return this.request(path, {
448
+ method: "POST",
449
+ formData,
450
+ headers
451
+ });
452
+ }
446
453
  /**
447
454
  * Send a DELETE request.
448
455
  *
@@ -526,7 +533,7 @@ function isRecord(value) {
526
533
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
527
534
  }
528
535
  function normalizePlayStatus(raw) {
529
- const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
536
+ const status = typeof raw.status === "string" ? raw.status : "running";
530
537
  const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
531
538
  return {
532
539
  ...raw,
@@ -534,22 +541,86 @@ function normalizePlayStatus(raw) {
534
541
  status
535
542
  };
536
543
  }
537
- function mapLegacyTemporalStatus(status) {
538
- switch (status.trim().toUpperCase()) {
539
- case "PENDING":
540
- return "queued";
541
- case "COMPLETED":
542
- return "completed";
543
- case "FAILED":
544
- return "failed";
545
- case "CANCELLED":
546
- case "TERMINATED":
547
- case "TIMED_OUT":
548
- return "cancelled";
549
- case "RUNNING":
550
- default:
551
- return "running";
544
+ function decodeBase64Bytes(value) {
545
+ const binary = atob(value);
546
+ const bytes = new Uint8Array(binary.length);
547
+ for (let index = 0; index < binary.length; index += 1) {
548
+ bytes[index] = binary.charCodeAt(index);
549
+ }
550
+ return bytes;
551
+ }
552
+ function readStringArray(value) {
553
+ return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
554
+ }
555
+ function getPlayLiveEventPayload(event) {
556
+ return event.payload && typeof event.payload === "object" ? event.payload : {};
557
+ }
558
+ function normalizeLiveStatus(value) {
559
+ if (value === "queued" || value === "running" || value === "waiting" || value === "completed" || value === "failed" || value === "cancelled") {
560
+ return value;
561
+ }
562
+ return null;
563
+ }
564
+ function updatePlayLiveStatusState(state, event) {
565
+ const payload = getPlayLiveEventPayload(event);
566
+ if (event.type === "play.run.log") {
567
+ state.logs.push(...readStringArray(payload.lines));
568
+ return null;
569
+ }
570
+ if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
571
+ return null;
572
+ }
573
+ const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : state.runId;
574
+ const status = normalizeLiveStatus(payload.status) ?? state.status;
575
+ const logs = readStringArray(payload.logs);
576
+ if (logs.length > 0 || event.type === "play.run.snapshot") {
577
+ state.logs = logs;
578
+ }
579
+ if ("result" in payload) {
580
+ state.result = payload.result;
552
581
  }
582
+ if (typeof payload.error === "string" && payload.error.trim()) {
583
+ state.error = payload.error;
584
+ }
585
+ state.runId = runId;
586
+ state.status = status;
587
+ const progressRecord = payload.progress && typeof payload.progress === "object" && !Array.isArray(payload.progress) ? payload.progress : {};
588
+ const next = {
589
+ ...payload,
590
+ runId,
591
+ status,
592
+ progress: {
593
+ ...progressRecord,
594
+ status: typeof progressRecord.status === "string" ? progressRecord.status : status,
595
+ logs: state.logs,
596
+ ...state.error ? { error: state.error } : {}
597
+ },
598
+ ..."result" in state ? { result: state.result } : {}
599
+ };
600
+ state.latest = next;
601
+ return next;
602
+ }
603
+ function playRunResultFromStatus(status, startedAt, fallbackRunId) {
604
+ return {
605
+ success: status.status === "completed",
606
+ runId: status.runId || fallbackRunId,
607
+ result: status.result,
608
+ logs: status.progress?.logs ?? [],
609
+ durationMs: Date.now() - startedAt,
610
+ error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
611
+ };
612
+ }
613
+ function playRunStatusFromState(state) {
614
+ return {
615
+ runId: state.runId,
616
+ status: state.status,
617
+ progress: {
618
+ status: state.status,
619
+ logs: state.logs,
620
+ ...state.error ? { error: state.error } : {}
621
+ },
622
+ ..."result" in state ? { result: state.result } : {}
623
+ };
553
624
  }
554
625
  var DeeplineClient = class {
555
626
  http;
@@ -660,7 +731,7 @@ var DeeplineClient = class {
660
731
  /**
661
732
  * Search available tools using Deepline's ranked backend search.
662
733
  *
663
- * This is the same discovery surface used by the legacy CLI: it ranks across
734
+ * This is the same discovery surface used by the CLI: it ranks across
664
735
  * tool metadata, categories, agent guidance, and input schema fields.
665
736
  */
666
737
  async searchTools(options = {}) {
@@ -753,7 +824,7 @@ var DeeplineClient = class {
753
824
  * `progress.logs`; they are not part of the user output object.
754
825
  *
755
826
  * @param request - Play run configuration (name, code, input, etc.)
756
- * @returns Workflow metadata including the `workflowId` for status polling
827
+ * @returns Run metadata including the public `workflowId`
757
828
  *
758
829
  * @example
759
830
  * ```typescript
@@ -1002,9 +1073,34 @@ var DeeplineClient = class {
1002
1073
  * ```
1003
1074
  */
1004
1075
  async stagePlayFiles(files) {
1005
- const response = await this.http.post(
1076
+ const formData = new FormData();
1077
+ formData.set(
1078
+ "metadata",
1079
+ JSON.stringify({
1080
+ files: files.map((file, index) => ({
1081
+ index,
1082
+ logicalPath: file.logicalPath,
1083
+ contentHash: file.contentHash,
1084
+ contentType: file.contentType,
1085
+ bytes: file.bytes
1086
+ }))
1087
+ })
1088
+ );
1089
+ for (const [index, file] of files.entries()) {
1090
+ const bytes = decodeBase64Bytes(file.contentBase64);
1091
+ const body = bytes.buffer.slice(
1092
+ bytes.byteOffset,
1093
+ bytes.byteOffset + bytes.byteLength
1094
+ );
1095
+ formData.set(
1096
+ `file:${index}`,
1097
+ new Blob([body], { type: file.contentType }),
1098
+ file.logicalPath
1099
+ );
1100
+ }
1101
+ const response = await this.http.postFormData(
1006
1102
  "/api/v2/plays/files/stage",
1007
- { files }
1103
+ formData
1008
1104
  );
1009
1105
  return response.files;
1010
1106
  }
@@ -1020,9 +1116,6 @@ var DeeplineClient = class {
1020
1116
  * Internal/advanced primitive. Public callers should usually prefer
1021
1117
  * {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
1022
1118
  *
1023
- * Poll this method until `status` reaches a terminal state:
1024
- * `'completed'`, `'failed'`, or `'cancelled'`.
1025
- *
1026
1119
  * @param workflowId - Play-run id from {@link startPlayRun}
1027
1120
  * @returns Current status with progress logs and partial results
1028
1121
  *
@@ -1033,41 +1126,22 @@ var DeeplineClient = class {
1033
1126
  * console.log(`Logs: ${status.progress?.logs.length ?? 0} lines`);
1034
1127
  * ```
1035
1128
  */
1036
- async getPlayStatus(workflowId) {
1037
- const response = await this.http.get(
1038
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}`
1039
- );
1040
- return normalizePlayStatus(response);
1041
- }
1042
- /**
1043
- * Get the lightweight tail-polling status for a play execution.
1044
- *
1045
- * This is intentionally smaller than {@link getPlayStatus}: it returns the
1046
- * fields needed for CLI log tailing while the run is in flight, without
1047
- * forcing the API to rebuild final result views on every poll. Call
1048
- * {@link getPlayStatus} once after a terminal state for the full result.
1049
- */
1050
- async getPlayTailStatus(workflowId, options) {
1051
- const params = new URLSearchParams({ mode: "tail" });
1052
- if (typeof options?.afterLogIndex === "number") {
1053
- params.set("afterLogIndex", String(options.afterLogIndex));
1054
- }
1055
- if (typeof options?.waitMs === "number") {
1056
- params.set("waitMs", String(options.waitMs));
1057
- }
1058
- if (options?.terminalOnly) {
1059
- params.set("terminalOnly", "true");
1129
+ async getPlayStatus(workflowId, options) {
1130
+ const params = new URLSearchParams();
1131
+ if (options?.billing === false) {
1132
+ params.set("billing", "false");
1060
1133
  }
1134
+ const query = params.size > 0 ? `?${params.toString()}` : "";
1061
1135
  const response = await this.http.get(
1062
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`
1136
+ `/api/v2/plays/run/${encodeURIComponent(workflowId)}${query}`
1063
1137
  );
1064
1138
  return normalizePlayStatus(response);
1065
1139
  }
1066
1140
  /**
1067
1141
  * Stream semantic play-run events using the same SSE feed as the dashboard.
1068
1142
  *
1069
- * Consumers should still keep a polling fallback: SSE is the fast live-update
1070
- * transport, while the status endpoints remain the authoritative recovery path.
1143
+ * The server emits a canonical `play.run.snapshot` event first for every
1144
+ * connection, then incremental live events until terminal state or reconnect.
1071
1145
  */
1072
1146
  async *streamPlayRunEvents(workflowId, options) {
1073
1147
  const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
@@ -1087,7 +1161,7 @@ var DeeplineClient = class {
1087
1161
  *
1088
1162
  * Sends a stop request for the run.
1089
1163
  *
1090
- * @param workflowId - Temporal workflow ID to cancel
1164
+ * @param workflowId - Public Deepline play-run id to cancel
1091
1165
  *
1092
1166
  * @example
1093
1167
  * ```typescript
@@ -1103,7 +1177,7 @@ var DeeplineClient = class {
1103
1177
  /**
1104
1178
  * Stop a running play execution, including open HITL waits.
1105
1179
  *
1106
- * @param workflowId - Temporal workflow ID to stop
1180
+ * @param workflowId - Public Deepline play-run id to stop
1107
1181
  * @param options.reason - Optional audit/debug reason
1108
1182
  */
1109
1183
  async stopPlay(workflowId, options) {
@@ -1175,32 +1249,42 @@ var DeeplineClient = class {
1175
1249
  );
1176
1250
  return response.runs ?? [];
1177
1251
  }
1178
- /**
1179
- * Fetch the lightweight tail status for a run using the public runs resource model.
1180
- *
1181
- * This is the SDK equivalent of:
1182
- *
1183
- * ```bash
1184
- * deepline runs tail <run-id> --json
1185
- * ```
1186
- */
1252
+ /** Read the canonical run stream and return the latest run snapshot. */
1187
1253
  async tailRun(runId, options) {
1188
- const afterLogIndex = typeof options?.afterLogIndex === "number" ? options.afterLogIndex : typeof options?.cursor === "number" ? options.cursor : typeof options?.cursor === "string" && options.cursor.trim() ? Number(options.cursor) : void 0;
1189
- const params = new URLSearchParams();
1190
- if (Number.isFinite(afterLogIndex)) {
1191
- params.set("afterLogIndex", String(Number(afterLogIndex)));
1254
+ const state = {
1255
+ runId,
1256
+ status: "running",
1257
+ logs: [],
1258
+ latest: null
1259
+ };
1260
+ let terminal = false;
1261
+ for await (const event of this.streamPlayRunEvents(runId, {
1262
+ mode: "cli",
1263
+ signal: options?.signal
1264
+ })) {
1265
+ const status = updatePlayLiveStatusState(state, event);
1266
+ if (!status) {
1267
+ continue;
1268
+ }
1269
+ terminal = TERMINAL_PLAY_STATUSES.has(status.status);
1270
+ if (terminal) {
1271
+ break;
1272
+ }
1192
1273
  }
1193
- if (typeof options?.waitMs === "number") {
1194
- params.set("waitMs", String(options.waitMs));
1274
+ if (terminal && state.latest) {
1275
+ return await this.getRunStatus(state.latest.runId || runId).catch(
1276
+ () => state.latest ?? playRunStatusFromState(state)
1277
+ );
1195
1278
  }
1196
- if (options?.terminalOnly) {
1197
- params.set("terminalOnly", "true");
1279
+ if (state.latest) {
1280
+ return state.latest;
1198
1281
  }
1199
- const suffix = params.toString() ? `?${params.toString()}` : "";
1200
- const response = await this.http.get(
1201
- `/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
1282
+ throw new DeeplineError(
1283
+ `Run stream for ${runId} ended before the initial snapshot.`,
1284
+ void 0,
1285
+ "PLAY_RUN_STREAM_EMPTY",
1286
+ { runId }
1202
1287
  );
1203
- return normalizePlayStatus(response);
1204
1288
  }
1205
1289
  /**
1206
1290
  * Fetch persisted logs for a run using the public runs resource model.
@@ -1357,11 +1441,11 @@ var DeeplineClient = class {
1357
1441
  // Plays — high-level orchestration
1358
1442
  // ——————————————————————————————————————————————————————————
1359
1443
  /**
1360
- * Run a play end-to-end: submit, poll until terminal, return result.
1444
+ * Run a play end-to-end: submit, stream until terminal, return result.
1361
1445
  *
1362
1446
  * This is the highest-level play execution method. It submits the play,
1363
- * polls for status updates, and returns a structured result with logs
1364
- * and timing. Supports cancellation via `AbortSignal`.
1447
+ * reads the canonical run stream for status updates, and returns a structured
1448
+ * result with logs and timing. Supports cancellation via `AbortSignal`.
1365
1449
  *
1366
1450
  * @param code - Source string fallback; pass the bundled artifact in `options.artifact`
1367
1451
  * @param csvPath - Input CSV path, or `null`
@@ -1377,7 +1461,6 @@ var DeeplineClient = class {
1377
1461
  * const logs = status.progress?.logs ?? [];
1378
1462
  * console.log(`[${status.status}] ${logs.length} log lines`);
1379
1463
  * },
1380
- * pollIntervalMs: 1000,
1381
1464
  * });
1382
1465
  *
1383
1466
  * if (result.success) {
@@ -1408,33 +1491,53 @@ var DeeplineClient = class {
1408
1491
  packagedFiles: options?.packagedFiles,
1409
1492
  force: options?.force
1410
1493
  });
1411
- const pollInterval = options?.pollIntervalMs ?? 500;
1412
1494
  const start = Date.now();
1413
- while (true) {
1495
+ const state = {
1496
+ runId: workflowId,
1497
+ status: "running",
1498
+ logs: [],
1499
+ latest: null
1500
+ };
1501
+ if (options?.signal?.aborted) {
1502
+ await this.cancelPlay(workflowId);
1503
+ return {
1504
+ success: false,
1505
+ runId: workflowId,
1506
+ logs: [],
1507
+ durationMs: Date.now() - start,
1508
+ error: "Cancelled by user"
1509
+ };
1510
+ }
1511
+ for await (const event of this.streamPlayRunEvents(workflowId, {
1512
+ mode: "cli",
1513
+ signal: options?.signal
1514
+ })) {
1414
1515
  if (options?.signal?.aborted) {
1415
1516
  await this.cancelPlay(workflowId);
1416
1517
  return {
1417
1518
  success: false,
1418
1519
  runId: workflowId,
1419
- logs: [],
1520
+ logs: state.logs,
1420
1521
  durationMs: Date.now() - start,
1421
1522
  error: "Cancelled by user"
1422
1523
  };
1423
1524
  }
1424
- const status = await this.getPlayStatus(workflowId);
1525
+ const status = updatePlayLiveStatusState(state, event);
1526
+ if (!status) {
1527
+ continue;
1528
+ }
1425
1529
  options?.onProgress?.(status);
1426
1530
  if (TERMINAL_PLAY_STATUSES.has(status.status)) {
1427
- return {
1428
- success: status.status === "completed",
1429
- runId: status.runId || workflowId,
1430
- result: status.result,
1431
- logs: status.progress?.logs ?? [],
1432
- durationMs: Date.now() - start,
1433
- error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
1434
- };
1531
+ const finalStatus = await this.getPlayStatus(status.runId || workflowId).catch(() => status);
1532
+ return playRunResultFromStatus(finalStatus, start, workflowId);
1435
1533
  }
1436
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
1437
1534
  }
1535
+ throw new DeeplineError(
1536
+ `Run stream for ${workflowId} ended before the run reached a terminal state.`,
1537
+ void 0,
1538
+ "PLAY_RUN_STREAM_ENDED",
1539
+ { runId: workflowId, workflowId }
1540
+ );
1438
1541
  }
1439
1542
  // ——————————————————————————————————————————————————————————
1440
1543
  // Health