deepline 0.1.91 → 0.1.93

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.
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
206
206
 
207
207
  // src/release.ts
208
208
  var SDK_RELEASE = {
209
- version: "0.1.91",
209
+ version: "0.1.93",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.91",
212
+ latest: "0.1.93",
213
213
  minimumSupported: "0.1.53",
214
214
  deprecatedBelow: "0.1.53"
215
215
  }
@@ -564,6 +564,807 @@ function sleep(ms) {
564
564
  return new Promise((resolve14) => setTimeout(resolve14, ms));
565
565
  }
566
566
 
567
+ // src/stream-reconnect.ts
568
+ var STREAM_RECONNECT_BASE_DELAY_MS = 500;
569
+ var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
570
+ var STREAM_HEALTHY_CONNECTION_MS = 3e4;
571
+ function streamReconnectDelayMs(attempt) {
572
+ const cappedExponentialMs = Math.min(
573
+ STREAM_RECONNECT_MAX_DELAY_MS,
574
+ STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt)
575
+ );
576
+ return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
577
+ }
578
+ function isTransientPlayStreamError(error) {
579
+ if (error instanceof DeeplineError && typeof error.statusCode === "number") {
580
+ return error.statusCode >= 500 && error.statusCode < 600;
581
+ }
582
+ const text = error instanceof Error ? error.message : String(error);
583
+ return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
584
+ text
585
+ );
586
+ }
587
+
588
+ // ../shared_libs/play-runtime/live-events.ts
589
+ function resolveTimingWindow(input2) {
590
+ const startedAt = typeof input2.startedAt === "number" && Number.isFinite(input2.startedAt) ? input2.startedAt : null;
591
+ const completedAt = typeof input2.completedAt === "number" && Number.isFinite(input2.completedAt) ? input2.completedAt : null;
592
+ const updatedAt = typeof input2.updatedAt === "number" && Number.isFinite(input2.updatedAt) ? input2.updatedAt : null;
593
+ return {
594
+ startedAt,
595
+ completedAt,
596
+ updatedAt
597
+ };
598
+ }
599
+
600
+ // ../shared_libs/play-runtime/run-ledger.ts
601
+ var LOG_TAIL_LIMIT = 100;
602
+ function isRecord(value) {
603
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
604
+ }
605
+ function finiteNumber(value) {
606
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
607
+ }
608
+ function optionalFiniteNumber(value) {
609
+ const normalized = finiteNumber(value);
610
+ return normalized === null ? void 0 : normalized;
611
+ }
612
+ function optionalString(value) {
613
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
614
+ }
615
+ function optionalNullableString(value) {
616
+ if (value === null) return null;
617
+ return optionalString(value);
618
+ }
619
+ function normalizePlayRunLedgerStatus(value) {
620
+ const normalized = String(value ?? "").trim().toLowerCase();
621
+ switch (normalized) {
622
+ case "queued":
623
+ case "pending":
624
+ return "queued";
625
+ case "running":
626
+ case "started":
627
+ return "running";
628
+ case "waiting":
629
+ return "waiting";
630
+ case "completed":
631
+ case "complete":
632
+ case "succeeded":
633
+ return "completed";
634
+ case "failed":
635
+ case "error":
636
+ return "failed";
637
+ case "cancelled":
638
+ case "canceled":
639
+ return "cancelled";
640
+ case "terminated":
641
+ return "terminated";
642
+ case "timed_out":
643
+ case "timeout":
644
+ return "timed_out";
645
+ default:
646
+ return "unknown";
647
+ }
648
+ }
649
+ function createEmptyPlayRunLedgerSnapshot(input2) {
650
+ const status = normalizePlayRunLedgerStatus(input2.status ?? "unknown");
651
+ const startedAt = finiteNumber(input2.startedAt) ?? null;
652
+ const finishedAt = finiteNumber(input2.finishedAt) ?? null;
653
+ return {
654
+ runId: input2.runId,
655
+ playName: input2.playName ?? null,
656
+ status,
657
+ error: optionalNullableString(input2.error) ?? null,
658
+ createdAt: finiteNumber(input2.createdAt) ?? null,
659
+ startedAt,
660
+ updatedAt: finiteNumber(input2.updatedAt) ?? finishedAt ?? startedAt ?? finiteNumber(input2.createdAt) ?? null,
661
+ finishedAt,
662
+ durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : null,
663
+ orderedStepIds: [],
664
+ stepsById: {},
665
+ logTail: [],
666
+ totalLogCount: 0,
667
+ logsTruncated: false,
668
+ activeStepId: null,
669
+ activeArtifactTableNamespace: null,
670
+ resultTableNamespace: null
671
+ };
672
+ }
673
+ function normalizePlayRunLedgerSnapshot(value, fallback) {
674
+ if (!isRecord(value)) {
675
+ return createEmptyPlayRunLedgerSnapshot(fallback);
676
+ }
677
+ const orderedStepIds = Array.isArray(value.orderedStepIds) ? value.orderedStepIds.filter(
678
+ (entry) => typeof entry === "string" && Boolean(entry.trim())
679
+ ) : [];
680
+ const rawSteps = isRecord(value.stepsById) ? value.stepsById : {};
681
+ const stepsById = {};
682
+ for (const [stepId, rawStep] of Object.entries(rawSteps)) {
683
+ if (!stepId.trim() || !isRecord(rawStep)) continue;
684
+ const rawStatus = normalizeStepStatus(rawStep.status);
685
+ if (!rawStatus) continue;
686
+ const completedAt = finiteNumber(rawStep.completedAt);
687
+ const status = rawStatus === "running" && completedAt !== null ? "completed" : rawStatus;
688
+ const rawProgress = isRecord(rawStep.progress) ? rawStep.progress : null;
689
+ stepsById[stepId] = {
690
+ stepId,
691
+ label: optionalString(rawStep.label),
692
+ kind: optionalString(rawStep.kind),
693
+ status,
694
+ artifactTableNamespace: optionalNullableString(
695
+ rawStep.artifactTableNamespace
696
+ ),
697
+ startedAt: finiteNumber(rawStep.startedAt),
698
+ completedAt,
699
+ updatedAt: finiteNumber(rawStep.updatedAt),
700
+ progress: rawProgress ? normalizeStepProgress(rawProgress) : null
701
+ };
702
+ }
703
+ const createdAt = finiteNumber(value.createdAt) ?? fallback.createdAt ?? null;
704
+ const startedAt = finiteNumber(value.startedAt) ?? fallback.startedAt ?? null;
705
+ const finishedAt = finiteNumber(value.finishedAt) ?? fallback.finishedAt ?? null;
706
+ const updatedAt = finiteNumber(value.updatedAt) ?? fallback.updatedAt ?? finishedAt ?? startedAt ?? createdAt ?? null;
707
+ const error = Object.prototype.hasOwnProperty.call(value, "error") ? optionalNullableString(value.error) ?? null : fallback.error ?? null;
708
+ const rawTail = Array.isArray(value.logTail) ? value.logTail : value.logs;
709
+ const logTail = Array.isArray(rawTail) ? rawTail.filter((line) => typeof line === "string") : [];
710
+ return {
711
+ runId: optionalString(value.runId) ?? fallback.runId,
712
+ playName: optionalNullableString(value.playName) ?? fallback.playName ?? null,
713
+ status: normalizePlayRunLedgerStatus(value.status ?? fallback.status),
714
+ error,
715
+ createdAt,
716
+ startedAt,
717
+ updatedAt,
718
+ finishedAt,
719
+ durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : finiteNumber(value.durationMs),
720
+ orderedStepIds: orderedStepIds.filter((stepId) => stepsById[stepId]),
721
+ stepsById,
722
+ logTail: logTail.slice(-LOG_TAIL_LIMIT),
723
+ // Snapshots persisted before totalLogCount existed only know the retained
724
+ // tail, so the best lower bound for the cumulative count is the tail size.
725
+ totalLogCount: Math.max(
726
+ finiteNumber(value.totalLogCount) ?? logTail.length,
727
+ logTail.length
728
+ ),
729
+ logsTruncated: value.logsTruncated === true,
730
+ activeStepId: optionalNullableString(value.activeStepId),
731
+ activeArtifactTableNamespace: optionalNullableString(
732
+ value.activeArtifactTableNamespace
733
+ ),
734
+ resultTableNamespace: optionalNullableString(value.resultTableNamespace),
735
+ resultSummary: value.resultSummary,
736
+ result: value.result
737
+ };
738
+ }
739
+ function normalizeStepStatus(value) {
740
+ const normalized = String(value ?? "").trim().toLowerCase();
741
+ if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "skipped") {
742
+ return normalized;
743
+ }
744
+ return null;
745
+ }
746
+ function normalizeStepProgress(value) {
747
+ return {
748
+ ...optionalFiniteNumber(value.completed) !== void 0 ? { completed: optionalFiniteNumber(value.completed) } : {},
749
+ ...optionalFiniteNumber(value.total) !== void 0 ? { total: optionalFiniteNumber(value.total) } : {},
750
+ ...optionalFiniteNumber(value.failed) !== void 0 ? { failed: optionalFiniteNumber(value.failed) } : {},
751
+ ...optionalString(value.message) ? { message: optionalString(value.message) } : {},
752
+ ...optionalNullableString(value.artifactTableNamespace) !== void 0 ? {
753
+ artifactTableNamespace: optionalNullableString(
754
+ value.artifactTableNamespace
755
+ )
756
+ } : {},
757
+ ...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
758
+ };
759
+ }
760
+
761
+ // ../shared_libs/play-runtime/run-snapshot-stream.ts
762
+ function normalizePlayRunLiveStatus(value) {
763
+ const normalized = String(value ?? "").trim().toLowerCase();
764
+ switch (normalized) {
765
+ case "queued":
766
+ case "pending":
767
+ return "running";
768
+ case "running":
769
+ case "started":
770
+ return "running";
771
+ case "completed":
772
+ case "complete":
773
+ case "succeeded":
774
+ return "completed";
775
+ case "failed":
776
+ case "error":
777
+ return "failed";
778
+ case "cancelled":
779
+ case "canceled":
780
+ return "cancelled";
781
+ case "terminated":
782
+ return "terminated";
783
+ case "timed_out":
784
+ case "timeout":
785
+ return "timed_out";
786
+ default:
787
+ return "unknown";
788
+ }
789
+ }
790
+ function isTerminalPlayRunLiveStatus(status) {
791
+ return status === "completed" || status === "failed" || status === "cancelled" || status === "terminated" || status === "timed_out";
792
+ }
793
+ function buildSnapshotFromLedger(snapshot) {
794
+ const nodeStates = snapshot.orderedStepIds.map((stepId) => snapshot.stepsById[stepId]).filter((step) => Boolean(step)).map((step) => ({
795
+ nodeId: step.stepId,
796
+ status: step.status,
797
+ artifactTableNamespace: step.artifactTableNamespace ?? null,
798
+ progress: step.progress ? {
799
+ completed: step.progress.completed,
800
+ total: step.progress.total,
801
+ failed: step.progress.failed,
802
+ message: step.progress.message,
803
+ artifactTableNamespace: step.progress.artifactTableNamespace ?? step.artifactTableNamespace ?? null,
804
+ startedAt: step.startedAt ?? null,
805
+ completedAt: step.completedAt ?? null,
806
+ updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null
807
+ } : null,
808
+ startedAt: step.startedAt ?? null,
809
+ completedAt: step.completedAt ?? null,
810
+ updatedAt: step.updatedAt ?? null
811
+ }));
812
+ return {
813
+ runId: snapshot.runId,
814
+ status: normalizePlayRunLiveStatus(snapshot.status),
815
+ updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
816
+ logs: snapshot.logTail,
817
+ totalLogCount: snapshot.totalLogCount,
818
+ ...snapshot.logsTruncated ? { logsTruncated: true } : {},
819
+ activeArtifactTableNamespace: snapshot.activeArtifactTableNamespace ?? null,
820
+ resultTableNamespace: snapshot.resultTableNamespace ?? null,
821
+ nodeStates,
822
+ activeNodeId: snapshot.activeStepId ?? null
823
+ };
824
+ }
825
+ function buildPlayRunStatusSnapshot(input2) {
826
+ const ledgerSnapshot = normalizePlayRunLedgerSnapshot(input2.run.runSnapshot, {
827
+ runId: input2.run.workflowId,
828
+ playName: input2.run.name ?? null,
829
+ status: input2.run.status,
830
+ createdAt: input2.run.createdAt ?? null,
831
+ startedAt: input2.run.startedAt ?? null,
832
+ updatedAt: input2.run.updatedAt ?? null,
833
+ finishedAt: input2.run.finishedAt ?? null
834
+ });
835
+ return buildSnapshotFromLedger(ledgerSnapshot);
836
+ }
837
+ function makeRunStreamEvent(input2) {
838
+ return {
839
+ ...input2,
840
+ scope: "play",
841
+ at: input2.at ?? (/* @__PURE__ */ new Date()).toISOString()
842
+ };
843
+ }
844
+ var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
845
+ runSignature: "",
846
+ snapshotSignature: "",
847
+ stepStatusSignature: "",
848
+ stepProgressSignature: "",
849
+ lastLogSeq: 0
850
+ };
851
+ function getSnapshotCursor(snapshot) {
852
+ return String(snapshot.updatedAt ?? Date.now());
853
+ }
854
+ function getRunSignature(snapshot) {
855
+ return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
856
+ }
857
+ function getStepStatusSignature(snapshot) {
858
+ return snapshot.nodeStates.map(
859
+ (state) => [
860
+ state.nodeId,
861
+ state.status,
862
+ state.artifactTableNamespace ?? "",
863
+ state.startedAt ?? "",
864
+ state.completedAt ?? "",
865
+ state.progress?.startedAt ?? "",
866
+ state.progress?.completedAt ?? "",
867
+ state.updatedAt ?? "",
868
+ state.progress?.updatedAt ?? ""
869
+ ].join(":")
870
+ ).join("|");
871
+ }
872
+ function getStepProgressSignature(snapshot) {
873
+ return snapshot.nodeStates.map(
874
+ (state) => [
875
+ state.nodeId,
876
+ state.progress?.completed ?? "",
877
+ state.progress?.total ?? "",
878
+ state.progress?.failed ?? "",
879
+ state.progress?.artifactTableNamespace ?? "",
880
+ state.progress?.startedAt ?? "",
881
+ state.progress?.completedAt ?? "",
882
+ state.progress?.updatedAt ?? "",
883
+ state.progress?.message ?? ""
884
+ ].join(":")
885
+ ).join("|");
886
+ }
887
+ function getSnapshotSignature(snapshot) {
888
+ return JSON.stringify(snapshot);
889
+ }
890
+ function resolvePlayRunLogGap(snapshot, lastLogSeq) {
891
+ if (snapshot.totalLogCount <= lastLogSeq) {
892
+ return null;
893
+ }
894
+ const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
895
+ if (lastLogSeq + 1 >= tailFirstSeq) {
896
+ return null;
897
+ }
898
+ return {
899
+ missingCount: tailFirstSeq - 1 - lastLogSeq,
900
+ tailFirstSeq
901
+ };
902
+ }
903
+ function diffLogLines(input2) {
904
+ const { logs, totalLogCount } = input2.snapshot;
905
+ if (totalLogCount <= input2.lastLogSeq) {
906
+ return { lines: [], lastLogSeq: input2.lastLogSeq, firstSeq: null };
907
+ }
908
+ const tailFirstSeq = totalLogCount - logs.length + 1;
909
+ if (input2.lastLogSeq + 1 >= tailFirstSeq) {
910
+ return {
911
+ lines: logs.slice(input2.lastLogSeq + 1 - tailFirstSeq),
912
+ lastLogSeq: totalLogCount,
913
+ firstSeq: input2.lastLogSeq + 1
914
+ };
915
+ }
916
+ const missingCount = tailFirstSeq - 1 - input2.lastLogSeq;
917
+ return {
918
+ lines: [
919
+ `[stream] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
920
+ ...logs
921
+ ],
922
+ lastLogSeq: totalLogCount,
923
+ firstSeq: null
924
+ };
925
+ }
926
+ function diffPlayRunStreamEvents(input2) {
927
+ const { snapshot, streamId, previous } = input2;
928
+ const cursor = getSnapshotCursor(snapshot);
929
+ const logDiff = diffLogLines({
930
+ snapshot,
931
+ lastLogSeq: previous.lastLogSeq
932
+ });
933
+ const next = {
934
+ runSignature: getRunSignature(snapshot),
935
+ stepStatusSignature: getStepStatusSignature(snapshot),
936
+ stepProgressSignature: getStepProgressSignature(snapshot),
937
+ snapshotSignature: getSnapshotSignature(snapshot),
938
+ lastLogSeq: logDiff.lastLogSeq
939
+ };
940
+ const events = [];
941
+ if (next.stepStatusSignature !== previous.stepStatusSignature) {
942
+ for (const state of snapshot.nodeStates) {
943
+ if (state.status === "idle") {
944
+ continue;
945
+ }
946
+ const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
947
+ const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
948
+ events.push(
949
+ makeRunStreamEvent({
950
+ cursor,
951
+ streamId,
952
+ type: "play.step.status",
953
+ payload: {
954
+ runId: snapshot.runId,
955
+ stepId: state.nodeId,
956
+ status: state.status,
957
+ artifactTableNamespace: state.artifactTableNamespace ?? null,
958
+ ...resolveTimingWindow({
959
+ startedAt: persistedStartedAt,
960
+ completedAt: persistedCompletedAt,
961
+ updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
962
+ })
963
+ }
964
+ })
965
+ );
966
+ }
967
+ }
968
+ if (next.stepProgressSignature !== previous.stepProgressSignature) {
969
+ for (const state of snapshot.nodeStates) {
970
+ if (!state.progress) {
971
+ continue;
972
+ }
973
+ events.push(
974
+ makeRunStreamEvent({
975
+ cursor: String(
976
+ state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
977
+ ),
978
+ streamId,
979
+ type: "play.step.progress",
980
+ payload: {
981
+ runId: snapshot.runId,
982
+ stepId: state.nodeId,
983
+ completed: state.progress.completed,
984
+ total: state.progress.total,
985
+ failed: state.progress.failed,
986
+ message: state.progress.message,
987
+ artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
988
+ ...resolveTimingWindow({
989
+ startedAt: state.startedAt ?? state.progress.startedAt ?? null,
990
+ completedAt: state.completedAt ?? state.progress.completedAt ?? null,
991
+ updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
992
+ })
993
+ }
994
+ })
995
+ );
996
+ }
997
+ }
998
+ if (logDiff.lines.length > 0) {
999
+ events.push(
1000
+ makeRunStreamEvent({
1001
+ cursor,
1002
+ streamId,
1003
+ type: "play.run.log",
1004
+ payload: {
1005
+ runId: snapshot.runId,
1006
+ lines: logDiff.lines,
1007
+ source: "worker",
1008
+ ...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
1009
+ totalLogCount: snapshot.totalLogCount
1010
+ }
1011
+ })
1012
+ );
1013
+ }
1014
+ if (next.snapshotSignature !== previous.snapshotSignature) {
1015
+ const enrichedNodeStates = snapshot.nodeStates.map((state) => {
1016
+ const timing = resolveTimingWindow({
1017
+ startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
1018
+ completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
1019
+ updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
1020
+ });
1021
+ return {
1022
+ ...state,
1023
+ ...timing,
1024
+ progress: state.progress ? {
1025
+ ...state.progress,
1026
+ ...resolveTimingWindow({
1027
+ startedAt: state.progress.startedAt ?? state.startedAt ?? null,
1028
+ completedAt: state.progress.completedAt ?? state.completedAt ?? null,
1029
+ updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
1030
+ })
1031
+ } : state.progress
1032
+ };
1033
+ });
1034
+ events.push(
1035
+ makeRunStreamEvent({
1036
+ cursor,
1037
+ streamId,
1038
+ type: "play.run.snapshot",
1039
+ payload: { ...snapshot, nodeStates: enrichedNodeStates }
1040
+ })
1041
+ );
1042
+ }
1043
+ if (next.runSignature !== previous.runSignature) {
1044
+ events.push(
1045
+ makeRunStreamEvent({
1046
+ cursor,
1047
+ streamId,
1048
+ type: "play.run.status",
1049
+ payload: {
1050
+ runId: snapshot.runId,
1051
+ status: snapshot.status,
1052
+ updatedAt: snapshot.updatedAt
1053
+ }
1054
+ })
1055
+ );
1056
+ }
1057
+ return { events, next };
1058
+ }
1059
+
1060
+ // src/runs/observe-transport.ts
1061
+ var RunObserveTransportUnavailableError = class extends Error {
1062
+ constructor(message, reason) {
1063
+ super(message);
1064
+ this.reason = reason;
1065
+ this.name = "RunObserveTransportUnavailableError";
1066
+ }
1067
+ reason;
1068
+ };
1069
+ var OBSERVE_BOOTSTRAP_TIMEOUT_MS = 1e4;
1070
+ var OBSERVE_RECONNECT_NOTICE_MS = 1e4;
1071
+ var OBSERVE_STALE_WARNING_MS = 12e4;
1072
+ var OBSERVE_WATCHDOG_TICK_MS = 5e3;
1073
+ var GRANT_REFRESH_MARGIN_MS = 5 * 6e4;
1074
+ var BACKFILL_PAGE_LIMIT = 1e3;
1075
+ var BACKFILL_MAX_PAGES = 30;
1076
+ var OBSERVER_SNAPSHOT_QUERY = "runObservers:getPlayRunSnapshotForObserver";
1077
+ var OBSERVER_LOG_PAGE_QUERY = "runObservers:getRunLogPageForObserver";
1078
+ function errorText(error) {
1079
+ return error instanceof Error ? error.message : String(error);
1080
+ }
1081
+ async function mintRunObserveGrant(http, runId) {
1082
+ let response;
1083
+ try {
1084
+ response = await http.post(
1085
+ `/api/v2/runs/${encodeURIComponent(runId)}/observe-grant`,
1086
+ {}
1087
+ );
1088
+ } catch (error) {
1089
+ if (error instanceof DeeplineError) {
1090
+ if (error.statusCode === 401 || error.statusCode === 403) {
1091
+ throw error;
1092
+ }
1093
+ throw new RunObserveTransportUnavailableError(
1094
+ `observe-grant endpoint unavailable (${error.statusCode ?? "network"}): ${error.message}`,
1095
+ "grant_endpoint_unavailable"
1096
+ );
1097
+ }
1098
+ throw new RunObserveTransportUnavailableError(
1099
+ `observe-grant request failed: ${errorText(error)}`,
1100
+ "grant_request_failed"
1101
+ );
1102
+ }
1103
+ const grant = response;
1104
+ if (!grant || typeof grant.convexUrl !== "string" || !grant.convexUrl.trim() || typeof grant.token !== "string" || !grant.token.trim() || typeof grant.expiresAt !== "number") {
1105
+ throw new RunObserveTransportUnavailableError(
1106
+ "observe-grant endpoint returned an invalid grant payload.",
1107
+ "grant_payload_invalid"
1108
+ );
1109
+ }
1110
+ return grant;
1111
+ }
1112
+ async function backfillLogGap(input2) {
1113
+ const lines = [];
1114
+ let cursor = input2.lastLogSeq;
1115
+ for (let page = 0; page < BACKFILL_MAX_PAGES; page += 1) {
1116
+ if (cursor >= input2.tailFirstSeq - 1) {
1117
+ break;
1118
+ }
1119
+ let logPage;
1120
+ try {
1121
+ logPage = await input2.queryLogPage(
1122
+ cursor,
1123
+ Math.min(BACKFILL_PAGE_LIMIT, input2.tailFirstSeq - 1 - cursor)
1124
+ );
1125
+ } catch {
1126
+ return null;
1127
+ }
1128
+ const entries = (logPage?.entries ?? []).filter(
1129
+ (entry) => entry.seq > cursor && entry.seq < input2.tailFirstSeq
1130
+ );
1131
+ if (entries.length === 0) {
1132
+ break;
1133
+ }
1134
+ for (const entry of entries) {
1135
+ if (entry.seq !== cursor + 1) {
1136
+ return null;
1137
+ }
1138
+ lines.push(entry.line);
1139
+ cursor = entry.seq;
1140
+ }
1141
+ }
1142
+ if (cursor < input2.tailFirstSeq - 1) {
1143
+ return null;
1144
+ }
1145
+ return lines;
1146
+ }
1147
+ async function* observeRunEvents(options) {
1148
+ const { http, runId } = options;
1149
+ let grant = await mintRunObserveGrant(http, runId);
1150
+ let convexBrowser;
1151
+ let convexServer;
1152
+ try {
1153
+ convexBrowser = await import("convex/browser");
1154
+ convexServer = await import("convex/server");
1155
+ } catch (error) {
1156
+ throw new RunObserveTransportUnavailableError(
1157
+ `convex client module unavailable: ${errorText(error)}`,
1158
+ "convex_module_unavailable"
1159
+ );
1160
+ }
1161
+ let webSocketConstructor;
1162
+ if (typeof WebSocket === "undefined") {
1163
+ try {
1164
+ const wsModuleName = "ws";
1165
+ const ws = await import(wsModuleName);
1166
+ webSocketConstructor = ws.default;
1167
+ } catch (error) {
1168
+ throw new RunObserveTransportUnavailableError(
1169
+ `no WebSocket implementation available: ${errorText(error)}`,
1170
+ "websocket_unavailable"
1171
+ );
1172
+ }
1173
+ }
1174
+ const snapshotQuery = convexServer.makeFunctionReference(
1175
+ OBSERVER_SNAPSHOT_QUERY
1176
+ );
1177
+ const logPageQuery = convexServer.makeFunctionReference(
1178
+ OBSERVER_LOG_PAGE_QUERY
1179
+ );
1180
+ const client = new convexBrowser.ConvexClient(grant.convexUrl, {
1181
+ ...webSocketConstructor ? { webSocketConstructor } : {},
1182
+ unsavedChangesWarning: false
1183
+ });
1184
+ const queue = [];
1185
+ let wake = null;
1186
+ const push = (item) => {
1187
+ queue.push(item);
1188
+ wake?.();
1189
+ wake = null;
1190
+ };
1191
+ let lastForcedRefreshAt = 0;
1192
+ client.setAuth(async ({ forceRefreshToken }) => {
1193
+ const now = Date.now();
1194
+ if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
1195
+ return grant.token;
1196
+ }
1197
+ if (forceRefreshToken && now - lastForcedRefreshAt < 5e3) {
1198
+ push({
1199
+ kind: "error",
1200
+ error: new DeeplineError(
1201
+ `Run observe grant for ${runId} was rejected after a re-mint. The server and Convex deployment disagree on the grant issuer/JWKS.`,
1202
+ 401,
1203
+ "RUN_OBSERVE_GRANT_REJECTED"
1204
+ )
1205
+ });
1206
+ return null;
1207
+ }
1208
+ if (forceRefreshToken) {
1209
+ lastForcedRefreshAt = now;
1210
+ }
1211
+ try {
1212
+ grant = await mintRunObserveGrant(http, runId);
1213
+ return grant.token;
1214
+ } catch (error) {
1215
+ push({ kind: "error", error });
1216
+ return null;
1217
+ }
1218
+ });
1219
+ const unsubscribe = client.onUpdate(
1220
+ snapshotQuery,
1221
+ { workflowId: runId },
1222
+ (run) => push({ kind: "run", run: run ?? null }),
1223
+ (error) => push({ kind: "error", error })
1224
+ );
1225
+ let lastUpdateAt = Date.now();
1226
+ let lastStatusTerminal = false;
1227
+ let disconnectedSince = null;
1228
+ let warnedReconnecting = false;
1229
+ let warnedStale = false;
1230
+ const watchdog = setInterval(() => {
1231
+ const now = Date.now();
1232
+ try {
1233
+ const connectionState = client.connectionState();
1234
+ if (connectionState.isWebSocketConnected) {
1235
+ disconnectedSince = null;
1236
+ warnedReconnecting = false;
1237
+ } else {
1238
+ disconnectedSince ??= now;
1239
+ if (!warnedReconnecting && now - disconnectedSince >= OBSERVE_RECONNECT_NOTICE_MS) {
1240
+ warnedReconnecting = true;
1241
+ options.onNotice?.(
1242
+ `[observe] connection lost; reconnecting to live run ${runId}\u2026`
1243
+ );
1244
+ }
1245
+ }
1246
+ } catch {
1247
+ }
1248
+ if (!lastStatusTerminal && !warnedStale && now - lastUpdateAt >= OBSERVE_STALE_WARNING_MS) {
1249
+ warnedStale = true;
1250
+ options.onNotice?.(
1251
+ `[observe] no live updates for ${Math.round((now - lastUpdateAt) / 1e3)}s; run ${runId} may be stalled (status checks continue)`
1252
+ );
1253
+ }
1254
+ }, OBSERVE_WATCHDOG_TICK_MS);
1255
+ watchdog.unref?.();
1256
+ const abortListener = () => push({
1257
+ kind: "error",
1258
+ error: new DeeplineError(
1259
+ "Run observation aborted.",
1260
+ void 0,
1261
+ "ABORTED"
1262
+ )
1263
+ });
1264
+ options.signal?.addEventListener("abort", abortListener, { once: true });
1265
+ let diffState = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
1266
+ const streamId = ["observe", runId].join(":");
1267
+ let sawFirstSnapshot = false;
1268
+ try {
1269
+ for (; ; ) {
1270
+ if (queue.length === 0) {
1271
+ const waitForItem = new Promise((resolve14) => {
1272
+ wake = resolve14;
1273
+ });
1274
+ if (!sawFirstSnapshot) {
1275
+ const timedOut = await Promise.race([
1276
+ waitForItem.then(() => false),
1277
+ new Promise(
1278
+ (resolve14) => setTimeout(() => resolve14(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1279
+ )
1280
+ ]);
1281
+ if (timedOut && queue.length === 0) {
1282
+ throw new RunObserveTransportUnavailableError(
1283
+ `no snapshot from Convex at ${grant.convexUrl} within ${OBSERVE_BOOTSTRAP_TIMEOUT_MS}ms`,
1284
+ "convex_unreachable"
1285
+ );
1286
+ }
1287
+ } else {
1288
+ await waitForItem;
1289
+ }
1290
+ continue;
1291
+ }
1292
+ const item = queue.shift();
1293
+ if (item.kind === "error") {
1294
+ if (options.signal?.aborted) {
1295
+ return;
1296
+ }
1297
+ throw item.error;
1298
+ }
1299
+ sawFirstSnapshot = true;
1300
+ lastUpdateAt = Date.now();
1301
+ warnedStale = false;
1302
+ if (item.run === null) {
1303
+ throw new DeeplineError(
1304
+ `Run ${runId} was not found (or is not visible to this grant).`,
1305
+ 404,
1306
+ "RUN_NOT_FOUND"
1307
+ );
1308
+ }
1309
+ const snapshot = buildPlayRunStatusSnapshot({ run: item.run });
1310
+ lastStatusTerminal = isTerminalPlayRunLiveStatus(snapshot.status);
1311
+ const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
1312
+ if (gap && diffState.lastLogSeq > 0) {
1313
+ const backfilled = await backfillLogGap({
1314
+ queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
1315
+ workflowId: runId,
1316
+ afterSeq,
1317
+ limit
1318
+ }),
1319
+ lastLogSeq: diffState.lastLogSeq,
1320
+ tailFirstSeq: gap.tailFirstSeq
1321
+ });
1322
+ if (backfilled && backfilled.length > 0) {
1323
+ yield {
1324
+ cursor: String(snapshot.updatedAt ?? Date.now()),
1325
+ streamId,
1326
+ scope: "play",
1327
+ type: "play.run.log",
1328
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1329
+ payload: {
1330
+ runId: snapshot.runId,
1331
+ lines: backfilled,
1332
+ source: "worker",
1333
+ firstSeq: diffState.lastLogSeq + 1,
1334
+ totalLogCount: snapshot.totalLogCount
1335
+ }
1336
+ };
1337
+ diffState = { ...diffState, lastLogSeq: gap.tailFirstSeq - 1 };
1338
+ }
1339
+ }
1340
+ const { events, next } = diffPlayRunStreamEvents({
1341
+ streamId,
1342
+ snapshot,
1343
+ previous: diffState
1344
+ });
1345
+ diffState = next;
1346
+ const ordered = [
1347
+ ...events.filter((event) => event.type === "play.run.snapshot"),
1348
+ ...events.filter((event) => event.type !== "play.run.snapshot")
1349
+ ];
1350
+ for (const event of ordered) {
1351
+ yield event;
1352
+ }
1353
+ if (lastStatusTerminal) {
1354
+ return;
1355
+ }
1356
+ }
1357
+ } finally {
1358
+ clearInterval(watchdog);
1359
+ options.signal?.removeEventListener("abort", abortListener);
1360
+ try {
1361
+ unsubscribe();
1362
+ } catch {
1363
+ }
1364
+ await client.close().catch(() => void 0);
1365
+ }
1366
+ }
1367
+
567
1368
  // src/client.ts
568
1369
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
569
1370
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -582,7 +1383,8 @@ function isTransientCompileManifestError(error) {
582
1383
  message
583
1384
  );
584
1385
  }
585
- function isRecord(value) {
1386
+ var RUN_LOGS_PAGE_LIMIT = 1e3;
1387
+ function isRecord2(value) {
586
1388
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
587
1389
  }
588
1390
  function isPrebuiltPlayDescription(play) {
@@ -652,10 +1454,31 @@ function normalizeLiveStatus(value) {
652
1454
  }
653
1455
  return null;
654
1456
  }
1457
+ function appendPlayLiveLogLines(state, payload) {
1458
+ const lines = readStringArray(payload.lines);
1459
+ if (lines.length === 0) {
1460
+ return;
1461
+ }
1462
+ const firstSeq = typeof payload.firstSeq === "number" && Number.isFinite(payload.firstSeq) && payload.firstSeq >= 1 ? Math.trunc(payload.firstSeq) : null;
1463
+ if (firstSeq === null) {
1464
+ state.logs.push(...lines);
1465
+ const totalLogCount = typeof payload.totalLogCount === "number" && Number.isFinite(payload.totalLogCount) ? Math.trunc(payload.totalLogCount) : null;
1466
+ if (totalLogCount !== null) {
1467
+ state.lastLogSeq = Math.max(state.lastLogSeq, totalLogCount);
1468
+ }
1469
+ return;
1470
+ }
1471
+ const skip = Math.max(0, state.lastLogSeq + 1 - firstSeq);
1472
+ if (skip >= lines.length) {
1473
+ return;
1474
+ }
1475
+ state.logs.push(...lines.slice(skip));
1476
+ state.lastLogSeq = Math.max(state.lastLogSeq, firstSeq + lines.length - 1);
1477
+ }
655
1478
  function updatePlayLiveStatusState(state, event) {
656
1479
  const payload = getPlayLiveEventPayload(event);
657
1480
  if (event.type === "play.run.log") {
658
- state.logs.push(...readStringArray(payload.lines));
1481
+ appendPlayLiveLogLines(state, payload);
659
1482
  return null;
660
1483
  }
661
1484
  if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
@@ -663,12 +1486,14 @@ function updatePlayLiveStatusState(state, event) {
663
1486
  }
664
1487
  const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
665
1488
  const status = normalizeLiveStatus(payload.status) ?? (isPlayRunPackage(payload) ? normalizeLiveStatus(payload.run.status) : null) ?? state.status;
666
- const progressPayload = isRecord(payload.progress) ? payload.progress : {};
667
- const payloadLogs = readStringArray(payload.logs);
668
- const progressLogs = readStringArray(progressPayload.logs);
669
- const logs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
670
- if (logs.length > 0 || event.type === "play.run.snapshot" || event.type === "play.run.final_status" && !isPlayRunPackage(payload)) {
671
- state.logs = logs;
1489
+ const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
1490
+ if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
1491
+ const payloadLogs = readStringArray(payload.logs);
1492
+ const progressLogs = readStringArray(progressPayload.logs);
1493
+ const seedLogs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
1494
+ if (seedLogs.length > 0) {
1495
+ state.logs = seedLogs;
1496
+ }
672
1497
  }
673
1498
  if ("result" in payload) {
674
1499
  state.result = payload.result;
@@ -762,9 +1587,9 @@ var DeeplineClient = class {
762
1587
  return fields.length > 0 ? { fields } : schema;
763
1588
  }
764
1589
  schemaMetadata(schema, key) {
765
- if (!isRecord(schema)) return null;
1590
+ if (!isRecord2(schema)) return null;
766
1591
  const value = schema[key];
767
- return isRecord(value) ? value : null;
1592
+ return isRecord2(value) ? value : null;
768
1593
  }
769
1594
  playRunCommand(play, options) {
770
1595
  const target = play.reference || play.name;
@@ -811,7 +1636,7 @@ var DeeplineClient = class {
811
1636
  aliases,
812
1637
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
813
1638
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
814
- staticPipeline: isRecord(play.staticPipeline) ? play.staticPipeline : isRecord(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
1639
+ staticPipeline: isRecord2(play.staticPipeline) ? play.staticPipeline : isRecord2(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord2(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
815
1640
  ...csvInput ? { csvInput } : {},
816
1641
  ...rowOutputSchema ? { rowOutputSchema } : {},
817
1642
  runCommand: runCommand2,
@@ -1495,43 +2320,140 @@ var DeeplineClient = class {
1495
2320
  );
1496
2321
  return response.runs ?? [];
1497
2322
  }
1498
- /** Read the canonical run stream and return the latest run snapshot. */
1499
- async tailRun(runId, options) {
2323
+ /**
2324
+ * Observe one run's live events through the Convex Run Snapshot
2325
+ * subscription transport (ADR-0008). Yields the same `play.*` event
2326
+ * envelopes as {@link streamPlayRunEvents} and ends after the terminal
2327
+ * snapshot. Throws {@link RunObserveTransportUnavailableError} when this
2328
+ * server cannot serve the transport (older server, unconfigured grants, or
2329
+ * unreachable Convex) — callers fall back to the SSE stream with a notice.
2330
+ */
2331
+ observeRunEvents(runId, options) {
2332
+ return observeRunEvents({
2333
+ http: this.http,
2334
+ runId,
2335
+ signal: options?.signal,
2336
+ onNotice: options?.onNotice
2337
+ });
2338
+ }
2339
+ /**
2340
+ * Tail one run through the subscription transport until terminal, then
2341
+ * return one durable REST status read (the final Run Response Package).
2342
+ */
2343
+ async tailRunViaObserveTransport(runId, options) {
1500
2344
  const state = {
1501
2345
  runId,
1502
2346
  status: "running",
1503
2347
  logs: [],
2348
+ lastLogSeq: 0,
1504
2349
  latest: null
1505
2350
  };
1506
- let terminal = false;
1507
- for await (const event of this.streamPlayRunEvents(runId, {
1508
- mode: "cli",
1509
- signal: options?.signal
2351
+ for await (const event of this.observeRunEvents(runId, {
2352
+ signal: options?.signal,
2353
+ onNotice: options?.onNotice
1510
2354
  })) {
1511
2355
  const status = updatePlayLiveStatusState(state, event);
1512
- if (!status) {
2356
+ if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
1513
2357
  continue;
1514
2358
  }
1515
- terminal = TERMINAL_PLAY_STATUSES.has(status.status);
1516
- if (terminal) {
1517
- break;
1518
- }
1519
- }
1520
- if (terminal && state.latest) {
1521
- return await this.getRunStatus(state.latest.runId || runId).catch(
2359
+ return await this.getRunStatus(status.runId || runId).catch(
1522
2360
  () => state.latest ?? playRunStatusFromState(state)
1523
2361
  );
1524
2362
  }
1525
- if (state.latest) {
1526
- return state.latest;
2363
+ if (options?.signal?.aborted) {
2364
+ throw new DeeplineError("Run observation aborted.", void 0, "ABORTED");
2365
+ }
2366
+ const refreshed = await this.getRunStatus(runId);
2367
+ if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2368
+ return refreshed;
1527
2369
  }
1528
2370
  throw new DeeplineError(
1529
- `Run stream for ${runId} ended before the initial snapshot.`,
2371
+ `Run observation for ${runId} ended before a terminal status.`,
1530
2372
  void 0,
1531
- "PLAY_RUN_STREAM_EMPTY",
1532
- { runId }
2373
+ "PLAY_LIVE_STREAM_ENDED"
1533
2374
  );
1534
2375
  }
2376
+ /**
2377
+ * Read the canonical run stream until a terminal run status is observed.
2378
+ *
2379
+ * Tries the Convex Run Snapshot subscription transport first (ADR-0008);
2380
+ * when the server cannot serve it (grant endpoint missing/unconfigured or
2381
+ * Convex unreachable) it falls back — with one `onNotice` message — to the
2382
+ * support-window SSE stream below.
2383
+ *
2384
+ * Server stream windows are finite: they end cleanly at the function
2385
+ * ceiling even while the run keeps executing. A window that ends (cleanly
2386
+ * or via transient network error) without a terminal event triggers one
2387
+ * durable-status re-check followed by a backed-off reconnect, so long runs
2388
+ * tail to completion. Abort via `options.signal` to stop waiting.
2389
+ */
2390
+ async tailRun(runId, options) {
2391
+ try {
2392
+ return await this.tailRunViaObserveTransport(runId, options);
2393
+ } catch (error) {
2394
+ if (!(error instanceof RunObserveTransportUnavailableError)) {
2395
+ throw error;
2396
+ }
2397
+ options?.onNotice?.(
2398
+ `[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
2399
+ );
2400
+ }
2401
+ const state = {
2402
+ runId,
2403
+ status: "running",
2404
+ logs: [],
2405
+ lastLogSeq: 0,
2406
+ latest: null
2407
+ };
2408
+ let reconnectAttempt = 0;
2409
+ for (; ; ) {
2410
+ const connectedAt = Date.now();
2411
+ let sawEvent = false;
2412
+ let endedReason = "stream window ended before a terminal event";
2413
+ try {
2414
+ for await (const event of this.streamPlayRunEvents(runId, {
2415
+ mode: "cli",
2416
+ signal: options?.signal
2417
+ })) {
2418
+ sawEvent = true;
2419
+ const status = updatePlayLiveStatusState(state, event);
2420
+ if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
2421
+ continue;
2422
+ }
2423
+ return await this.getRunStatus(status.runId || runId).catch(
2424
+ () => state.latest ?? playRunStatusFromState(state)
2425
+ );
2426
+ }
2427
+ } catch (error) {
2428
+ if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
2429
+ throw error;
2430
+ }
2431
+ endedReason = error instanceof Error ? error.message : String(error);
2432
+ }
2433
+ let refreshed = null;
2434
+ try {
2435
+ refreshed = await this.getRunStatus(runId);
2436
+ } catch (error) {
2437
+ if (!isTransientPlayStreamError(error)) {
2438
+ throw error;
2439
+ }
2440
+ }
2441
+ if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2442
+ return refreshed;
2443
+ }
2444
+ if (sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
2445
+ reconnectAttempt = 0;
2446
+ }
2447
+ const delayMs = streamReconnectDelayMs(reconnectAttempt);
2448
+ reconnectAttempt += 1;
2449
+ options?.onReconnect?.({
2450
+ attempt: reconnectAttempt,
2451
+ delayMs,
2452
+ reason: endedReason
2453
+ });
2454
+ await sleep2(delayMs);
2455
+ }
2456
+ }
1535
2457
  /**
1536
2458
  * Fetch persisted logs for a run using the public runs resource model.
1537
2459
  *
@@ -1542,19 +2464,40 @@ var DeeplineClient = class {
1542
2464
  * ```
1543
2465
  */
