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.mjs CHANGED
@@ -195,7 +195,7 @@ function resolveConfig(options) {
195
195
  }
196
196
 
197
197
  // src/version.ts
198
- var SDK_VERSION = "0.1.20";
198
+ var SDK_VERSION = "0.1.22";
199
199
  var SDK_API_CONTRACT = "2026-05-runs-v2";
200
200
 
201
201
  // ../shared_libs/play-runtime/coordinator-headers.ts
@@ -278,7 +278,7 @@ var HttpClient = class {
278
278
  const response = await fetch(candidateUrl, {
279
279
  method,
280
280
  headers,
281
- body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
281
+ body: options?.formData !== void 0 ? options.formData : options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
282
282
  signal: controller.signal
283
283
  });
284
284
  clearTimeout(timeoutId);
@@ -397,6 +397,13 @@ var HttpClient = class {
397
397
  headers
398
398
  });
399
399
  }
400
+ async postFormData(path, formData, headers) {
401
+ return this.request(path, {
402
+ method: "POST",
403
+ formData,
404
+ headers
405
+ });
406
+ }
400
407
  /**
401
408
  * Send a DELETE request.
402
409
  *
@@ -480,7 +487,7 @@ function isRecord(value) {
480
487
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
481
488
  }
482
489
  function normalizePlayStatus(raw) {
483
- const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
490
+ const status = typeof raw.status === "string" ? raw.status : "running";
484
491
  const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
485
492
  return {
486
493
  ...raw,
@@ -488,22 +495,86 @@ function normalizePlayStatus(raw) {
488
495
  status
489
496
  };
490
497
  }
491
- function mapLegacyTemporalStatus(status) {
492
- switch (status.trim().toUpperCase()) {
493
- case "PENDING":
494
- return "queued";
495
- case "COMPLETED":
496
- return "completed";
497
- case "FAILED":
498
- return "failed";
499
- case "CANCELLED":
500
- case "TERMINATED":
501
- case "TIMED_OUT":
502
- return "cancelled";
503
- case "RUNNING":
504
- default:
505
- return "running";
498
+ function decodeBase64Bytes(value) {
499
+ const binary = atob(value);
500
+ const bytes = new Uint8Array(binary.length);
501
+ for (let index = 0; index < binary.length; index += 1) {
502
+ bytes[index] = binary.charCodeAt(index);
503
+ }
504
+ return bytes;
505
+ }
506
+ function readStringArray(value) {
507
+ return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
508
+ }
509
+ function getPlayLiveEventPayload(event) {
510
+ return event.payload && typeof event.payload === "object" ? event.payload : {};
511
+ }
512
+ function normalizeLiveStatus(value) {
513
+ if (value === "queued" || value === "running" || value === "waiting" || value === "completed" || value === "failed" || value === "cancelled") {
514
+ return value;
515
+ }
516
+ return null;
517
+ }
518
+ function updatePlayLiveStatusState(state, event) {
519
+ const payload = getPlayLiveEventPayload(event);
520
+ if (event.type === "play.run.log") {
521
+ state.logs.push(...readStringArray(payload.lines));
522
+ return null;
523
+ }
524
+ if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
525
+ return null;
526
+ }
527
+ const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : state.runId;
528
+ const status = normalizeLiveStatus(payload.status) ?? state.status;
529
+ const logs = readStringArray(payload.logs);
530
+ if (logs.length > 0 || event.type === "play.run.snapshot") {
531
+ state.logs = logs;
532
+ }
533
+ if ("result" in payload) {
534
+ state.result = payload.result;
506
535
  }
536
+ if (typeof payload.error === "string" && payload.error.trim()) {
537
+ state.error = payload.error;
538
+ }
539
+ state.runId = runId;
540
+ state.status = status;
541
+ const progressRecord = payload.progress && typeof payload.progress === "object" && !Array.isArray(payload.progress) ? payload.progress : {};
542
+ const next = {
543
+ ...payload,
544
+ runId,
545
+ status,
546
+ progress: {
547
+ ...progressRecord,
548
+ status: typeof progressRecord.status === "string" ? progressRecord.status : status,
549
+ logs: state.logs,
550
+ ...state.error ? { error: state.error } : {}
551
+ },
552
+ ..."result" in state ? { result: state.result } : {}
553
+ };
554
+ state.latest = next;
555
+ return next;
556
+ }
557
+ function playRunResultFromStatus(status, startedAt, fallbackRunId) {
558
+ return {
559
+ success: status.status === "completed",
560
+ runId: status.runId || fallbackRunId,
561
+ result: status.result,
562
+ logs: status.progress?.logs ?? [],
563
+ durationMs: Date.now() - startedAt,
564
+ error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
565
+ };
566
+ }
567
+ function playRunStatusFromState(state) {
568
+ return {
569
+ runId: state.runId,
570
+ status: state.status,
571
+ progress: {
572
+ status: state.status,
573
+ logs: state.logs,
574
+ ...state.error ? { error: state.error } : {}
575
+ },
576
+ ..."result" in state ? { result: state.result } : {}
577
+ };
507
578
  }
508
579
  var DeeplineClient = class {
509
580
  http;
@@ -614,7 +685,7 @@ var DeeplineClient = class {
614
685
  /**
615
686
  * Search available tools using Deepline's ranked backend search.
616
687
  *
617
- * This is the same discovery surface used by the legacy CLI: it ranks across
688
+ * This is the same discovery surface used by the CLI: it ranks across
618
689
  * tool metadata, categories, agent guidance, and input schema fields.
619
690
  */