1544
2466
  async getRunLogs(runId, options) {
1545
- const status = await this.getRunStatus(runId, { full: true });
1546
- const logs = status.progress?.logs ?? [];
1547
- const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
1548
- const entries = logs.slice(Math.max(0, logs.length - limit));
2467
+ const limit = options?.all ? Number.MAX_SAFE_INTEGER : typeof options?.limit === "number" && Number.isFinite(options.limit) && options.limit > 0 ? Math.trunc(options.limit) : 200;
2468
+ const fetchPage = (afterSeq2, pageLimit) => this.http.get(
2469
+ `/api/v2/runs/${encodeURIComponent(runId)}/logs?afterSeq=${afterSeq2}&limit=${pageLimit}`
2470
+ );
2471
+ const probe = await fetchPage(0, 1);
2472
+ const lastStoredSeq = probe.lastStoredSeq;
2473
+ let afterSeq = options?.all ? 0 : Math.max(0, lastStoredSeq - limit);
2474
+ const entries = [];
2475
+ while (entries.length < limit) {
2476
+ const page = await fetchPage(
2477
+ afterSeq,
2478
+ Math.min(RUN_LOGS_PAGE_LIMIT, limit - entries.length)
2479
+ );
2480
+ if (page.entries.length === 0) {
2481
+ break;
2482
+ }
2483
+ entries.push(...page.entries);
2484
+ afterSeq = page.entries[page.entries.length - 1].seq;
2485
+ if (!page.hasMore) {
2486
+ break;
2487
+ }
2488
+ }
2489
+ const firstSequence = entries.length > 0 ? entries[0].seq : null;
2490
+ const lastSequence = entries.length > 0 ? entries[entries.length - 1].seq : null;
1549
2491
  return {
1550
- runId: status.runId,
1551
- totalCount: logs.length,
2492
+ runId: probe.runId,
2493
+ totalCount: probe.totalLogCount,
1552
2494
  returnedCount: entries.length,
1553
- firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
1554
- lastSequence: logs.length === 0 ? null : logs.length,
1555
- truncated: logs.length > entries.length,
1556
- hasMore: logs.length > entries.length,
1557
- entries
2495
+ firstSequence,
2496
+ lastSequence,
2497
+ truncated: entries.length < probe.totalLogCount,
2498
+ hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
2499
+ entries: entries.map((entry) => entry.line),
2500
+ ...probe.logsTruncated ? { logsTruncated: true } : {}
1558
2501
  };
1559
2502
  }
1560
2503
  /**
@@ -1845,6 +2788,7 @@ var DeeplineClient = class {
1845
2788
  runId: workflowId,
1846
2789
  status: "running",
1847
2790
  logs: [],
2791
+ lastLogSeq: 0,
1848
2792
  latest: null
1849
2793
  };
1850
2794
  if (options?.signal?.aborted) {
@@ -3539,11 +4483,11 @@ function sanitizeCsvProjectionInfo(input2) {
3539
4483
  const rows = input2.rows.map(stripCsvProjectionFields);
3540
4484
  return { rows, columns };
3541
4485
  }
3542
- function isRecord2(value) {
4486
+ function isRecord3(value) {
3543
4487
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
3544
4488
  }
3545
4489
  function isSerializedDataset(value) {
3546
- return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
4490
+ return isRecord3(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
3547
4491
  }
3548
4492
  function pathParts(path) {
3549
4493
  return path.split(".").map((part) => part.trim()).filter(Boolean);
@@ -3551,7 +4495,7 @@ function pathParts(path) {
3551
4495
  function valueAtPath(root, path) {
3552
4496
  let cursor = root;
3553
4497
  for (const part of pathParts(path)) {
3554
- if (!isRecord2(cursor)) {
4498
+ if (!isRecord3(cursor)) {
3555
4499
  return void 0;
3556
4500
  }
3557
4501
  cursor = cursor[part];
@@ -3559,17 +4503,17 @@ function valueAtPath(root, path) {
3559
4503
  return cursor;
3560
4504
  }
3561
4505
  function totalRowsForDataset(result, datasetPath) {
3562
- const metadata = isRecord2(result._metadata) ? result._metadata : null;
4506
+ const metadata = isRecord3(result._metadata) ? result._metadata : null;
3563
4507
  const parentPath = datasetPath.split(".").slice(0, -1).join(".");
3564
4508
  const parent = parentPath ? valueAtPath({ result }, parentPath) : result;
3565
- return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord2(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
4509
+ return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord3(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
3566
4510
  }
3567
4511
  function rowArray(value) {
3568
4512
  if (!Array.isArray(value)) {
3569
4513
  return null;
3570
4514
  }
3571
4515
  const rows = value.filter(
3572
- (row) => isRecord2(row)
4516
+ (row) => isRecord3(row)
3573
4517
  );
3574
4518
  return rows.length === value.length ? rows : null;
3575
4519
  }
@@ -3646,7 +4590,7 @@ function collectDatasetCandidates(input2) {
3646
4590
  });
3647
4591
  return;
3648
4592
  }
3649
- if (!isRecord2(input2.value)) {
4593
+ if (!isRecord3(input2.value)) {
3650
4594
  return;
3651
4595
  }
3652
4596
  for (const [key, child] of Object.entries(input2.value)) {
@@ -3663,12 +4607,12 @@ function collectDatasetCandidates(input2) {
3663
4607
  }
3664
4608
  }
3665
4609
  function collectCanonicalRowsInfos(statusOrResult) {
3666
- const root = isRecord2(statusOrResult) ? statusOrResult : null;
3667
- const result = isRecord2(root?.result) ? root.result : root;
4610
+ const root = isRecord3(statusOrResult) ? statusOrResult : null;
4611
+ const result = isRecord3(root?.result) ? root.result : root;
3668
4612
  if (!result) {
3669
4613
  return [];
3670
4614
  }
3671
- const metadata = isRecord2(result._metadata) ? result._metadata : null;
4615
+ const metadata = isRecord3(result._metadata) ? result._metadata : null;
3672
4616
  const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
3673
4617
  const candidates = [
3674
4618
  {
@@ -3692,8 +4636,8 @@ function collectCanonicalRowsInfos(statusOrResult) {
3692
4636
  total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count
3693
4637
  }
3694
4638
  ];
3695
- if (isRecord2(result.output)) {
3696
- const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
4639
+ if (isRecord3(result.output)) {
4640
+ const outputMetadata = isRecord3(result.output._metadata) ? result.output._metadata : null;
3697
4641
  const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
3698
4642
  candidates.push(
3699
4643
  {
@@ -3739,8 +4683,8 @@ function collectCanonicalRowsInfos(statusOrResult) {
3739
4683
  return infos;
3740
4684
  }
3741
4685
  function collectSerializedDatasetRowsInfos(statusOrResult) {
3742
- const root = isRecord2(statusOrResult) ? statusOrResult : null;
3743
- const result = isRecord2(root?.result) ? root.result : root;
4686
+ const root = isRecord3(statusOrResult) ? statusOrResult : null;
4687
+ const result = isRecord3(root?.result) ? root.result : root;
3744
4688
  if (!result) {
3745
4689
  return [];
3746
4690
  }
@@ -3774,17 +4718,17 @@ function percentText(numerator, denominator) {
3774
4718
  return datasetSummaryPercentText(numerator, denominator);
3775
4719
  }
3776
4720
  function isDatasetExecutionStatsInput(value) {
3777
- return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
4721
+ return isRecord3(value) && isRecord3(value.columnStats) && Object.values(value.columnStats).every(isRecord3);
3778
4722
  }
3779
4723
  function extractDatasetExecutionStats(statusOrResult) {
3780
- if (!isRecord2(statusOrResult)) {
4724
+ if (!isRecord3(statusOrResult)) {
3781
4725
  return null;
3782
4726
  }
3783
4727
  const direct = statusOrResult.dataset_execution_stats;
3784
4728
  if (isDatasetExecutionStatsInput(direct)) {
3785
4729
  return direct;
3786
4730
  }
3787
- const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
4731
+ const nested = isRecord3(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
3788
4732
  return isDatasetExecutionStatsInput(nested) ? nested : null;
3789
4733
  }
3790
4734
  function countPercentText(count, denominator) {
@@ -3825,13 +4769,13 @@ function summarizeSampleValue(value, depth = 0) {
3825
4769
  if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
3826
4770
  if (depth >= 3) {
3827
4771
  if (Array.isArray(parsed)) return [];
3828
- if (isRecord2(parsed)) return {};
4772
+ if (isRecord3(parsed)) return {};
3829
4773
  return compactScalar(parsed);
3830
4774
  }
3831
4775
  if (Array.isArray(parsed)) {
3832
4776
  return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
3833
4777
  }
3834
- if (isRecord2(parsed)) {
4778
+ if (isRecord3(parsed)) {
3835
4779
  const out = {};
3836
4780
  for (const [key, nested] of Object.entries(parsed)) {
3837
4781
  if (["__dl", "meta", "metadata"].includes(key)) {
@@ -3862,7 +4806,7 @@ function compactCell(value) {
3862
4806
  }
3863
4807
  return `[${parsed.length} items]`;
3864
4808
  }
3865
- if (isRecord2(parsed)) {
4809
+ if (isRecord3(parsed)) {
3866
4810
  for (const key of ["matched_result", "output"]) {
3867
4811
  if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
3868
4812
  return compactCell(parsed[key]);
@@ -6201,17 +7145,17 @@ function parsePositiveInteger2(value, flagName) {
6201
7145
  }
6202
7146
  return parsed;
6203
7147
  }
6204
- function isRecord3(value) {
7148
+ function isRecord4(value) {
6205
7149
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6206
7150
  }
6207
7151
  function stringValue(value) {
6208
7152
  return typeof value === "string" ? value.trim() : "";
6209
7153
  }
6210
7154
  function extractionEntries(value) {
6211
- if (Array.isArray(value)) return value.filter(isRecord3);
6212
- if (!isRecord3(value)) return [];
7155
+ if (Array.isArray(value)) return value.filter(isRecord4);
7156
+ if (!isRecord4(value)) return [];
6213
7157
  return Object.entries(value).map(
6214
- ([name, entry]) => isRecord3(entry) ? { name, ...entry } : { name }
7158
+ ([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
6215
7159
  );
6216
7160
  }
6217
7161
  var PlayBootstrapError = class extends Error {
@@ -6631,7 +7575,7 @@ function readCsvSampleRows(sample) {
6631
7575
  relax_column_count: true,
6632
7576
  trim: true
6633
7577
  });
6634
- return Array.isArray(parsedRows) ? parsedRows.filter(isRecord3) : [];
7578
+ return Array.isArray(parsedRows) ? parsedRows.filter(isRecord4) : [];
6635
7579
  }
6636
7580
  function readSourceCsvColumnSpecs(csvPath) {
6637
7581
  const sample = readCsvSample(csvPath);
@@ -6664,16 +7608,16 @@ function packagedCsvPathForPlay(csvPath) {
6664
7608
  return portablePath.startsWith(".") ? portablePath : `./${portablePath}`;
6665
7609
  }
6666
7610
  function getterNamesFromTool(tool, kind) {
6667
- const usageGuidance = isRecord3(tool?.usageGuidance) ? tool.usageGuidance : {};
6668
- const resultGuidance = isRecord3(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord3(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
7611
+ const usageGuidance = isRecord4(tool?.usageGuidance) ? tool.usageGuidance : {};
7612
+ const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
6669
7613
  const key = kind === "list" ? "extractedLists" : "extractedValues";
6670
7614
  const snakeKey = kind === "list" ? "extracted_lists" : "extracted_values";
6671
7615
  return extractionEntries(resultGuidance[key] ?? resultGuidance[snakeKey]).map((entry) => stringValue(entry.name)).filter(Boolean);
6672
7616
  }
6673
7617
  function targetGettersFromTool(tool) {
6674
- const record = isRecord3(tool) ? tool : {};
7618
+ const record = isRecord4(tool) ? tool : {};
6675
7619
  const raw = record.targetGetters ?? record.target_getters;
6676
- if (!isRecord3(raw)) return {};
7620
+ if (!isRecord4(raw)) return {};
6677
7621
  const entries = [];
6678
7622
  for (const [target, value] of Object.entries(raw)) {
6679
7623
  const paths = Array.isArray(value) ? value.map((path) => typeof path === "string" ? path.trim() : "").filter(Boolean) : [];
@@ -6694,10 +7638,10 @@ function listRowCandidateKeysFromTool(tool) {
6694
7638
  return [...keys].sort();
6695
7639
  }
6696
7640
  function inputPropertyNames(schema) {
6697
- if (!isRecord3(schema)) return [];
6698
- if (isRecord3(schema.properties)) return Object.keys(schema.properties);
7641
+ if (!isRecord4(schema)) return [];
7642
+ if (isRecord4(schema.properties)) return Object.keys(schema.properties);
6699
7643
  if (Array.isArray(schema.fields)) {
6700
- return schema.fields.filter(isRecord3).map((field) => stringValue(field.name)).filter(Boolean);
7644
+ return schema.fields.filter(isRecord4).map((field) => stringValue(field.name)).filter(Boolean);
6701
7645
  }
6702
7646
  return [];
6703
7647
  }
@@ -6711,7 +7655,7 @@ function schemaFieldDetails(schema) {
6711
7655
  return { required, optional };
6712
7656
  }
6713
7657
  function jsonSchemaTypeExpression(schema) {
6714
- if (!isRecord3(schema)) return "unknown";
7658
+ if (!isRecord4(schema)) return "unknown";
6715
7659
  const type = schema.type;
6716
7660
  if (Array.isArray(type)) {
6717
7661
  return type.map((entry) => jsonSchemaTypeExpression({ ...schema, type: entry })).join(" | ");
@@ -6741,7 +7685,7 @@ function jsonSchemaTypeExpression(schema) {
6741
7685
  }
6742
7686
  }
6743
7687
  function objectPropertySchema(schema, property) {
6744
- return isRecord3(schema) && isRecord3(schema.properties) ? schema.properties[property] : null;
7688
+ return isRecord4(schema) && isRecord4(schema.properties) ? schema.properties[property] : null;
6745
7689
  }
6746
7690
  function playOutputHasField(schema, field) {
6747
7691
  return objectPropertySchema(schema, field) != null;
@@ -6917,14 +7861,14 @@ ${indent2.slice(2)}}`;
6917
7861
  }
6918
7862
  function requiredPlayInputFields(play) {
6919
7863
  const schema = play?.inputSchema;
6920
- if (!isRecord3(schema)) return [];
7864
+ if (!isRecord4(schema)) return [];
6921
7865
  if (Array.isArray(schema.required)) {
6922
7866
  return schema.required.filter(
6923
7867
  (value) => typeof value === "string"
6924
7868
  );
6925
7869
  }
6926
7870
  if (Array.isArray(schema.fields)) {
6927
- return schema.fields.filter(isRecord3).filter(
7871
+ return schema.fields.filter(isRecord4).filter(
6928
7872
  (field) => field.required === true && typeof field.name === "string"
6929
7873
  ).map((field) => String(field.name));
6930
7874
  }
@@ -7105,7 +8049,7 @@ function validateBootstrapRoutes(input2) {
7105
8049
  }
7106
8050
  }
7107
8051
  function staticPipelineSubsteps(pipeline) {
7108
- if (!isRecord3(pipeline)) return [];
8052
+ if (!isRecord4(pipeline)) return [];
7109
8053
  return [
7110
8054
  ...extractionEntries(pipeline.stages),
7111
8055
  ...extractionEntries(pipeline.substeps)
@@ -7113,7 +8057,7 @@ function staticPipelineSubsteps(pipeline) {
7113
8057
  }
7114
8058
  function playUsesMapBackedRuntime(play) {
7115
8059
  const pipeline = play?.staticPipeline;
7116
- if (!isRecord3(pipeline)) return false;
8060
+ if (!isRecord4(pipeline)) return false;
7117
8061
  if (stringValue(pipeline.tableNamespace)) return true;
7118
8062
  return staticPipelineSubsteps(pipeline).some((substep) => {
7119
8063
  if (stringValue(substep.type) === "map") return true;
@@ -7122,7 +8066,7 @@ function playUsesMapBackedRuntime(play) {
7122
8066
  aliases: [],
7123
8067
  runCommand: "",
7124
8068
  examples: [],
7125
- staticPipeline: isRecord3(substep.pipeline) ? substep.pipeline : null
8069
+ staticPipeline: isRecord4(substep.pipeline) ? substep.pipeline : null
7126
8070
  });
7127
8071
  });
7128
8072
  }
@@ -8447,15 +9391,6 @@ function formatRunLine(run) {
8447
9391
  const credits = typeof run.billingTotalCredits === "number" && Number.isFinite(run.billingTotalCredits) ? `${formatCreditAmount(run.billingTotalCredits)} credits` : "\u2014";
8448
9392
  return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)} ${credits}`;
8449
9393
  }
8450
- function isTransientPlayStreamError(error) {
8451
- if (error instanceof DeeplineError && typeof error.statusCode === "number") {
8452
- return error.statusCode >= 500 && error.statusCode < 600;
8453
- }
8454
- const text = error instanceof Error ? error.message : String(error);
8455
- return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
8456
- text
8457
- );
8458
- }
8459
9394
  function playStatusErrorText(status) {
8460
9395
  const chunks = [];
8461
9396
  const progressError = status.progress?.error;
@@ -8793,97 +9728,227 @@ function assertPlayWaitNotTimedOut(input2) {
8793
9728
  }
8794
9729
  }
8795
9730
  async function waitForPlayCompletionByStream(input2) {
8796
- const controller = new AbortController();
8797
- let timedOut = false;
8798
9731
  let lastPhase = null;
8799
- const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
8800
- () => {
8801
- timedOut = true;
8802
- controller.abort();
8803
- },
8804
- Math.max(1, input2.waitTimeoutMs - (Date.now() - input2.startedAt))
8805
- );
8806
- try {
8807
- for await (const event of input2.client.streamPlayRunEvents(
8808
- input2.workflowId,
8809
- { signal: controller.signal }
8810
- )) {
8811
- assertPlayWaitNotTimedOut({ ...input2, lastPhase });
8812
- const phase = describeLiveEventPhase(event);
8813
- if (phase) {
8814
- lastPhase = phase;
8815
- input2.progress.phase(phase);
8816
- }
8817
- emitLiveDebugTableHints({
8818
- event,
8819
- playName: input2.playName,
8820
- runId: input2.workflowId,
8821
- jsonOutput: input2.jsonOutput,
8822
- state: input2.state,
8823
- progress: input2.progress
9732
+ let reconnectAttempt = 0;
9733
+ const withDashboardUrl = (status) => input2.dashboardUrl ? { ...status, dashboardUrl: input2.dashboardUrl } : status;
9734
+ const remainingWaitMs = () => input2.waitTimeoutMs === null ? null : input2.waitTimeoutMs - (Date.now() - input2.startedAt);
9735
+ const fetchTerminalStatus = async () => {
9736
+ let refreshed;
9737
+ try {
9738
+ refreshed = await input2.client.getPlayStatus(input2.workflowId, {
9739
+ billing: false
8824
9740
  });
8825
- printPlayLogLines({
8826
- lines: getLogLinesFromLiveEvent(event),
8827
- status: null,
8828
- jsonOutput: input2.jsonOutput,
8829
- emitLogs: input2.emitLogs,
9741
+ } catch (error) {
9742
+ if (isTransientPlayStreamError(error)) {
9743
+ return null;
9744
+ }
9745
+ throw error;
9746
+ }
9747
+ return TERMINAL_PLAY_STATUSES2.has(refreshed.status) ? withDashboardUrl(refreshed) : null;
9748
+ };
9749
+ const handleLiveEvent = async (event) => {
9750
+ assertPlayWaitNotTimedOut({ ...input2, lastPhase });
9751
+ const phase = describeLiveEventPhase(event);
9752
+ if (phase) {
9753
+ lastPhase = phase;
9754
+ input2.progress.phase(phase);
9755
+ }
9756
+ emitLiveDebugTableHints({
9757
+ event,
9758
+ playName: input2.playName,
9759
+ runId: input2.workflowId,
9760
+ jsonOutput: input2.jsonOutput,
9761
+ state: input2.state,
9762
+ progress: input2.progress
9763
+ });
9764
+ printPlayLogLines({
9765
+ lines: getLogLinesFromLiveEvent(event),
9766
+ status: null,
9767
+ jsonOutput: input2.jsonOutput,
9768
+ emitLogs: input2.emitLogs,
9769
+ state: input2.state,
9770
+ progress: input2.progress
9771
+ });
9772
+ if (!input2.jsonOutput) {
9773
+ const progressLines = getProgressLinesFromLiveEvent(event);
9774
+ printPlayProgressLines({
9775
+ lines: progressLines,
8830
9776
  state: input2.state,
8831
9777
  progress: input2.progress
8832
9778
  });
8833
- if (!input2.jsonOutput) {
8834
- const progressLines = getProgressLinesFromLiveEvent(event);
8835
- printPlayProgressLines({
8836
- lines: progressLines,
9779
+ if (progressLines.length === 0) {
9780
+ printPlayStatusHeartbeat({
9781
+ event,
9782
+ playName: input2.playName,
8837
9783
  state: input2.state,
8838
9784
  progress: input2.progress
8839
9785
  });
8840
- if (progressLines.length === 0) {
8841
- printPlayStatusHeartbeat({
8842
- event,
8843
- playName: input2.playName,
8844
- state: input2.state,
8845
- progress: input2.progress
8846
- });
8847
- }
8848
9786
  }
8849
- const finalStatus = getFinalStatusFromLiveEvent(event);
8850
- if (finalStatus) {
8851
- return input2.dashboardUrl ? { ...finalStatus, dashboardUrl: input2.dashboardUrl } : finalStatus;
9787
+ }
9788
+ const finalStatus = getFinalStatusFromLiveEvent(event);
9789
+ if (finalStatus) {
9790
+ return withDashboardUrl(finalStatus);
9791
+ }
9792
+ const status = getStatusFromLiveEvent(event);
9793
+ if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
9794
+ const refreshedStatus = await input2.client.getPlayStatus(
9795
+ input2.workflowId,
9796
+ {
9797
+ billing: false
9798
+ }
9799
+ );
9800
+ if (TERMINAL_PLAY_STATUSES2.has(refreshedStatus.status)) {
9801
+ return withDashboardUrl(refreshedStatus);
8852
9802
  }
8853
- const status = getStatusFromLiveEvent(event);
8854
- if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
8855
- const refreshedStatus = await input2.client.getPlayStatus(
8856
- input2.workflowId,
8857
- {
8858
- billing: false
8859
- }
8860
- );
8861
- if (TERMINAL_PLAY_STATUSES2.has(refreshedStatus.status)) {
8862
- return input2.dashboardUrl ? { ...refreshedStatus, dashboardUrl: input2.dashboardUrl } : refreshedStatus;
9803
+ }
9804
+ return null;
9805
+ };
9806
+ const watchViaObserveTransport = async () => {
9807
+ const controller = new AbortController();
9808
+ let timedOut = false;
9809
+ const remaining = remainingWaitMs();
9810
+ const timeout = remaining === null ? null : setTimeout(
9811
+ () => {
9812
+ timedOut = true;
9813
+ controller.abort();
9814
+ },
9815
+ Math.max(1, remaining)
9816
+ );
9817
+ try {
9818
+ for await (const event of input2.client.observeRunEvents(
9819
+ input2.workflowId,
9820
+ {
9821
+ signal: controller.signal,
9822
+ onNotice: input2.jsonOutput ? void 0 : (message) => input2.progress.writeLine(message)
9823
+ }
9824
+ )) {
9825
+ const terminal2 = await handleLiveEvent(event);
9826
+ if (terminal2) {
9827
+ return terminal2;
8863
9828
  }
8864
9829
  }
9830
+ } finally {
9831
+ if (timeout) {
9832
+ clearTimeout(timeout);
9833
+ }
9834
+ }
9835
+ const terminal = await fetchTerminalStatus();
9836
+ if (terminal) {
9837
+ return terminal;
8865
9838
  }
8866
- } catch (error) {
8867
9839
  if (timedOut) {
8868
9840
  assertPlayWaitNotTimedOut({ ...input2, lastPhase });
8869
9841
  }
8870
- throw error;
8871
- } finally {
8872
- if (timeout) {
8873
- clearTimeout(timeout);
9842
+ throw new DeeplineError(
9843
+ `Run observation for ${input2.workflowId} ended before a terminal status.`,
9844
+ void 0,
9845
+ "PLAY_LIVE_STREAM_ENDED",
9846
+ { runId: input2.workflowId, workflowId: input2.workflowId }
9847
+ );
9848
+ };
9849
+ try {
9850
+ return await watchViaObserveTransport();
9851
+ } catch (error) {
9852
+ if (!(error instanceof RunObserveTransportUnavailableError)) {
9853
+ throw error;
8874
9854
  }
8875
- }
8876
- const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8877
- throw new DeeplineError(
8878
- `Play watch stream ended before the run reached a terminal status. runId=${input2.workflowId}${phaseSuffix}. Inspect the current run with 'deepline runs get ${input2.workflowId} --full --json' or continue watching with 'deepline runs tail ${input2.workflowId}'.`,
8879
- void 0,
8880
- "PLAY_LIVE_STREAM_ENDED",
8881
- {
8882
- runId: input2.workflowId,
9855
+ if (!input2.jsonOutput) {
9856
+ input2.progress.writeLine(
9857
+ `[play watch] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
9858
+ );
9859
+ }
9860
+ recordCliTrace({
9861
+ phase: "cli.play_observe_transport_fallback",
9862
+ ms: Date.now() - input2.startedAt,
9863
+ ok: true,
9864
+ playName: input2.playName,
8883
9865
  workflowId: input2.workflowId,
8884
- ...lastPhase ? { phase: lastPhase } : {}
9866
+ reason: error.reason
9867
+ });
9868
+ }
9869
+ const streamOneWindow = async () => {
9870
+ const controller = new AbortController();
9871
+ let timedOut = false;
9872
+ let sawEvent = false;
9873
+ const remaining = remainingWaitMs();
9874
+ const timeout = remaining === null ? null : setTimeout(
9875
+ () => {
9876
+ timedOut = true;
9877
+ controller.abort();
9878
+ },
9879
+ Math.max(1, remaining)
9880
+ );
9881
+ try {
9882
+ for await (const event of input2.client.streamPlayRunEvents(
9883
+ input2.workflowId,
9884
+ { signal: controller.signal }
9885
+ )) {
9886
+ sawEvent = true;
9887
+ const terminal = await handleLiveEvent(event);
9888
+ if (terminal) {
9889
+ return { kind: "terminal", status: terminal };
9890
+ }
9891
+ }
9892
+ } catch (error) {
9893
+ if (timedOut) {
9894
+ return { kind: "timed_out", sawEvent };
9895
+ }
9896
+ if (!isTransientPlayStreamError(error)) {
9897
+ throw error;
9898
+ }
9899
+ return {
9900
+ kind: "ended",
9901
+ sawEvent,
9902
+ reason: error instanceof Error ? error.message : String(error)
9903
+ };
9904
+ } finally {
9905
+ if (timeout) {
9906
+ clearTimeout(timeout);
9907
+ }
8885
9908
  }
8886
- );
9909
+ return {
9910
+ kind: "ended",
9911
+ sawEvent,
9912
+ reason: "stream window ended before a terminal event"
9913
+ };
9914
+ };
9915
+ for (; ; ) {
9916
+ const connectedAt = Date.now();
9917
+ const window = await streamOneWindow();
9918
+ if (window.kind === "terminal") {
9919
+ return window.status;
9920
+ }
9921
+ const terminal = await fetchTerminalStatus();
9922
+ if (terminal) {
9923
+ return terminal;
9924
+ }
9925
+ assertPlayWaitNotTimedOut({ ...input2, lastPhase });
9926
+ if (window.sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
9927
+ reconnectAttempt = 0;
9928
+ }
9929
+ const delayMs = streamReconnectDelayMs(reconnectAttempt);
9930
+ reconnectAttempt += 1;
9931
+ const reason = window.kind === "ended" ? window.reason : "wait timeout raced the stream";
9932
+ if (!input2.jsonOutput) {
9933
+ input2.progress.writeLine(
9934
+ `[play watch] stream ended without a terminal status; reconnecting to run ${input2.workflowId} (${reason})`
9935
+ );
9936
+ }
9937
+ recordCliTrace({
9938
+ phase: "cli.play_tail_stream_reconnect",
9939
+ ms: Date.now() - input2.startedAt,
9940
+ ok: true,
9941
+ playName: input2.playName,
9942
+ workflowId: input2.workflowId,
9943
+ attempt: reconnectAttempt,
9944
+ delayMs,
9945
+ reason
9946
+ });
9947
+ const remainingBeforeSleep = remainingWaitMs();
9948
+ await sleep4(
9949
+ remainingBeforeSleep === null ? delayMs : Math.min(delayMs, Math.max(1, remainingBeforeSleep))
9950
+ );
9951
+ }
8887
9952
  }
8888
9953
  async function startAndWaitForPlayCompletionByStream(input2) {
8889
9954
  for (let attempt = 0; attempt <= PLAY_START_TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
@@ -9828,6 +10893,10 @@ function buildRunPackageTextLines(packaged) {
9828
10893
  const lines = [
9829
10894
  `${status === "completed" ? "\u2713" : status === "failed" ? "\u2717" : "\u2022"} ${status} ${runId}`
9830
10895
  ];
10896
+ const runError = typeof run.error === "string" && run.error.trim() ? run.error.trim() : null;
10897
+ if (runError && (status === "failed" || status === "cancelled")) {
10898
+ lines.push(` error: ${runError.slice(0, 200)}`);
10899
+ }
9831
10900
  if (playName) {
9832
10901
  lines.push(` play: ${playName}`);
9833
10902
  }
@@ -10209,7 +11278,7 @@ function extractPlayValidationErrors(value) {
10209
11278
  if (value instanceof DeeplineError) {
10210
11279
  return extractPlayValidationErrors(value.details);
10211
11280
  }
10212
- if (!isRecord4(value)) {
11281
+ if (!isRecord5(value)) {
10213
11282
  return [];
10214
11283
  }
10215
11284
  const directErrors = stringArrayField(value, "errors");
@@ -10567,7 +11636,7 @@ function shouldUseLocalOnlyPlayCheck() {
10567
11636
  const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
10568
11637
  return value === "1" || value === "true" || value === "yes" || value === "on";
10569
11638
  }
10570
- function isRecord4(value) {
11639
+ function isRecord5(value) {
10571
11640
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
10572
11641
  }
10573
11642
  function stringValue2(value) {
@@ -10577,14 +11646,14 @@ function asArray(value) {
10577
11646
  return Array.isArray(value) ? value : [];
10578
11647
  }
10579
11648
  function extractionEntries2(value) {
10580
- if (Array.isArray(value)) return value.filter(isRecord4);
10581
- if (!isRecord4(value)) return [];
11649
+ if (Array.isArray(value)) return value.filter(isRecord5);
11650
+ if (!isRecord5(value)) return [];
10582
11651
  return Object.entries(value).map(
10583
- ([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
11652
+ ([name, entry]) => isRecord5(entry) ? { name, ...entry } : { name }
10584
11653
  );
10585
11654
  }
10586
11655
  function firstRawPath(entry) {
10587
- const details = isRecord4(entry.details) ? entry.details : {};
11656
+ const details = isRecord5(entry.details) ? entry.details : {};
10588
11657
  const paths = [
10589
11658
  ...asArray(details.rawToolOutputPaths),
10590
11659
  ...asArray(details.raw_tool_output_paths),
@@ -10602,12 +11671,12 @@ function checkHintRawPath(value) {
10602
11671
  function collectStaticPipelineToolIds(staticPipeline) {
10603
11672
  const seen = /* @__PURE__ */ new Set();