620
691
  async searchTools(options = {}) {
@@ -707,7 +778,7 @@ var DeeplineClient = class {
707
778
  * `progress.logs`; they are not part of the user output object.
708
779
  *
709
780
  * @param request - Play run configuration (name, code, input, etc.)
710
- * @returns Workflow metadata including the `workflowId` for status polling
781
+ * @returns Run metadata including the public `workflowId`
711
782
  *
712
783
  * @example
713
784
  * ```typescript
@@ -956,9 +1027,34 @@ var DeeplineClient = class {
956
1027
  * ```
957
1028
  */
958
1029
  async stagePlayFiles(files) {
959
- const response = await this.http.post(
1030
+ const formData = new FormData();
1031
+ formData.set(
1032
+ "metadata",
1033
+ JSON.stringify({
1034
+ files: files.map((file, index) => ({
1035
+ index,
1036
+ logicalPath: file.logicalPath,
1037
+ contentHash: file.contentHash,
1038
+ contentType: file.contentType,
1039
+ bytes: file.bytes
1040
+ }))
1041
+ })
1042
+ );
1043
+ for (const [index, file] of files.entries()) {
1044
+ const bytes = decodeBase64Bytes(file.contentBase64);
1045
+ const body = bytes.buffer.slice(
1046
+ bytes.byteOffset,
1047
+ bytes.byteOffset + bytes.byteLength
1048
+ );
1049
+ formData.set(
1050
+ `file:${index}`,
1051
+ new Blob([body], { type: file.contentType }),
1052
+ file.logicalPath
1053
+ );
1054
+ }
1055
+ const response = await this.http.postFormData(
960
1056
  "/api/v2/plays/files/stage",
961
- { files }
1057
+ formData
962
1058
  );
963
1059
  return response.files;
964
1060
  }
@@ -974,9 +1070,6 @@ var DeeplineClient = class {
974
1070
  * Internal/advanced primitive. Public callers should usually prefer
975
1071
  * {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
976
1072
  *
977
- * Poll this method until `status` reaches a terminal state:
978
- * `'completed'`, `'failed'`, or `'cancelled'`.
979
- *
980
1073
  * @param workflowId - Play-run id from {@link startPlayRun}
981
1074
  * @returns Current status with progress logs and partial results
982
1075
  *
@@ -987,41 +1080,22 @@ var DeeplineClient = class {
987
1080
  * console.log(`Logs: ${status.progress?.logs.length ?? 0} lines`);
988
1081
  * ```
989
1082
  */
990
- async getPlayStatus(workflowId) {
991
- const response = await this.http.get(
992
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}`
993
- );
994
- return normalizePlayStatus(response);
995
- }
996
- /**
997
- * Get the lightweight tail-polling status for a play execution.
998
- *
999
- * This is intentionally smaller than {@link getPlayStatus}: it returns the
1000
- * fields needed for CLI log tailing while the run is in flight, without
1001
- * forcing the API to rebuild final result views on every poll. Call
1002
- * {@link getPlayStatus} once after a terminal state for the full result.
1003
- */
1004
- async getPlayTailStatus(workflowId, options) {
1005
- const params = new URLSearchParams({ mode: "tail" });
1006
- if (typeof options?.afterLogIndex === "number") {
1007
- params.set("afterLogIndex", String(options.afterLogIndex));
1008
- }
1009
- if (typeof options?.waitMs === "number") {
1010
- params.set("waitMs", String(options.waitMs));
1011
- }
1012
- if (options?.terminalOnly) {
1013
- params.set("terminalOnly", "true");
1083
+ async getPlayStatus(workflowId, options) {
1084
+ const params = new URLSearchParams();
1085
+ if (options?.billing === false) {
1086
+ params.set("billing", "false");
1014
1087
  }
1088
+ const query = params.size > 0 ? `?${params.toString()}` : "";
1015
1089
  const response = await this.http.get(
1016
- `/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`
1090
+ `/api/v2/plays/run/${encodeURIComponent(workflowId)}${query}`
1017
1091
  );
1018
1092
  return normalizePlayStatus(response);
1019
1093
  }
1020
1094
  /**
1021
1095
  * Stream semantic play-run events using the same SSE feed as the dashboard.
1022
1096
  *
1023
- * Consumers should still keep a polling fallback: SSE is the fast live-update
1024
- * transport, while the status endpoints remain the authoritative recovery path.
1097
+ * The server emits a canonical `play.run.snapshot` event first for every
1098
+ * connection, then incremental live events until terminal state or reconnect.
1025
1099
  */
1026
1100
  async *streamPlayRunEvents(workflowId, options) {
1027
1101
  const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
@@ -1041,7 +1115,7 @@ var DeeplineClient = class {
1041
1115
  *
1042
1116
  * Sends a stop request for the run.
1043
1117
  *
1044
- * @param workflowId - Temporal workflow ID to cancel
1118
+ * @param workflowId - Public Deepline play-run id to cancel
1045
1119
  *
1046
1120
  * @example
1047
1121
  * ```typescript
@@ -1057,7 +1131,7 @@ var DeeplineClient = class {
1057
1131
  /**
1058
1132
  * Stop a running play execution, including open HITL waits.
1059
1133
  *
1060
- * @param workflowId - Temporal workflow ID to stop
1134
+ * @param workflowId - Public Deepline play-run id to stop
1061
1135
  * @param options.reason - Optional audit/debug reason
1062
1136
  */
1063
1137
  async stopPlay(workflowId, options) {
@@ -1129,32 +1203,42 @@ var DeeplineClient = class {
1129
1203
  );
1130
1204
  return response.runs ?? [];
1131
1205
  }
1132
- /**
1133
- * Fetch the lightweight tail status for a run using the public runs resource model.
1134
- *
1135
- * This is the SDK equivalent of:
1136
- *
1137
- * ```bash
1138
- * deepline runs tail <run-id> --json
1139
- * ```
1140
- */
1206
+ /** Read the canonical run stream and return the latest run snapshot. */
1141
1207
  async tailRun(runId, options) {
1142
- 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;
1143
- const params = new URLSearchParams();
1144
- if (Number.isFinite(afterLogIndex)) {
1145
- params.set("afterLogIndex", String(Number(afterLogIndex)));
1208
+ const state = {
1209
+ runId,
1210
+ status: "running",
1211
+ logs: [],
1212
+ latest: null
1213
+ };
1214
+ let terminal = false;
1215
+ for await (const event of this.streamPlayRunEvents(runId, {
1216
+ mode: "cli",
1217
+ signal: options?.signal
1218
+ })) {
1219
+ const status = updatePlayLiveStatusState(state, event);
1220
+ if (!status) {
1221
+ continue;
1222
+ }
1223
+ terminal = TERMINAL_PLAY_STATUSES.has(status.status);
1224
+ if (terminal) {
1225
+ break;
1226
+ }
1146
1227
  }
1147
- if (typeof options?.waitMs === "number") {
1148
- params.set("waitMs", String(options.waitMs));
1228
+ if (terminal && state.latest) {
1229
+ return await this.getRunStatus(state.latest.runId || runId).catch(
1230
+ () => state.latest ?? playRunStatusFromState(state)
1231
+ );
1149
1232
  }
1150
- if (options?.terminalOnly) {
1151
- params.set("terminalOnly", "true");
1233
+ if (state.latest) {
1234
+ return state.latest;
1152
1235
  }
1153
- const suffix = params.toString() ? `?${params.toString()}` : "";
1154
- const response = await this.http.get(
1155
- `/api/v2/runs/${encodeURIComponent(runId)}/tail${suffix}`
1236
+ throw new DeeplineError(
1237
+ `Run stream for ${runId} ended before the initial snapshot.`,
1238
+ void 0,
1239
+ "PLAY_RUN_STREAM_EMPTY",
1240
+ { runId }
1156
1241
  );
1157
- return normalizePlayStatus(response);
1158
1242
  }
1159
1243
  /**
1160
1244
  * Fetch persisted logs for a run using the public runs resource model.
@@ -1311,11 +1395,11 @@ var DeeplineClient = class {
1311
1395
  // Plays — high-level orchestration
1312
1396
  // ——————————————————————————————————————————————————————————
1313
1397
  /**
1314
- * Run a play end-to-end: submit, poll until terminal, return result.
1398
+ * Run a play end-to-end: submit, stream until terminal, return result.
1315
1399
  *
1316
1400
  * This is the highest-level play execution method. It submits the play,
1317
- * polls for status updates, and returns a structured result with logs
1318
- * and timing. Supports cancellation via `AbortSignal`.
1401
+ * reads the canonical run stream for status updates, and returns a structured
1402
+ * result with logs and timing. Supports cancellation via `AbortSignal`.
1319
1403
  *
1320
1404
  * @param code - Source string fallback; pass the bundled artifact in `options.artifact`
1321
1405
  * @param csvPath - Input CSV path, or `null`
@@ -1331,7 +1415,6 @@ var DeeplineClient = class {
1331
1415
  * const logs = status.progress?.logs ?? [];
1332
1416
  * console.log(`[${status.status}] ${logs.length} log lines`);
1333
1417
  * },
1334
- * pollIntervalMs: 1000,
1335
1418
  * });
1336
1419
  *
1337
1420
  * if (result.success) {
@@ -1362,33 +1445,53 @@ var DeeplineClient = class {
1362
1445
  packagedFiles: options?.packagedFiles,
1363
1446
  force: options?.force
1364
1447
  });
1365
- const pollInterval = options?.pollIntervalMs ?? 500;
1366
1448
  const start = Date.now();
1367
- while (true) {
1449
+ const state = {
1450
+ runId: workflowId,
1451
+ status: "running",
1452
+ logs: [],
1453
+ latest: null
1454
+ };
1455
+ if (options?.signal?.aborted) {
1456
+ await this.cancelPlay(workflowId);
1457
+ return {
1458
+ success: false,
1459
+ runId: workflowId,
1460
+ logs: [],
1461
+ durationMs: Date.now() - start,
1462
+ error: "Cancelled by user"
1463
+ };
1464
+ }
1465
+ for await (const event of this.streamPlayRunEvents(workflowId, {
1466
+ mode: "cli",
1467
+ signal: options?.signal
1468
+ })) {
1368
1469
  if (options?.signal?.aborted) {
1369
1470
  await this.cancelPlay(workflowId);
1370
1471
  return {
1371
1472
  success: false,
1372
1473
  runId: workflowId,
1373
- logs: [],
1474
+ logs: state.logs,
1374
1475
  durationMs: Date.now() - start,
1375
1476
  error: "Cancelled by user"
1376
1477
  };
1377
1478
  }
1378
- const status = await this.getPlayStatus(workflowId);
1479
+ const status = updatePlayLiveStatusState(state, event);
1480
+ if (!status) {
1481
+ continue;
1482
+ }
1379
1483
  options?.onProgress?.(status);
1380
1484
  if (TERMINAL_PLAY_STATUSES.has(status.status)) {
1381
- return {
1382
- success: status.status === "completed",
1383
- runId: status.runId || workflowId,
1384
- result: status.result,
1385
- logs: status.progress?.logs ?? [],
1386
- durationMs: Date.now() - start,
1387
- error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
1388
- };
1485
+ const finalStatus = await this.getPlayStatus(status.runId || workflowId).catch(() => status);
1486
+ return playRunResultFromStatus(finalStatus, start, workflowId);
1389
1487
  }
1390
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
1391
1488
  }
1489
+ throw new DeeplineError(
1490
+ `Run stream for ${workflowId} ended before the run reached a terminal state.`,
1491
+ void 0,
1492
+ "PLAY_RUN_STREAM_ENDED",
1493
+ { runId: workflowId, workflowId }
1494
+ );
1392
1495
  }
1393
1496
  // ——————————————————————————————————————————————————————————
1394
1497
  // Health