10604
11673
  const visitPipeline = (pipeline) => {
10605
- if (!isRecord4(pipeline)) return;
11674
+ if (!isRecord5(pipeline)) return;
10606
11675
  for (const step of [
10607
11676
  ...asArray(pipeline.stages),
10608
11677
  ...asArray(pipeline.substeps)
10609
11678
  ]) {
10610
- if (!isRecord4(step)) continue;
11679
+ if (!isRecord5(step)) continue;
10611
11680
  if (step.type === "tool") {
10612
11681
  const toolId = stringValue2(step.toolId) || stringValue2(step.tool);
10613
11682
  if (toolId) seen.add(toolId);
@@ -10621,9 +11690,9 @@ function collectStaticPipelineToolIds(staticPipeline) {
10621
11690
  return [...seen].sort();
10622
11691
  }
10623
11692
  function toolGetterHintFromMetadata(toolId, tool) {
10624
- const usageGuidance = isRecord4(tool.usageGuidance) ? tool.usageGuidance : {};
10625
- const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
10626
- const toolResponse = isRecord4(resultGuidance.toolResponse) ? resultGuidance.toolResponse : isRecord4(resultGuidance.tool_response) ? resultGuidance.tool_response : {};
11693
+ const usageGuidance = isRecord5(tool.usageGuidance) ? tool.usageGuidance : {};
11694
+ const resultGuidance = isRecord5(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord5(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
11695
+ const toolResponse = isRecord5(resultGuidance.toolResponse) ? resultGuidance.toolResponse : isRecord5(resultGuidance.tool_response) ? resultGuidance.tool_response : {};
10627
11696
  const lists = extractionEntries2(
10628
11697
  resultGuidance.extractedLists ?? resultGuidance.extracted_lists
10629
11698
  ).map((entry) => ({
@@ -11252,8 +12321,17 @@ async function handleRunTail(args) {
11252
12321
  }
11253
12322
  }
11254
12323
  const client = new DeeplineClient();
11255
- const status = await client.runs.tail(runId);
11256
- writePlayResult(status, argsWantJson(args));
12324
+ const jsonOutput = argsWantJson(args);
12325
+ const status = await client.runs.tail(runId, {
12326
+ // Human mode only: in --json mode emit nothing non-protocol.
12327
+ onReconnect: jsonOutput ? void 0 : ({ reason }) => {
12328
+ process.stderr.write(
12329
+ `[runs tail] stream ended without a terminal status; reconnecting to run ${runId} (${reason})
12330
+ `
12331
+ );
12332
+ }
12333
+ });
12334
+ writePlayResult(status, jsonOutput);
11257
12335
  return status.status === "failed" ? 1 : 0;
11258
12336
  }
11259
12337
  async function handleRunLogs(args) {
@@ -11278,21 +12356,28 @@ async function handleRunLogs(args) {
11278
12356
  }
11279
12357
  }
11280
12358
  const client = new DeeplineClient();
11281
- const status = await client.runs.get(runId, { full: true });
11282
- const logs = status.progress?.logs ?? [];
11283
12359
  if (outPath) {
12360
+ const result2 = await client.runs.logs(runId, { all: true });
12361
+ const logs = result2.entries;
11284
12362
  writeFileSync7(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
11285
12363
  printCommandEnvelope(
11286
12364
  {
11287
- runId: status.runId,
12365
+ runId: result2.runId,
11288
12366
  log_path: outPath,
11289
12367
  lineCount: logs.length,
12368
+ totalCount: result2.totalCount,
12369
+ ...result2.logsTruncated ? { logsTruncated: true } : {},
11290
12370
  local: { log_path: outPath },
11291
12371
  render: {
11292
12372
  sections: [
11293
12373
  {
11294
12374
  title: "run logs",
11295
- lines: [`Wrote ${logs.length} log lines to ${outPath}`]
12375
+ lines: [
12376
+ `Wrote ${logs.length} log lines to ${outPath}`,
12377
+ ...result2.logsTruncated ? [
12378
+ `Run crossed the log retention cap: ${result2.totalCount} lines were emitted, stored bodies end at the truncation marker.`
12379
+ ] : []
12380
+ ]
11296
12381
  }
11297
12382
  ]
11298
12383
  }
@@ -11301,25 +12386,26 @@ async function handleRunLogs(args) {
11301
12386
  );
11302
12387
  return 0;
11303
12388
  }
11304
- const entries = logs.slice(Math.max(0, logs.length - limit));
12389
+ const result = await client.runs.logs(runId, { limit });
11305
12390
  printCommandEnvelope(
11306
12391
  {
11307
- runId: status.runId,
11308
- totalCount: logs.length,
11309
- returnedCount: entries.length,
11310
- firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
11311
- lastSequence: logs.length === 0 ? null : logs.length,
11312
- truncated: logs.length > entries.length,
11313
- hasMore: logs.length > entries.length,
11314
- entries,
12392
+ runId: result.runId,
12393
+ totalCount: result.totalCount,
12394
+ returnedCount: result.returnedCount,
12395
+ firstSequence: result.firstSequence,
12396
+ lastSequence: result.lastSequence,
12397
+ truncated: result.truncated,
12398
+ hasMore: result.hasMore,
12399
+ ...result.logsTruncated ? { logsTruncated: true } : {},
12400
+ entries: result.entries,
11315
12401
  next: {
11316
- export: `deepline runs logs ${status.runId} --out run.log --json`
12402
+ export: `deepline runs logs ${result.runId} --out run.log --json`
11317
12403
  },
11318
- render: { sections: [{ title: "run logs", lines: entries }] }
12404
+ render: { sections: [{ title: "run logs", lines: result.entries }] }
11319
12405
  },
11320
12406
  {
11321
12407
  json: argsWantJson(args),
11322
- text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`
12408
+ text: `${result.entries.join("\n")}${result.entries.length > 0 ? "\n" : ""}`
11323
12409
  }
11324
12410
  );
11325
12411
  return 0;
@@ -11342,6 +12428,34 @@ async function handleRunStop(args) {
11342
12428
  }
11343
12429
  const client = new DeeplineClient();
11344
12430
  const result = await client.runs.stop(runId, { reason });
12431
+ const stopNotConfirmed = result.stopped === false || result.staleSchedulerState === true;
12432
+ if (stopNotConfirmed) {
12433
+ const detail = typeof result.error === "string" && result.error.trim() ? result.error.trim() : result.staleSchedulerState === true ? "scheduler state for the run is stale" : "the server did not confirm the stop";
12434
+ process.stderr.write(
12435
+ `Failed to stop run ${runId}: ${detail}. Retry 'deepline runs stop ${runId}' or inspect the run with 'deepline runs get ${runId} --full --json'.
12436
+ `
12437
+ );
12438
+ printCommandEnvelope(
12439
+ {
12440
+ ...result,
12441
+ render: {
12442
+ sections: [
12443
+ {
12444
+ title: "run stop",
12445
+ lines: [
12446
+ `\u2717 stop not confirmed for ${result.runId || runId}`,
12447
+ ` ${detail}`,
12448
+ ` retry: deepline runs stop ${runId}`,
12449
+ ` inspect: deepline runs get ${runId} --full --json`
12450
+ ]
12451
+ }
12452
+ ]
12453
+ }
12454
+ },
12455
+ { json: argsWantJson(args) }
12456
+ );
12457
+ return 1;
12458
+ }
11345
12459
  const lines = [
11346
12460
  `Stopped ${result.runId}`,
11347
12461
  ...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
@@ -14989,7 +16103,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
14989
16103
  }
14990
16104
  function extractionContractEntries(entries) {
14991
16105
  return entries.flatMap((entry) => {
14992
- if (!isRecord5(entry)) return [];
16106
+ if (!isRecord6(entry)) return [];
14993
16107
  const name = stringField(entry, "name");
14994
16108
  const expression = stringField(entry, "expression");
14995
16109
  return name && expression ? [{ name, expression }] : [];
@@ -14997,8 +16111,8 @@ function extractionContractEntries(entries) {
14997
16111
  }
14998
16112
  function printCompactToolContract(tool, requestedToolId) {
14999
16113
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15000
- const cost = isRecord5(contract.cost) ? contract.cost : {};
15001
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16114
+ const cost = isRecord6(contract.cost) ? contract.cost : {};
16115
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15002
16116
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15003
16117
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15004
16118
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
@@ -15015,7 +16129,7 @@ function printCompactToolContract(tool, requestedToolId) {
15015
16129
  console.log("");
15016
16130
  console.log("Inputs:");
15017
16131
  for (const field of inputFields) {
15018
- if (!isRecord5(field)) continue;
16132
+ if (!isRecord6(field)) continue;
15019
16133
  const name = stringField(field, "name");
15020
16134
  if (!name) continue;
15021
16135
  const required = field.required ? "*" : "";
@@ -15028,7 +16142,7 @@ function printCompactToolContract(tool, requestedToolId) {
15028
16142
  }
15029
16143
  console.log("");
15030
16144
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
15031
- const starterScript = isRecord5(contract.starterScript) ? contract.starterScript : {};
16145
+ const starterScript = isRecord6(contract.starterScript) ? contract.starterScript : {};
15032
16146
  const starterPath = stringField(starterScript, "path");
15033
16147
  if (starterPath) {
15034
16148
  console.log("");
@@ -15042,14 +16156,14 @@ function printCompactToolContract(tool, requestedToolId) {
15042
16156
  console.log("Getters:");
15043
16157
  if (listGetters.length) console.log("Lists:");
15044
16158
  for (const entry of listGetters) {
15045
- if (isRecord5(entry))
16159
+ if (isRecord6(entry))
15046
16160
  console.log(
15047
16161
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15048
16162
  );
15049
16163
  }
15050
16164
  if (valueGetters.length) console.log("Values:");
15051
16165
  for (const entry of valueGetters) {
15052
- if (isRecord5(entry))
16166
+ if (isRecord6(entry))
15053
16167
  console.log(
15054
16168
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15055
16169
  );
@@ -15062,7 +16176,7 @@ function printCompactToolContract(tool, requestedToolId) {
15062
16176
  }
15063
16177
  function printToolPricingOnly(tool, requestedToolId, options = {}) {
15064
16178
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15065
- const cost = isRecord5(contract.cost) ? contract.cost : {};
16179
+ const cost = isRecord6(contract.cost) ? contract.cost : {};
15066
16180
  const pricingModel = stringField(cost, "pricingModel") || "unknown";
15067
16181
  const billingMode = stringField(cost, "billingMode") || "unknown";
15068
16182
  const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
@@ -15082,7 +16196,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
15082
16196
  }
15083
16197
  console.log("Inputs:");
15084
16198
  for (const field of inputFields) {
15085
- if (!isRecord5(field)) continue;
16199
+ if (!isRecord6(field)) continue;
15086
16200
  const name = stringField(field, "name");
15087
16201
  if (!name) continue;
15088
16202
  const required = field.required ? "*" : "";
@@ -15108,10 +16222,10 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
15108
16222
  ` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
15109
16223
  );
15110
16224
  console.log("});");
15111
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16225
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15112
16226
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15113
16227
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15114
- const firstGetter = [...valueGetters, ...listGetters].find(isRecord5);
16228
+ const firstGetter = [...valueGetters, ...listGetters].find(isRecord6);
15115
16229
  if (firstGetter) {
15116
16230
  const name = stringField(firstGetter, "name") || "value";
15117
16231
  const expression = stringField(firstGetter, "expression");
@@ -15128,7 +16242,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
15128
16242
  }
15129
16243
  function printToolGettersOnly(tool, requestedToolId) {
15130
16244
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15131
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16245
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15132
16246
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15133
16247
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15134
16248
  console.log(`Getters: ${contract.toolId}`);
@@ -15141,7 +16255,7 @@ function printToolGettersOnly(tool, requestedToolId) {
15141
16255
  if (listGetters.length) {
15142
16256
  console.log("Lists:");
15143
16257
  for (const entry of listGetters) {
15144
- if (isRecord5(entry))
16258
+ if (isRecord6(entry))
15145
16259
  console.log(
15146
16260
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15147
16261
  );
@@ -15150,7 +16264,7 @@ function printToolGettersOnly(tool, requestedToolId) {
15150
16264
  if (valueGetters.length) {
15151
16265
  console.log("Values:");
15152
16266
  for (const entry of valueGetters) {
15153
- if (isRecord5(entry))
16267
+ if (isRecord6(entry))
15154
16268
  console.log(
15155
16269
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15156
16270
  );
@@ -15176,7 +16290,7 @@ function sampleValueForField(field) {
15176
16290
  function samplePayloadForInputFields(fields) {
15177
16291
  return Object.fromEntries(
15178
16292
  fields.slice(0, 4).flatMap((field) => {
15179
- if (!isRecord5(field)) return [];
16293
+ if (!isRecord6(field)) return [];
15180
16294
  const name = stringField(field, "name");
15181
16295
  if (!name) return [];
15182
16296
  return [[name, sampleValueForField(field)]];
@@ -15290,12 +16404,12 @@ function formatListedToolCost(tool) {
15290
16404
  }
15291
16405
  function toolInputFieldsForDisplay(inputSchema) {
15292
16406
  if (Array.isArray(inputSchema.fields))
15293
- return inputSchema.fields.filter(isRecord5);
15294
- const jsonSchema = isRecord5(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
15295
- const properties = isRecord5(jsonSchema.properties) ? jsonSchema.properties : {};
16407
+ return inputSchema.fields.filter(isRecord6);
16408
+ const jsonSchema = isRecord6(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
16409
+ const properties = isRecord6(jsonSchema.properties) ? jsonSchema.properties : {};
15296
16410
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
15297
16411
  return Object.entries(properties).map(([name, value]) => {
15298
- const property = isRecord5(value) ? value : {};
16412
+ const property = isRecord6(value) ? value : {};
15299
16413
  return {
15300
16414
  name,
15301
16415
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -15324,15 +16438,15 @@ function printJsonPreview(label, payload) {
15324
16438
  }
15325
16439
  function samplePayload(samples, key) {
15326
16440
  const entry = samples[key];
15327
- if (!isRecord5(entry)) return void 0;
16441
+ if (!isRecord6(entry)) return void 0;
15328
16442
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
15329
16443
  }
15330
16444
  function commandEnvelopeFromRawResponse(rawResponse) {
15331
- return isRecord5(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
16445
+ return isRecord6(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
15332
16446
  }
15333
16447
  function listExtractorPathsFromUsageGuidance(tool) {
15334
16448
  const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
15335
- const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord5(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
16449
+ const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord6(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
15336
16450
  return extractedLists.flatMap((entry) => {
15337
16451
  const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
15338
16452
  if (!Array.isArray(paths)) return [];
@@ -15348,7 +16462,7 @@ function formatDecimal(value) {
15348
16462
  function formatUsd(value) {
15349
16463
  return `$${formatDecimal(value)}`;
15350
16464
  }
15351
- function isRecord5(value) {
16465
+ function isRecord6(value) {
15352
16466
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
15353
16467
  }
15354
16468
  function stringField(source, ...keys) {
@@ -15375,7 +16489,7 @@ function arrayField(source, ...keys) {
15375
16489
  function recordField(source, ...keys) {
15376
16490
  for (const key of keys) {
15377
16491
  const value = source[key];
15378
- if (isRecord5(value)) return value;
16492
+ if (isRecord6(value)) return value;
15379
16493
  }
15380
16494
  return {};
15381
16495
  }
@@ -15441,7 +16555,7 @@ function parseJsonObjectArgument(raw, flagName) {
15441
16555
  }
15442
16556
  throw invalidJsonError(flagName, message);
15443
16557
  }
15444
- if (!isRecord5(parsed)) {
16558
+ if (!isRecord6(parsed)) {
15445
16559
  throw invalidJsonError(flagName, "expected an object.");
15446
16560
  }
15447
16561
  return parsed;
@@ -15564,7 +16678,7 @@ function buildToolExecuteBaseEnvelope(input2) {
15564
16678
  kind: summaryEntries.length > 0 ? "object" : "raw",
15565
16679
  summary: input2.summary
15566
16680
  };
15567
- const envelopeHasCanonicalOutput = isRecord5(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
16681
+ const envelopeHasCanonicalOutput = isRecord6(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
15568
16682
  const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
15569
16683
  const actions = input2.listConversion ? [
15570
16684
  {
@@ -15673,7 +16787,7 @@ async function executeTool(args) {
15673
16787
  {
15674
16788
  ...baseEnvelope,
15675
16789
  local: {
15676
- ...isRecord5(baseEnvelope.local) ? baseEnvelope.local : {},
16790
+ ...isRecord6(baseEnvelope.local) ? baseEnvelope.local : {},
15677
16791
  payload_file: jsonPath
15678
16792
  }
15679
16793
  },