deepline 0.1.90 → 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.
package/dist/cli/index.js CHANGED
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
229
229
 
230
230
  // src/release.ts
231
231
  var SDK_RELEASE = {
232
- version: "0.1.90",
232
+ version: "0.1.93",
233
233
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
234
234
  supportPolicy: {
235
- latest: "0.1.90",
235
+ latest: "0.1.93",
236
236
  minimumSupported: "0.1.53",
237
237
  deprecatedBelow: "0.1.53"
238
238
  }
@@ -587,6 +587,807 @@ function sleep(ms) {
587
587
  return new Promise((resolve14) => setTimeout(resolve14, ms));
588
588
  }
589
589
 
590
+ // src/stream-reconnect.ts
591
+ var STREAM_RECONNECT_BASE_DELAY_MS = 500;
592
+ var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
593
+ var STREAM_HEALTHY_CONNECTION_MS = 3e4;
594
+ function streamReconnectDelayMs(attempt) {
595
+ const cappedExponentialMs = Math.min(
596
+ STREAM_RECONNECT_MAX_DELAY_MS,
597
+ STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt)
598
+ );
599
+ return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
600
+ }
601
+ function isTransientPlayStreamError(error) {
602
+ if (error instanceof DeeplineError && typeof error.statusCode === "number") {
603
+ return error.statusCode >= 500 && error.statusCode < 600;
604
+ }
605
+ const text = error instanceof Error ? error.message : String(error);
606
+ return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
607
+ text
608
+ );
609
+ }
610
+
611
+ // ../shared_libs/play-runtime/live-events.ts
612
+ function resolveTimingWindow(input2) {
613
+ const startedAt = typeof input2.startedAt === "number" && Number.isFinite(input2.startedAt) ? input2.startedAt : null;
614
+ const completedAt = typeof input2.completedAt === "number" && Number.isFinite(input2.completedAt) ? input2.completedAt : null;
615
+ const updatedAt = typeof input2.updatedAt === "number" && Number.isFinite(input2.updatedAt) ? input2.updatedAt : null;
616
+ return {
617
+ startedAt,
618
+ completedAt,
619
+ updatedAt
620
+ };
621
+ }
622
+
623
+ // ../shared_libs/play-runtime/run-ledger.ts
624
+ var LOG_TAIL_LIMIT = 100;
625
+ function isRecord(value) {
626
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
627
+ }
628
+ function finiteNumber(value) {
629
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
630
+ }
631
+ function optionalFiniteNumber(value) {
632
+ const normalized = finiteNumber(value);
633
+ return normalized === null ? void 0 : normalized;
634
+ }
635
+ function optionalString(value) {
636
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
637
+ }
638
+ function optionalNullableString(value) {
639
+ if (value === null) return null;
640
+ return optionalString(value);
641
+ }
642
+ function normalizePlayRunLedgerStatus(value) {
643
+ const normalized = String(value ?? "").trim().toLowerCase();
644
+ switch (normalized) {
645
+ case "queued":
646
+ case "pending":
647
+ return "queued";
648
+ case "running":
649
+ case "started":
650
+ return "running";
651
+ case "waiting":
652
+ return "waiting";
653
+ case "completed":
654
+ case "complete":
655
+ case "succeeded":
656
+ return "completed";
657
+ case "failed":
658
+ case "error":
659
+ return "failed";
660
+ case "cancelled":
661
+ case "canceled":
662
+ return "cancelled";
663
+ case "terminated":
664
+ return "terminated";
665
+ case "timed_out":
666
+ case "timeout":
667
+ return "timed_out";
668
+ default:
669
+ return "unknown";
670
+ }
671
+ }
672
+ function createEmptyPlayRunLedgerSnapshot(input2) {
673
+ const status = normalizePlayRunLedgerStatus(input2.status ?? "unknown");
674
+ const startedAt = finiteNumber(input2.startedAt) ?? null;
675
+ const finishedAt = finiteNumber(input2.finishedAt) ?? null;
676
+ return {
677
+ runId: input2.runId,
678
+ playName: input2.playName ?? null,
679
+ status,
680
+ error: optionalNullableString(input2.error) ?? null,
681
+ createdAt: finiteNumber(input2.createdAt) ?? null,
682
+ startedAt,
683
+ updatedAt: finiteNumber(input2.updatedAt) ?? finishedAt ?? startedAt ?? finiteNumber(input2.createdAt) ?? null,
684
+ finishedAt,
685
+ durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : null,
686
+ orderedStepIds: [],
687
+ stepsById: {},
688
+ logTail: [],
689
+ totalLogCount: 0,
690
+ logsTruncated: false,
691
+ activeStepId: null,
692
+ activeArtifactTableNamespace: null,
693
+ resultTableNamespace: null
694
+ };
695
+ }
696
+ function normalizePlayRunLedgerSnapshot(value, fallback) {
697
+ if (!isRecord(value)) {
698
+ return createEmptyPlayRunLedgerSnapshot(fallback);
699
+ }
700
+ const orderedStepIds = Array.isArray(value.orderedStepIds) ? value.orderedStepIds.filter(
701
+ (entry) => typeof entry === "string" && Boolean(entry.trim())
702
+ ) : [];
703
+ const rawSteps = isRecord(value.stepsById) ? value.stepsById : {};
704
+ const stepsById = {};
705
+ for (const [stepId, rawStep] of Object.entries(rawSteps)) {
706
+ if (!stepId.trim() || !isRecord(rawStep)) continue;
707
+ const rawStatus = normalizeStepStatus(rawStep.status);
708
+ if (!rawStatus) continue;
709
+ const completedAt = finiteNumber(rawStep.completedAt);
710
+ const status = rawStatus === "running" && completedAt !== null ? "completed" : rawStatus;
711
+ const rawProgress = isRecord(rawStep.progress) ? rawStep.progress : null;
712
+ stepsById[stepId] = {
713
+ stepId,
714
+ label: optionalString(rawStep.label),
715
+ kind: optionalString(rawStep.kind),
716
+ status,
717
+ artifactTableNamespace: optionalNullableString(
718
+ rawStep.artifactTableNamespace
719
+ ),
720
+ startedAt: finiteNumber(rawStep.startedAt),
721
+ completedAt,
722
+ updatedAt: finiteNumber(rawStep.updatedAt),
723
+ progress: rawProgress ? normalizeStepProgress(rawProgress) : null
724
+ };
725
+ }
726
+ const createdAt = finiteNumber(value.createdAt) ?? fallback.createdAt ?? null;
727
+ const startedAt = finiteNumber(value.startedAt) ?? fallback.startedAt ?? null;
728
+ const finishedAt = finiteNumber(value.finishedAt) ?? fallback.finishedAt ?? null;
729
+ const updatedAt = finiteNumber(value.updatedAt) ?? fallback.updatedAt ?? finishedAt ?? startedAt ?? createdAt ?? null;
730
+ const error = Object.prototype.hasOwnProperty.call(value, "error") ? optionalNullableString(value.error) ?? null : fallback.error ?? null;
731
+ const rawTail = Array.isArray(value.logTail) ? value.logTail : value.logs;
732
+ const logTail = Array.isArray(rawTail) ? rawTail.filter((line) => typeof line === "string") : [];
733
+ return {
734
+ runId: optionalString(value.runId) ?? fallback.runId,
735
+ playName: optionalNullableString(value.playName) ?? fallback.playName ?? null,
736
+ status: normalizePlayRunLedgerStatus(value.status ?? fallback.status),
737
+ error,
738
+ createdAt,
739
+ startedAt,
740
+ updatedAt,
741
+ finishedAt,
742
+ durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : finiteNumber(value.durationMs),
743
+ orderedStepIds: orderedStepIds.filter((stepId) => stepsById[stepId]),
744
+ stepsById,
745
+ logTail: logTail.slice(-LOG_TAIL_LIMIT),
746
+ // Snapshots persisted before totalLogCount existed only know the retained
747
+ // tail, so the best lower bound for the cumulative count is the tail size.
748
+ totalLogCount: Math.max(
749
+ finiteNumber(value.totalLogCount) ?? logTail.length,
750
+ logTail.length
751
+ ),
752
+ logsTruncated: value.logsTruncated === true,
753
+ activeStepId: optionalNullableString(value.activeStepId),
754
+ activeArtifactTableNamespace: optionalNullableString(
755
+ value.activeArtifactTableNamespace
756
+ ),
757
+ resultTableNamespace: optionalNullableString(value.resultTableNamespace),
758
+ resultSummary: value.resultSummary,
759
+ result: value.result
760
+ };
761
+ }
762
+ function normalizeStepStatus(value) {
763
+ const normalized = String(value ?? "").trim().toLowerCase();
764
+ if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "skipped") {
765
+ return normalized;
766
+ }
767
+ return null;
768
+ }
769
+ function normalizeStepProgress(value) {
770
+ return {
771
+ ...optionalFiniteNumber(value.completed) !== void 0 ? { completed: optionalFiniteNumber(value.completed) } : {},
772
+ ...optionalFiniteNumber(value.total) !== void 0 ? { total: optionalFiniteNumber(value.total) } : {},
773
+ ...optionalFiniteNumber(value.failed) !== void 0 ? { failed: optionalFiniteNumber(value.failed) } : {},
774
+ ...optionalString(value.message) ? { message: optionalString(value.message) } : {},
775
+ ...optionalNullableString(value.artifactTableNamespace) !== void 0 ? {
776
+ artifactTableNamespace: optionalNullableString(
777
+ value.artifactTableNamespace
778
+ )
779
+ } : {},
780
+ ...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
781
+ };
782
+ }
783
+
784
+ // ../shared_libs/play-runtime/run-snapshot-stream.ts
785
+ function normalizePlayRunLiveStatus(value) {
786
+ const normalized = String(value ?? "").trim().toLowerCase();
787
+ switch (normalized) {
788
+ case "queued":
789
+ case "pending":
790
+ return "running";
791
+ case "running":
792
+ case "started":
793
+ return "running";
794
+ case "completed":
795
+ case "complete":
796
+ case "succeeded":
797
+ return "completed";
798
+ case "failed":
799
+ case "error":
800
+ return "failed";
801
+ case "cancelled":
802
+ case "canceled":
803
+ return "cancelled";
804
+ case "terminated":
805
+ return "terminated";
806
+ case "timed_out":
807
+ case "timeout":
808
+ return "timed_out";
809
+ default:
810
+ return "unknown";
811
+ }
812
+ }
813
+ function isTerminalPlayRunLiveStatus(status) {
814
+ return status === "completed" || status === "failed" || status === "cancelled" || status === "terminated" || status === "timed_out";
815
+ }
816
+ function buildSnapshotFromLedger(snapshot) {
817
+ const nodeStates = snapshot.orderedStepIds.map((stepId) => snapshot.stepsById[stepId]).filter((step) => Boolean(step)).map((step) => ({
818
+ nodeId: step.stepId,
819
+ status: step.status,
820
+ artifactTableNamespace: step.artifactTableNamespace ?? null,
821
+ progress: step.progress ? {
822
+ completed: step.progress.completed,
823
+ total: step.progress.total,
824
+ failed: step.progress.failed,
825
+ message: step.progress.message,
826
+ artifactTableNamespace: step.progress.artifactTableNamespace ?? step.artifactTableNamespace ?? null,
827
+ startedAt: step.startedAt ?? null,
828
+ completedAt: step.completedAt ?? null,
829
+ updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null
830
+ } : null,
831
+ startedAt: step.startedAt ?? null,
832
+ completedAt: step.completedAt ?? null,
833
+ updatedAt: step.updatedAt ?? null
834
+ }));
835
+ return {
836
+ runId: snapshot.runId,
837
+ status: normalizePlayRunLiveStatus(snapshot.status),
838
+ updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
839
+ logs: snapshot.logTail,
840
+ totalLogCount: snapshot.totalLogCount,
841
+ ...snapshot.logsTruncated ? { logsTruncated: true } : {},
842
+ activeArtifactTableNamespace: snapshot.activeArtifactTableNamespace ?? null,
843
+ resultTableNamespace: snapshot.resultTableNamespace ?? null,
844
+ nodeStates,
845
+ activeNodeId: snapshot.activeStepId ?? null
846
+ };
847
+ }
848
+ function buildPlayRunStatusSnapshot(input2) {
849
+ const ledgerSnapshot = normalizePlayRunLedgerSnapshot(input2.run.runSnapshot, {
850
+ runId: input2.run.workflowId,
851
+ playName: input2.run.name ?? null,
852
+ status: input2.run.status,
853
+ createdAt: input2.run.createdAt ?? null,
854
+ startedAt: input2.run.startedAt ?? null,
855
+ updatedAt: input2.run.updatedAt ?? null,
856
+ finishedAt: input2.run.finishedAt ?? null
857
+ });
858
+ return buildSnapshotFromLedger(ledgerSnapshot);
859
+ }
860
+ function makeRunStreamEvent(input2) {
861
+ return {
862
+ ...input2,
863
+ scope: "play",
864
+ at: input2.at ?? (/* @__PURE__ */ new Date()).toISOString()
865
+ };
866
+ }
867
+ var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
868
+ runSignature: "",
869
+ snapshotSignature: "",
870
+ stepStatusSignature: "",
871
+ stepProgressSignature: "",
872
+ lastLogSeq: 0
873
+ };
874
+ function getSnapshotCursor(snapshot) {
875
+ return String(snapshot.updatedAt ?? Date.now());
876
+ }
877
+ function getRunSignature(snapshot) {
878
+ return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
879
+ }
880
+ function getStepStatusSignature(snapshot) {
881
+ return snapshot.nodeStates.map(
882
+ (state) => [
883
+ state.nodeId,
884
+ state.status,
885
+ state.artifactTableNamespace ?? "",
886
+ state.startedAt ?? "",
887
+ state.completedAt ?? "",
888
+ state.progress?.startedAt ?? "",
889
+ state.progress?.completedAt ?? "",
890
+ state.updatedAt ?? "",
891
+ state.progress?.updatedAt ?? ""
892
+ ].join(":")
893
+ ).join("|");
894
+ }
895
+ function getStepProgressSignature(snapshot) {
896
+ return snapshot.nodeStates.map(
897
+ (state) => [
898
+ state.nodeId,
899
+ state.progress?.completed ?? "",
900
+ state.progress?.total ?? "",
901
+ state.progress?.failed ?? "",
902
+ state.progress?.artifactTableNamespace ?? "",
903
+ state.progress?.startedAt ?? "",
904
+ state.progress?.completedAt ?? "",
905
+ state.progress?.updatedAt ?? "",
906
+ state.progress?.message ?? ""
907
+ ].join(":")
908
+ ).join("|");
909
+ }
910
+ function getSnapshotSignature(snapshot) {
911
+ return JSON.stringify(snapshot);
912
+ }
913
+ function resolvePlayRunLogGap(snapshot, lastLogSeq) {
914
+ if (snapshot.totalLogCount <= lastLogSeq) {
915
+ return null;
916
+ }
917
+ const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
918
+ if (lastLogSeq + 1 >= tailFirstSeq) {
919
+ return null;
920
+ }
921
+ return {
922
+ missingCount: tailFirstSeq - 1 - lastLogSeq,
923
+ tailFirstSeq
924
+ };
925
+ }
926
+ function diffLogLines(input2) {
927
+ const { logs, totalLogCount } = input2.snapshot;
928
+ if (totalLogCount <= input2.lastLogSeq) {
929
+ return { lines: [], lastLogSeq: input2.lastLogSeq, firstSeq: null };
930
+ }
931
+ const tailFirstSeq = totalLogCount - logs.length + 1;
932
+ if (input2.lastLogSeq + 1 >= tailFirstSeq) {
933
+ return {
934
+ lines: logs.slice(input2.lastLogSeq + 1 - tailFirstSeq),
935
+ lastLogSeq: totalLogCount,
936
+ firstSeq: input2.lastLogSeq + 1
937
+ };
938
+ }
939
+ const missingCount = tailFirstSeq - 1 - input2.lastLogSeq;
940
+ return {
941
+ lines: [
942
+ `[stream] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
943
+ ...logs
944
+ ],
945
+ lastLogSeq: totalLogCount,
946
+ firstSeq: null
947
+ };
948
+ }
949
+ function diffPlayRunStreamEvents(input2) {
950
+ const { snapshot, streamId, previous } = input2;
951
+ const cursor = getSnapshotCursor(snapshot);
952
+ const logDiff = diffLogLines({
953
+ snapshot,
954
+ lastLogSeq: previous.lastLogSeq
955
+ });
956
+ const next = {
957
+ runSignature: getRunSignature(snapshot),
958
+ stepStatusSignature: getStepStatusSignature(snapshot),
959
+ stepProgressSignature: getStepProgressSignature(snapshot),
960
+ snapshotSignature: getSnapshotSignature(snapshot),
961
+ lastLogSeq: logDiff.lastLogSeq
962
+ };
963
+ const events = [];
964
+ if (next.stepStatusSignature !== previous.stepStatusSignature) {
965
+ for (const state of snapshot.nodeStates) {
966
+ if (state.status === "idle") {
967
+ continue;
968
+ }
969
+ const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
970
+ const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
971
+ events.push(
972
+ makeRunStreamEvent({
973
+ cursor,
974
+ streamId,
975
+ type: "play.step.status",
976
+ payload: {
977
+ runId: snapshot.runId,
978
+ stepId: state.nodeId,
979
+ status: state.status,
980
+ artifactTableNamespace: state.artifactTableNamespace ?? null,
981
+ ...resolveTimingWindow({
982
+ startedAt: persistedStartedAt,
983
+ completedAt: persistedCompletedAt,
984
+ updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
985
+ })
986
+ }
987
+ })
988
+ );
989
+ }
990
+ }
991
+ if (next.stepProgressSignature !== previous.stepProgressSignature) {
992
+ for (const state of snapshot.nodeStates) {
993
+ if (!state.progress) {
994
+ continue;
995
+ }
996
+ events.push(
997
+ makeRunStreamEvent({
998
+ cursor: String(
999
+ state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
1000
+ ),
1001
+ streamId,
1002
+ type: "play.step.progress",
1003
+ payload: {
1004
+ runId: snapshot.runId,
1005
+ stepId: state.nodeId,
1006
+ completed: state.progress.completed,
1007
+ total: state.progress.total,
1008
+ failed: state.progress.failed,
1009
+ message: state.progress.message,
1010
+ artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
1011
+ ...resolveTimingWindow({
1012
+ startedAt: state.startedAt ?? state.progress.startedAt ?? null,
1013
+ completedAt: state.completedAt ?? state.progress.completedAt ?? null,
1014
+ updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
1015
+ })
1016
+ }
1017
+ })
1018
+ );
1019
+ }
1020
+ }
1021
+ if (logDiff.lines.length > 0) {
1022
+ events.push(
1023
+ makeRunStreamEvent({
1024
+ cursor,
1025
+ streamId,
1026
+ type: "play.run.log",
1027
+ payload: {
1028
+ runId: snapshot.runId,
1029
+ lines: logDiff.lines,
1030
+ source: "worker",
1031
+ ...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
1032
+ totalLogCount: snapshot.totalLogCount
1033
+ }
1034
+ })
1035
+ );
1036
+ }
1037
+ if (next.snapshotSignature !== previous.snapshotSignature) {
1038
+ const enrichedNodeStates = snapshot.nodeStates.map((state) => {
1039
+ const timing = resolveTimingWindow({
1040
+ startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
1041
+ completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
1042
+ updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
1043
+ });
1044
+ return {
1045
+ ...state,
1046
+ ...timing,
1047
+ progress: state.progress ? {
1048
+ ...state.progress,
1049
+ ...resolveTimingWindow({
1050
+ startedAt: state.progress.startedAt ?? state.startedAt ?? null,
1051
+ completedAt: state.progress.completedAt ?? state.completedAt ?? null,
1052
+ updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
1053
+ })
1054
+ } : state.progress
1055
+ };
1056
+ });
1057
+ events.push(
1058
+ makeRunStreamEvent({
1059
+ cursor,
1060
+ streamId,
1061
+ type: "play.run.snapshot",
1062
+ payload: { ...snapshot, nodeStates: enrichedNodeStates }
1063
+ })
1064
+ );
1065
+ }
1066
+ if (next.runSignature !== previous.runSignature) {
1067
+ events.push(
1068
+ makeRunStreamEvent({
1069
+ cursor,
1070
+ streamId,
1071
+ type: "play.run.status",
1072
+ payload: {
1073
+ runId: snapshot.runId,
1074
+ status: snapshot.status,
1075
+ updatedAt: snapshot.updatedAt
1076
+ }
1077
+ })
1078
+ );
1079
+ }
1080
+ return { events, next };
1081
+ }
1082
+
1083
+ // src/runs/observe-transport.ts
1084
+ var RunObserveTransportUnavailableError = class extends Error {
1085
+ constructor(message, reason) {
1086
+ super(message);
1087
+ this.reason = reason;
1088
+ this.name = "RunObserveTransportUnavailableError";
1089
+ }
1090
+ reason;
1091
+ };
1092
+ var OBSERVE_BOOTSTRAP_TIMEOUT_MS = 1e4;
1093
+ var OBSERVE_RECONNECT_NOTICE_MS = 1e4;
1094
+ var OBSERVE_STALE_WARNING_MS = 12e4;
1095
+ var OBSERVE_WATCHDOG_TICK_MS = 5e3;
1096
+ var GRANT_REFRESH_MARGIN_MS = 5 * 6e4;
1097
+ var BACKFILL_PAGE_LIMIT = 1e3;
1098
+ var BACKFILL_MAX_PAGES = 30;
1099
+ var OBSERVER_SNAPSHOT_QUERY = "runObservers:getPlayRunSnapshotForObserver";
1100
+ var OBSERVER_LOG_PAGE_QUERY = "runObservers:getRunLogPageForObserver";
1101
+ function errorText(error) {
1102
+ return error instanceof Error ? error.message : String(error);
1103
+ }
1104
+ async function mintRunObserveGrant(http, runId) {
1105
+ let response;
1106
+ try {
1107
+ response = await http.post(
1108
+ `/api/v2/runs/${encodeURIComponent(runId)}/observe-grant`,
1109
+ {}
1110
+ );
1111
+ } catch (error) {
1112
+ if (error instanceof DeeplineError) {
1113
+ if (error.statusCode === 401 || error.statusCode === 403) {
1114
+ throw error;
1115
+ }
1116
+ throw new RunObserveTransportUnavailableError(
1117
+ `observe-grant endpoint unavailable (${error.statusCode ?? "network"}): ${error.message}`,
1118
+ "grant_endpoint_unavailable"
1119
+ );
1120
+ }
1121
+ throw new RunObserveTransportUnavailableError(
1122
+ `observe-grant request failed: ${errorText(error)}`,
1123
+ "grant_request_failed"
1124
+ );
1125
+ }
1126
+ const grant = response;
1127
+ if (!grant || typeof grant.convexUrl !== "string" || !grant.convexUrl.trim() || typeof grant.token !== "string" || !grant.token.trim() || typeof grant.expiresAt !== "number") {
1128
+ throw new RunObserveTransportUnavailableError(
1129
+ "observe-grant endpoint returned an invalid grant payload.",
1130
+ "grant_payload_invalid"
1131
+ );
1132
+ }
1133
+ return grant;
1134
+ }
1135
+ async function backfillLogGap(input2) {
1136
+ const lines = [];
1137
+ let cursor = input2.lastLogSeq;
1138
+ for (let page = 0; page < BACKFILL_MAX_PAGES; page += 1) {
1139
+ if (cursor >= input2.tailFirstSeq - 1) {
1140
+ break;
1141
+ }
1142
+ let logPage;
1143
+ try {
1144
+ logPage = await input2.queryLogPage(
1145
+ cursor,
1146
+ Math.min(BACKFILL_PAGE_LIMIT, input2.tailFirstSeq - 1 - cursor)
1147
+ );
1148
+ } catch {
1149
+ return null;
1150
+ }
1151
+ const entries = (logPage?.entries ?? []).filter(
1152
+ (entry) => entry.seq > cursor && entry.seq < input2.tailFirstSeq
1153
+ );
1154
+ if (entries.length === 0) {
1155
+ break;
1156
+ }
1157
+ for (const entry of entries) {
1158
+ if (entry.seq !== cursor + 1) {
1159
+ return null;
1160
+ }
1161
+ lines.push(entry.line);
1162
+ cursor = entry.seq;
1163
+ }
1164
+ }
1165
+ if (cursor < input2.tailFirstSeq - 1) {
1166
+ return null;
1167
+ }
1168
+ return lines;
1169
+ }
1170
+ async function* observeRunEvents(options) {
1171
+ const { http, runId } = options;
1172
+ let grant = await mintRunObserveGrant(http, runId);
1173
+ let convexBrowser;
1174
+ let convexServer;
1175
+ try {
1176
+ convexBrowser = await import("convex/browser");
1177
+ convexServer = await import("convex/server");
1178
+ } catch (error) {
1179
+ throw new RunObserveTransportUnavailableError(
1180
+ `convex client module unavailable: ${errorText(error)}`,
1181
+ "convex_module_unavailable"
1182
+ );
1183
+ }
1184
+ let webSocketConstructor;
1185
+ if (typeof WebSocket === "undefined") {
1186
+ try {
1187
+ const wsModuleName = "ws";
1188
+ const ws = await import(wsModuleName);
1189
+ webSocketConstructor = ws.default;
1190
+ } catch (error) {
1191
+ throw new RunObserveTransportUnavailableError(
1192
+ `no WebSocket implementation available: ${errorText(error)}`,
1193
+ "websocket_unavailable"
1194
+ );
1195
+ }
1196
+ }
1197
+ const snapshotQuery = convexServer.makeFunctionReference(
1198
+ OBSERVER_SNAPSHOT_QUERY
1199
+ );
1200
+ const logPageQuery = convexServer.makeFunctionReference(
1201
+ OBSERVER_LOG_PAGE_QUERY
1202
+ );
1203
+ const client = new convexBrowser.ConvexClient(grant.convexUrl, {
1204
+ ...webSocketConstructor ? { webSocketConstructor } : {},
1205
+ unsavedChangesWarning: false
1206
+ });
1207
+ const queue = [];
1208
+ let wake = null;
1209
+ const push = (item) => {
1210
+ queue.push(item);
1211
+ wake?.();
1212
+ wake = null;
1213
+ };
1214
+ let lastForcedRefreshAt = 0;
1215
+ client.setAuth(async ({ forceRefreshToken }) => {
1216
+ const now = Date.now();
1217
+ if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
1218
+ return grant.token;
1219
+ }
1220
+ if (forceRefreshToken && now - lastForcedRefreshAt < 5e3) {
1221
+ push({
1222
+ kind: "error",
1223
+ error: new DeeplineError(
1224
+ `Run observe grant for ${runId} was rejected after a re-mint. The server and Convex deployment disagree on the grant issuer/JWKS.`,
1225
+ 401,
1226
+ "RUN_OBSERVE_GRANT_REJECTED"
1227
+ )
1228
+ });
1229
+ return null;
1230
+ }
1231
+ if (forceRefreshToken) {
1232
+ lastForcedRefreshAt = now;
1233
+ }
1234
+ try {
1235
+ grant = await mintRunObserveGrant(http, runId);
1236
+ return grant.token;
1237
+ } catch (error) {
1238
+ push({ kind: "error", error });
1239
+ return null;
1240
+ }
1241
+ });
1242
+ const unsubscribe = client.onUpdate(
1243
+ snapshotQuery,
1244
+ { workflowId: runId },
1245
+ (run) => push({ kind: "run", run: run ?? null }),
1246
+ (error) => push({ kind: "error", error })
1247
+ );
1248
+ let lastUpdateAt = Date.now();
1249
+ let lastStatusTerminal = false;
1250
+ let disconnectedSince = null;
1251
+ let warnedReconnecting = false;
1252
+ let warnedStale = false;
1253
+ const watchdog = setInterval(() => {
1254
+ const now = Date.now();
1255
+ try {
1256
+ const connectionState = client.connectionState();
1257
+ if (connectionState.isWebSocketConnected) {
1258
+ disconnectedSince = null;
1259
+ warnedReconnecting = false;
1260
+ } else {
1261
+ disconnectedSince ??= now;
1262
+ if (!warnedReconnecting && now - disconnectedSince >= OBSERVE_RECONNECT_NOTICE_MS) {
1263
+ warnedReconnecting = true;
1264
+ options.onNotice?.(
1265
+ `[observe] connection lost; reconnecting to live run ${runId}\u2026`
1266
+ );
1267
+ }
1268
+ }
1269
+ } catch {
1270
+ }
1271
+ if (!lastStatusTerminal && !warnedStale && now - lastUpdateAt >= OBSERVE_STALE_WARNING_MS) {
1272
+ warnedStale = true;
1273
+ options.onNotice?.(
1274
+ `[observe] no live updates for ${Math.round((now - lastUpdateAt) / 1e3)}s; run ${runId} may be stalled (status checks continue)`
1275
+ );
1276
+ }
1277
+ }, OBSERVE_WATCHDOG_TICK_MS);
1278
+ watchdog.unref?.();
1279
+ const abortListener = () => push({
1280
+ kind: "error",
1281
+ error: new DeeplineError(
1282
+ "Run observation aborted.",
1283
+ void 0,
1284
+ "ABORTED"
1285
+ )
1286
+ });
1287
+ options.signal?.addEventListener("abort", abortListener, { once: true });
1288
+ let diffState = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
1289
+ const streamId = ["observe", runId].join(":");
1290
+ let sawFirstSnapshot = false;
1291
+ try {
1292
+ for (; ; ) {
1293
+ if (queue.length === 0) {
1294
+ const waitForItem = new Promise((resolve14) => {
1295
+ wake = resolve14;
1296
+ });
1297
+ if (!sawFirstSnapshot) {
1298
+ const timedOut = await Promise.race([
1299
+ waitForItem.then(() => false),
1300
+ new Promise(
1301
+ (resolve14) => setTimeout(() => resolve14(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1302
+ )
1303
+ ]);
1304
+ if (timedOut && queue.length === 0) {
1305
+ throw new RunObserveTransportUnavailableError(
1306
+ `no snapshot from Convex at ${grant.convexUrl} within ${OBSERVE_BOOTSTRAP_TIMEOUT_MS}ms`,
1307
+ "convex_unreachable"
1308
+ );
1309
+ }
1310
+ } else {
1311
+ await waitForItem;
1312
+ }
1313
+ continue;
1314
+ }
1315
+ const item = queue.shift();
1316
+ if (item.kind === "error") {
1317
+ if (options.signal?.aborted) {
1318
+ return;
1319
+ }
1320
+ throw item.error;
1321
+ }
1322
+ sawFirstSnapshot = true;
1323
+ lastUpdateAt = Date.now();
1324
+ warnedStale = false;
1325
+ if (item.run === null) {
1326
+ throw new DeeplineError(
1327
+ `Run ${runId} was not found (or is not visible to this grant).`,
1328
+ 404,
1329
+ "RUN_NOT_FOUND"
1330
+ );
1331
+ }
1332
+ const snapshot = buildPlayRunStatusSnapshot({ run: item.run });
1333
+ lastStatusTerminal = isTerminalPlayRunLiveStatus(snapshot.status);
1334
+ const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
1335
+ if (gap && diffState.lastLogSeq > 0) {
1336
+ const backfilled = await backfillLogGap({
1337
+ queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
1338
+ workflowId: runId,
1339
+ afterSeq,
1340
+ limit
1341
+ }),
1342
+ lastLogSeq: diffState.lastLogSeq,
1343
+ tailFirstSeq: gap.tailFirstSeq
1344
+ });
1345
+ if (backfilled && backfilled.length > 0) {
1346
+ yield {
1347
+ cursor: String(snapshot.updatedAt ?? Date.now()),
1348
+ streamId,
1349
+ scope: "play",
1350
+ type: "play.run.log",
1351
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1352
+ payload: {
1353
+ runId: snapshot.runId,
1354
+ lines: backfilled,
1355
+ source: "worker",
1356
+ firstSeq: diffState.lastLogSeq + 1,
1357
+ totalLogCount: snapshot.totalLogCount
1358
+ }
1359
+ };
1360
+ diffState = { ...diffState, lastLogSeq: gap.tailFirstSeq - 1 };
1361
+ }
1362
+ }
1363
+ const { events, next } = diffPlayRunStreamEvents({
1364
+ streamId,
1365
+ snapshot,
1366
+ previous: diffState
1367
+ });
1368
+ diffState = next;
1369
+ const ordered = [
1370
+ ...events.filter((event) => event.type === "play.run.snapshot"),
1371
+ ...events.filter((event) => event.type !== "play.run.snapshot")
1372
+ ];
1373
+ for (const event of ordered) {
1374
+ yield event;
1375
+ }
1376
+ if (lastStatusTerminal) {
1377
+ return;
1378
+ }
1379
+ }
1380
+ } finally {
1381
+ clearInterval(watchdog);
1382
+ options.signal?.removeEventListener("abort", abortListener);
1383
+ try {
1384
+ unsubscribe();
1385
+ } catch {
1386
+ }
1387
+ await client.close().catch(() => void 0);
1388
+ }
1389
+ }
1390
+
590
1391
  // src/client.ts
591
1392
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
592
1393
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -605,7 +1406,8 @@ function isTransientCompileManifestError(error) {
605
1406
  message
606
1407
  );
607
1408
  }
608
- function isRecord(value) {
1409
+ var RUN_LOGS_PAGE_LIMIT = 1e3;
1410
+ function isRecord2(value) {
609
1411
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
610
1412
  }
611
1413
  function isPrebuiltPlayDescription(play) {
@@ -675,10 +1477,31 @@ function normalizeLiveStatus(value) {
675
1477
  }
676
1478
  return null;
677
1479
  }
1480
+ function appendPlayLiveLogLines(state, payload) {
1481
+ const lines = readStringArray(payload.lines);
1482
+ if (lines.length === 0) {
1483
+ return;
1484
+ }
1485
+ const firstSeq = typeof payload.firstSeq === "number" && Number.isFinite(payload.firstSeq) && payload.firstSeq >= 1 ? Math.trunc(payload.firstSeq) : null;
1486
+ if (firstSeq === null) {
1487
+ state.logs.push(...lines);
1488
+ const totalLogCount = typeof payload.totalLogCount === "number" && Number.isFinite(payload.totalLogCount) ? Math.trunc(payload.totalLogCount) : null;
1489
+ if (totalLogCount !== null) {
1490
+ state.lastLogSeq = Math.max(state.lastLogSeq, totalLogCount);
1491
+ }
1492
+ return;
1493
+ }
1494
+ const skip = Math.max(0, state.lastLogSeq + 1 - firstSeq);
1495
+ if (skip >= lines.length) {
1496
+ return;
1497
+ }
1498
+ state.logs.push(...lines.slice(skip));
1499
+ state.lastLogSeq = Math.max(state.lastLogSeq, firstSeq + lines.length - 1);
1500
+ }
678
1501
  function updatePlayLiveStatusState(state, event) {
679
1502
  const payload = getPlayLiveEventPayload(event);
680
1503
  if (event.type === "play.run.log") {
681
- state.logs.push(...readStringArray(payload.lines));
1504
+ appendPlayLiveLogLines(state, payload);
682
1505
  return null;
683
1506
  }
684
1507
  if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
@@ -686,12 +1509,14 @@ function updatePlayLiveStatusState(state, event) {
686
1509
  }
687
1510
  const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
688
1511
  const status = normalizeLiveStatus(payload.status) ?? (isPlayRunPackage(payload) ? normalizeLiveStatus(payload.run.status) : null) ?? state.status;
689
- const progressPayload = isRecord(payload.progress) ? payload.progress : {};
690
- const payloadLogs = readStringArray(payload.logs);
691
- const progressLogs = readStringArray(progressPayload.logs);
692
- const logs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
693
- if (logs.length > 0 || event.type === "play.run.snapshot" || event.type === "play.run.final_status" && !isPlayRunPackage(payload)) {
694
- state.logs = logs;
1512
+ const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
1513
+ if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
1514
+ const payloadLogs = readStringArray(payload.logs);
1515
+ const progressLogs = readStringArray(progressPayload.logs);
1516
+ const seedLogs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
1517
+ if (seedLogs.length > 0) {
1518
+ state.logs = seedLogs;
1519
+ }
695
1520
  }
696
1521
  if ("result" in payload) {
697
1522
  state.result = payload.result;
@@ -785,9 +1610,9 @@ var DeeplineClient = class {
785
1610
  return fields.length > 0 ? { fields } : schema;
786
1611
  }
787
1612
  schemaMetadata(schema, key) {
788
- if (!isRecord(schema)) return null;
1613
+ if (!isRecord2(schema)) return null;
789
1614
  const value = schema[key];
790
- return isRecord(value) ? value : null;
1615
+ return isRecord2(value) ? value : null;
791
1616
  }
792
1617
  playRunCommand(play, options) {
793
1618
  const target = play.reference || play.name;
@@ -834,7 +1659,7 @@ var DeeplineClient = class {
834
1659
  aliases,
835
1660
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
836
1661
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
837
- staticPipeline: isRecord(play.staticPipeline) ? play.staticPipeline : isRecord(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
1662
+ staticPipeline: isRecord2(play.staticPipeline) ? play.staticPipeline : isRecord2(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord2(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
838
1663
  ...csvInput ? { csvInput } : {},
839
1664
  ...rowOutputSchema ? { rowOutputSchema } : {},
840
1665
  runCommand: runCommand2,
@@ -1518,43 +2343,140 @@ var DeeplineClient = class {
1518
2343
  );
1519
2344
  return response.runs ?? [];
1520
2345
  }
1521
- /** Read the canonical run stream and return the latest run snapshot. */
1522
- async tailRun(runId, options) {
2346
+ /**
2347
+ * Observe one run's live events through the Convex Run Snapshot
2348
+ * subscription transport (ADR-0008). Yields the same `play.*` event
2349
+ * envelopes as {@link streamPlayRunEvents} and ends after the terminal
2350
+ * snapshot. Throws {@link RunObserveTransportUnavailableError} when this
2351
+ * server cannot serve the transport (older server, unconfigured grants, or
2352
+ * unreachable Convex) — callers fall back to the SSE stream with a notice.
2353
+ */
2354
+ observeRunEvents(runId, options) {
2355
+ return observeRunEvents({
2356
+ http: this.http,
2357
+ runId,
2358
+ signal: options?.signal,
2359
+ onNotice: options?.onNotice
2360
+ });
2361
+ }
2362
+ /**
2363
+ * Tail one run through the subscription transport until terminal, then
2364
+ * return one durable REST status read (the final Run Response Package).
2365
+ */
2366
+ async tailRunViaObserveTransport(runId, options) {
1523
2367
  const state = {
1524
2368
  runId,
1525
2369
  status: "running",
1526
2370
  logs: [],
2371
+ lastLogSeq: 0,
1527
2372
  latest: null
1528
2373
  };
1529
- let terminal = false;
1530
- for await (const event of this.streamPlayRunEvents(runId, {
1531
- mode: "cli",
1532
- signal: options?.signal
2374
+ for await (const event of this.observeRunEvents(runId, {
2375
+ signal: options?.signal,
2376
+ onNotice: options?.onNotice
1533
2377
  })) {
1534
2378
  const status = updatePlayLiveStatusState(state, event);
1535
- if (!status) {
2379
+ if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
1536
2380
  continue;
1537
2381
  }
1538
- terminal = TERMINAL_PLAY_STATUSES.has(status.status);
1539
- if (terminal) {
1540
- break;
1541
- }
1542
- }
1543
- if (terminal && state.latest) {
1544
- return await this.getRunStatus(state.latest.runId || runId).catch(
2382
+ return await this.getRunStatus(status.runId || runId).catch(
1545
2383
  () => state.latest ?? playRunStatusFromState(state)
1546
2384
  );
1547
2385
  }
1548
- if (state.latest) {
1549
- return state.latest;
2386
+ if (options?.signal?.aborted) {
2387
+ throw new DeeplineError("Run observation aborted.", void 0, "ABORTED");
2388
+ }
2389
+ const refreshed = await this.getRunStatus(runId);
2390
+ if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2391
+ return refreshed;
1550
2392
  }
1551
2393
  throw new DeeplineError(
1552
- `Run stream for ${runId} ended before the initial snapshot.`,
2394
+ `Run observation for ${runId} ended before a terminal status.`,
1553
2395
  void 0,
1554
- "PLAY_RUN_STREAM_EMPTY",
1555
- { runId }
2396
+ "PLAY_LIVE_STREAM_ENDED"
1556
2397
  );
1557
2398
  }
2399
+ /**
2400
+ * Read the canonical run stream until a terminal run status is observed.
2401
+ *
2402
+ * Tries the Convex Run Snapshot subscription transport first (ADR-0008);
2403
+ * when the server cannot serve it (grant endpoint missing/unconfigured or
2404
+ * Convex unreachable) it falls back — with one `onNotice` message — to the
2405
+ * support-window SSE stream below.
2406
+ *
2407
+ * Server stream windows are finite: they end cleanly at the function
2408
+ * ceiling even while the run keeps executing. A window that ends (cleanly
2409
+ * or via transient network error) without a terminal event triggers one
2410
+ * durable-status re-check followed by a backed-off reconnect, so long runs
2411
+ * tail to completion. Abort via `options.signal` to stop waiting.
2412
+ */
2413
+ async tailRun(runId, options) {
2414
+ try {
2415
+ return await this.tailRunViaObserveTransport(runId, options);
2416
+ } catch (error) {
2417
+ if (!(error instanceof RunObserveTransportUnavailableError)) {
2418
+ throw error;
2419
+ }
2420
+ options?.onNotice?.(
2421
+ `[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
2422
+ );
2423
+ }
2424
+ const state = {
2425
+ runId,
2426
+ status: "running",
2427
+ logs: [],
2428
+ lastLogSeq: 0,
2429
+ latest: null
2430
+ };
2431
+ let reconnectAttempt = 0;
2432
+ for (; ; ) {
2433
+ const connectedAt = Date.now();
2434
+ let sawEvent = false;
2435
+ let endedReason = "stream window ended before a terminal event";
2436
+ try {
2437
+ for await (const event of this.streamPlayRunEvents(runId, {
2438
+ mode: "cli",
2439
+ signal: options?.signal
2440
+ })) {
2441
+ sawEvent = true;
2442
+ const status = updatePlayLiveStatusState(state, event);
2443
+ if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
2444
+ continue;
2445
+ }
2446
+ return await this.getRunStatus(status.runId || runId).catch(
2447
+ () => state.latest ?? playRunStatusFromState(state)
2448
+ );
2449
+ }
2450
+ } catch (error) {
2451
+ if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
2452
+ throw error;
2453
+ }
2454
+ endedReason = error instanceof Error ? error.message : String(error);
2455
+ }
2456
+ let refreshed = null;
2457
+ try {
2458
+ refreshed = await this.getRunStatus(runId);
2459
+ } catch (error) {
2460
+ if (!isTransientPlayStreamError(error)) {
2461
+ throw error;
2462
+ }
2463
+ }
2464
+ if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2465
+ return refreshed;
2466
+ }
2467
+ if (sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
2468
+ reconnectAttempt = 0;
2469
+ }
2470
+ const delayMs = streamReconnectDelayMs(reconnectAttempt);
2471
+ reconnectAttempt += 1;
2472
+ options?.onReconnect?.({
2473
+ attempt: reconnectAttempt,
2474
+ delayMs,
2475
+ reason: endedReason
2476
+ });
2477
+ await sleep2(delayMs);
2478
+ }
2479
+ }
1558
2480
  /**
1559
2481
  * Fetch persisted logs for a run using the public runs resource model.
1560
2482
  *
@@ -1565,19 +2487,40 @@ var DeeplineClient = class {
1565
2487
  * ```
1566
2488
  */
1567
2489
  async getRunLogs(runId, options) {
1568
- const status = await this.getRunStatus(runId, { full: true });
1569
- const logs = status.progress?.logs ?? [];
1570
- const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
1571
- const entries = logs.slice(Math.max(0, logs.length - limit));
2490
+ const limit = options?.all ? Number.MAX_SAFE_INTEGER : typeof options?.limit === "number" && Number.isFinite(options.limit) && options.limit > 0 ? Math.trunc(options.limit) : 200;
2491
+ const fetchPage = (afterSeq2, pageLimit) => this.http.get(
2492
+ `/api/v2/runs/${encodeURIComponent(runId)}/logs?afterSeq=${afterSeq2}&limit=${pageLimit}`
2493
+ );
2494
+ const probe = await fetchPage(0, 1);
2495
+ const lastStoredSeq = probe.lastStoredSeq;
2496
+ let afterSeq = options?.all ? 0 : Math.max(0, lastStoredSeq - limit);
2497
+ const entries = [];
2498
+ while (entries.length < limit) {
2499
+ const page = await fetchPage(
2500
+ afterSeq,
2501
+ Math.min(RUN_LOGS_PAGE_LIMIT, limit - entries.length)
2502
+ );
2503
+ if (page.entries.length === 0) {
2504
+ break;
2505
+ }
2506
+ entries.push(...page.entries);
2507
+ afterSeq = page.entries[page.entries.length - 1].seq;
2508
+ if (!page.hasMore) {
2509
+ break;
2510
+ }
2511
+ }
2512
+ const firstSequence = entries.length > 0 ? entries[0].seq : null;
2513
+ const lastSequence = entries.length > 0 ? entries[entries.length - 1].seq : null;
1572
2514
  return {
1573
- runId: status.runId,
1574
- totalCount: logs.length,
2515
+ runId: probe.runId,
2516
+ totalCount: probe.totalLogCount,
1575
2517
  returnedCount: entries.length,
1576
- firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
1577
- lastSequence: logs.length === 0 ? null : logs.length,
1578
- truncated: logs.length > entries.length,
1579
- hasMore: logs.length > entries.length,
1580
- entries
2518
+ firstSequence,
2519
+ lastSequence,
2520
+ truncated: entries.length < probe.totalLogCount,
2521
+ hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
2522
+ entries: entries.map((entry) => entry.line),
2523
+ ...probe.logsTruncated ? { logsTruncated: true } : {}
1581
2524
  };
1582
2525
  }
1583
2526
  /**
@@ -1868,6 +2811,7 @@ var DeeplineClient = class {
1868
2811
  runId: workflowId,
1869
2812
  status: "running",
1870
2813
  logs: [],
2814
+ lastLogSeq: 0,
1871
2815
  latest: null
1872
2816
  };
1873
2817
  if (options?.signal?.aborted) {
@@ -3550,11 +4494,11 @@ function sanitizeCsvProjectionInfo(input2) {
3550
4494
  const rows = input2.rows.map(stripCsvProjectionFields);
3551
4495
  return { rows, columns };
3552
4496
  }
3553
- function isRecord2(value) {
4497
+ function isRecord3(value) {
3554
4498
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
3555
4499
  }
3556
4500
  function isSerializedDataset(value) {
3557
- return isRecord2(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
4501
+ return isRecord3(value) && value.kind === "dataset" && typeof value.count === "number" && Array.isArray(value.preview);
3558
4502
  }
3559
4503
  function pathParts(path) {
3560
4504
  return path.split(".").map((part) => part.trim()).filter(Boolean);
@@ -3562,7 +4506,7 @@ function pathParts(path) {
3562
4506
  function valueAtPath(root, path) {
3563
4507
  let cursor = root;
3564
4508
  for (const part of pathParts(path)) {
3565
- if (!isRecord2(cursor)) {
4509
+ if (!isRecord3(cursor)) {
3566
4510
  return void 0;
3567
4511
  }
3568
4512
  cursor = cursor[part];
@@ -3570,17 +4514,17 @@ function valueAtPath(root, path) {
3570
4514
  return cursor;
3571
4515
  }
3572
4516
  function totalRowsForDataset(result, datasetPath) {
3573
- const metadata = isRecord2(result._metadata) ? result._metadata : null;
4517
+ const metadata = isRecord3(result._metadata) ? result._metadata : null;
3574
4518
  const parentPath = datasetPath.split(".").slice(0, -1).join(".");
3575
4519
  const parent = parentPath ? valueAtPath({ result }, parentPath) : result;
3576
- return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord2(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
4520
+ return metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count ?? (isRecord3(parent) ? parent.totalRows ?? parent.rowCount ?? parent.count : void 0) ?? result.totalRows ?? result.rowCount ?? result.count;
3577
4521
  }
3578
4522
  function rowArray(value) {
3579
4523
  if (!Array.isArray(value)) {
3580
4524
  return null;
3581
4525
  }
3582
4526
  const rows = value.filter(
3583
- (row) => isRecord2(row)
4527
+ (row) => isRecord3(row)
3584
4528
  );
3585
4529
  return rows.length === value.length ? rows : null;
3586
4530
  }
@@ -3657,7 +4601,7 @@ function collectDatasetCandidates(input2) {
3657
4601
  });
3658
4602
  return;
3659
4603
  }
3660
- if (!isRecord2(input2.value)) {
4604
+ if (!isRecord3(input2.value)) {
3661
4605
  return;
3662
4606
  }
3663
4607
  for (const [key, child] of Object.entries(input2.value)) {
@@ -3674,12 +4618,12 @@ function collectDatasetCandidates(input2) {
3674
4618
  }
3675
4619
  }
3676
4620
  function collectCanonicalRowsInfos(statusOrResult) {
3677
- const root = isRecord2(statusOrResult) ? statusOrResult : null;
3678
- const result = isRecord2(root?.result) ? root.result : root;
4621
+ const root = isRecord3(statusOrResult) ? statusOrResult : null;
4622
+ const result = isRecord3(root?.result) ? root.result : root;
3679
4623
  if (!result) {
3680
4624
  return [];
3681
4625
  }
3682
- const metadata = isRecord2(result._metadata) ? result._metadata : null;
4626
+ const metadata = isRecord3(result._metadata) ? result._metadata : null;
3683
4627
  const totalFromMetadata = metadata?.totalRows ?? metadata?.rowCount ?? metadata?.count;
3684
4628
  const candidates = [
3685
4629
  {
@@ -3703,8 +4647,8 @@ function collectCanonicalRowsInfos(statusOrResult) {
3703
4647
  total: totalFromMetadata ?? result.totalRows ?? result.rowCount ?? result.count
3704
4648
  }
3705
4649
  ];
3706
- if (isRecord2(result.output)) {
3707
- const outputMetadata = isRecord2(result.output._metadata) ? result.output._metadata : null;
4650
+ if (isRecord3(result.output)) {
4651
+ const outputMetadata = isRecord3(result.output._metadata) ? result.output._metadata : null;
3708
4652
  const outputTotalFromMetadata = outputMetadata?.totalRows ?? outputMetadata?.rowCount ?? outputMetadata?.count;
3709
4653
  candidates.push(
3710
4654
  {
@@ -3750,8 +4694,8 @@ function collectCanonicalRowsInfos(statusOrResult) {
3750
4694
  return infos;
3751
4695
  }
3752
4696
  function collectSerializedDatasetRowsInfos(statusOrResult) {
3753
- const root = isRecord2(statusOrResult) ? statusOrResult : null;
3754
- const result = isRecord2(root?.result) ? root.result : root;
4697
+ const root = isRecord3(statusOrResult) ? statusOrResult : null;
4698
+ const result = isRecord3(root?.result) ? root.result : root;
3755
4699
  if (!result) {
3756
4700
  return [];
3757
4701
  }
@@ -3785,17 +4729,17 @@ function percentText(numerator, denominator) {
3785
4729
  return datasetSummaryPercentText(numerator, denominator);
3786
4730
  }
3787
4731
  function isDatasetExecutionStatsInput(value) {
3788
- return isRecord2(value) && isRecord2(value.columnStats) && Object.values(value.columnStats).every(isRecord2);
4732
+ return isRecord3(value) && isRecord3(value.columnStats) && Object.values(value.columnStats).every(isRecord3);
3789
4733
  }
3790
4734
  function extractDatasetExecutionStats(statusOrResult) {
3791
- if (!isRecord2(statusOrResult)) {
4735
+ if (!isRecord3(statusOrResult)) {
3792
4736
  return null;
3793
4737
  }
3794
4738
  const direct = statusOrResult.dataset_execution_stats;
3795
4739
  if (isDatasetExecutionStatsInput(direct)) {
3796
4740
  return direct;
3797
4741
  }
3798
- const nested = isRecord2(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
4742
+ const nested = isRecord3(statusOrResult.result) ? statusOrResult.result.dataset_execution_stats : null;
3799
4743
  return isDatasetExecutionStatsInput(nested) ? nested : null;
3800
4744
  }
3801
4745
  function countPercentText(count, denominator) {
@@ -3836,13 +4780,13 @@ function summarizeSampleValue(value, depth = 0) {
3836
4780
  if (typeof parsed === "number" || typeof parsed === "boolean") return parsed;
3837
4781
  if (depth >= 3) {
3838
4782
  if (Array.isArray(parsed)) return [];
3839
- if (isRecord2(parsed)) return {};
4783
+ if (isRecord3(parsed)) return {};
3840
4784
  return compactScalar(parsed);
3841
4785
  }
3842
4786
  if (Array.isArray(parsed)) {
3843
4787
  return parsed.slice(0, 3).map((item) => summarizeSampleValue(item, depth + 1));
3844
4788
  }
3845
- if (isRecord2(parsed)) {
4789
+ if (isRecord3(parsed)) {
3846
4790
  const out = {};
3847
4791
  for (const [key, nested] of Object.entries(parsed)) {
3848
4792
  if (["__dl", "meta", "metadata"].includes(key)) {
@@ -3873,7 +4817,7 @@ function compactCell(value) {
3873
4817
  }
3874
4818
  return `[${parsed.length} items]`;
3875
4819
  }
3876
- if (isRecord2(parsed)) {
4820
+ if (isRecord3(parsed)) {
3877
4821
  for (const key of ["matched_result", "output"]) {
3878
4822
  if (parsed[key] !== null && parsed[key] !== void 0 && parsed[key] !== "") {
3879
4823
  return compactCell(parsed[key]);
@@ -6192,17 +7136,17 @@ function parsePositiveInteger2(value, flagName) {
6192
7136
  }
6193
7137
  return parsed;
6194
7138
  }
6195
- function isRecord3(value) {
7139
+ function isRecord4(value) {
6196
7140
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
6197
7141
  }
6198
7142
  function stringValue(value) {
6199
7143
  return typeof value === "string" ? value.trim() : "";
6200
7144
  }
6201
7145
  function extractionEntries(value) {
6202
- if (Array.isArray(value)) return value.filter(isRecord3);
6203
- if (!isRecord3(value)) return [];
7146
+ if (Array.isArray(value)) return value.filter(isRecord4);
7147
+ if (!isRecord4(value)) return [];
6204
7148
  return Object.entries(value).map(
6205
- ([name, entry]) => isRecord3(entry) ? { name, ...entry } : { name }
7149
+ ([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
6206
7150
  );
6207
7151
  }
6208
7152
  var PlayBootstrapError = class extends Error {
@@ -6622,7 +7566,7 @@ function readCsvSampleRows(sample) {
6622
7566
  relax_column_count: true,
6623
7567
  trim: true
6624
7568
  });
6625
- return Array.isArray(parsedRows) ? parsedRows.filter(isRecord3) : [];
7569
+ return Array.isArray(parsedRows) ? parsedRows.filter(isRecord4) : [];
6626
7570
  }
6627
7571
  function readSourceCsvColumnSpecs(csvPath) {
6628
7572
  const sample = readCsvSample(csvPath);
@@ -6655,16 +7599,16 @@ function packagedCsvPathForPlay(csvPath) {
6655
7599
  return portablePath.startsWith(".") ? portablePath : `./${portablePath}`;
6656
7600
  }
6657
7601
  function getterNamesFromTool(tool, kind) {
6658
- const usageGuidance = isRecord3(tool?.usageGuidance) ? tool.usageGuidance : {};
6659
- const resultGuidance = isRecord3(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord3(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
7602
+ const usageGuidance = isRecord4(tool?.usageGuidance) ? tool.usageGuidance : {};
7603
+ const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
6660
7604
  const key = kind === "list" ? "extractedLists" : "extractedValues";
6661
7605
  const snakeKey = kind === "list" ? "extracted_lists" : "extracted_values";
6662
7606
  return extractionEntries(resultGuidance[key] ?? resultGuidance[snakeKey]).map((entry) => stringValue(entry.name)).filter(Boolean);
6663
7607
  }
6664
7608
  function targetGettersFromTool(tool) {
6665
- const record = isRecord3(tool) ? tool : {};
7609
+ const record = isRecord4(tool) ? tool : {};
6666
7610
  const raw = record.targetGetters ?? record.target_getters;
6667
- if (!isRecord3(raw)) return {};
7611
+ if (!isRecord4(raw)) return {};
6668
7612
  const entries = [];
6669
7613
  for (const [target, value] of Object.entries(raw)) {
6670
7614
  const paths = Array.isArray(value) ? value.map((path) => typeof path === "string" ? path.trim() : "").filter(Boolean) : [];
@@ -6685,10 +7629,10 @@ function listRowCandidateKeysFromTool(tool) {
6685
7629
  return [...keys].sort();
6686
7630
  }
6687
7631
  function inputPropertyNames(schema) {
6688
- if (!isRecord3(schema)) return [];
6689
- if (isRecord3(schema.properties)) return Object.keys(schema.properties);
7632
+ if (!isRecord4(schema)) return [];
7633
+ if (isRecord4(schema.properties)) return Object.keys(schema.properties);
6690
7634
  if (Array.isArray(schema.fields)) {
6691
- return schema.fields.filter(isRecord3).map((field) => stringValue(field.name)).filter(Boolean);
7635
+ return schema.fields.filter(isRecord4).map((field) => stringValue(field.name)).filter(Boolean);
6692
7636
  }
6693
7637
  return [];
6694
7638
  }
@@ -6702,7 +7646,7 @@ function schemaFieldDetails(schema) {
6702
7646
  return { required, optional };
6703
7647
  }
6704
7648
  function jsonSchemaTypeExpression(schema) {
6705
- if (!isRecord3(schema)) return "unknown";
7649
+ if (!isRecord4(schema)) return "unknown";
6706
7650
  const type = schema.type;
6707
7651
  if (Array.isArray(type)) {
6708
7652
  return type.map((entry) => jsonSchemaTypeExpression({ ...schema, type: entry })).join(" | ");
@@ -6732,7 +7676,7 @@ function jsonSchemaTypeExpression(schema) {
6732
7676
  }
6733
7677
  }
6734
7678
  function objectPropertySchema(schema, property) {
6735
- return isRecord3(schema) && isRecord3(schema.properties) ? schema.properties[property] : null;
7679
+ return isRecord4(schema) && isRecord4(schema.properties) ? schema.properties[property] : null;
6736
7680
  }
6737
7681
  function playOutputHasField(schema, field) {
6738
7682
  return objectPropertySchema(schema, field) != null;
@@ -6908,14 +7852,14 @@ ${indent2.slice(2)}}`;
6908
7852
  }
6909
7853
  function requiredPlayInputFields(play) {
6910
7854
  const schema = play?.inputSchema;
6911
- if (!isRecord3(schema)) return [];
7855
+ if (!isRecord4(schema)) return [];
6912
7856
  if (Array.isArray(schema.required)) {
6913
7857
  return schema.required.filter(
6914
7858
  (value) => typeof value === "string"
6915
7859
  );
6916
7860
  }
6917
7861
  if (Array.isArray(schema.fields)) {
6918
- return schema.fields.filter(isRecord3).filter(
7862
+ return schema.fields.filter(isRecord4).filter(
6919
7863
  (field) => field.required === true && typeof field.name === "string"
6920
7864
  ).map((field) => String(field.name));
6921
7865
  }
@@ -7096,7 +8040,7 @@ function validateBootstrapRoutes(input2) {
7096
8040
  }
7097
8041
  }
7098
8042
  function staticPipelineSubsteps(pipeline) {
7099
- if (!isRecord3(pipeline)) return [];
8043
+ if (!isRecord4(pipeline)) return [];
7100
8044
  return [
7101
8045
  ...extractionEntries(pipeline.stages),
7102
8046
  ...extractionEntries(pipeline.substeps)
@@ -7104,7 +8048,7 @@ function staticPipelineSubsteps(pipeline) {
7104
8048
  }
7105
8049
  function playUsesMapBackedRuntime(play) {
7106
8050
  const pipeline = play?.staticPipeline;
7107
- if (!isRecord3(pipeline)) return false;
8051
+ if (!isRecord4(pipeline)) return false;
7108
8052
  if (stringValue(pipeline.tableNamespace)) return true;
7109
8053
  return staticPipelineSubsteps(pipeline).some((substep) => {
7110
8054
  if (stringValue(substep.type) === "map") return true;
@@ -7113,7 +8057,7 @@ function playUsesMapBackedRuntime(play) {
7113
8057
  aliases: [],
7114
8058
  runCommand: "",
7115
8059
  examples: [],
7116
- staticPipeline: isRecord3(substep.pipeline) ? substep.pipeline : null
8060
+ staticPipeline: isRecord4(substep.pipeline) ? substep.pipeline : null
7117
8061
  });
7118
8062
  });
7119
8063
  }
@@ -8438,15 +9382,6 @@ function formatRunLine(run) {
8438
9382
  const credits = typeof run.billingTotalCredits === "number" && Number.isFinite(run.billingTotalCredits) ? `${formatCreditAmount(run.billingTotalCredits)} credits` : "\u2014";
8439
9383
  return `${run.workflowId} ${run.status} ${formatTimestamp(run.startTime)} ${credits}`;
8440
9384
  }
8441
- function isTransientPlayStreamError(error) {
8442
- if (error instanceof DeeplineError && typeof error.statusCode === "number") {
8443
- return error.statusCode >= 500 && error.statusCode < 600;
8444
- }
8445
- const text = error instanceof Error ? error.message : String(error);
8446
- return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
8447
- text
8448
- );
8449
- }
8450
9385
  function playStatusErrorText(status) {
8451
9386
  const chunks = [];
8452
9387
  const progressError = status.progress?.error;
@@ -8784,97 +9719,227 @@ function assertPlayWaitNotTimedOut(input2) {
8784
9719
  }
8785
9720
  }
8786
9721
  async function waitForPlayCompletionByStream(input2) {
8787
- const controller = new AbortController();
8788
- let timedOut = false;
8789
9722
  let lastPhase = null;
8790
- const timeout = input2.waitTimeoutMs === null ? null : setTimeout(
8791
- () => {
8792
- timedOut = true;
8793
- controller.abort();
8794
- },
8795
- Math.max(1, input2.waitTimeoutMs - (Date.now() - input2.startedAt))
8796
- );
8797
- try {
8798
- for await (const event of input2.client.streamPlayRunEvents(
8799
- input2.workflowId,
8800
- { signal: controller.signal }
8801
- )) {
8802
- assertPlayWaitNotTimedOut({ ...input2, lastPhase });
8803
- const phase = describeLiveEventPhase(event);
8804
- if (phase) {
8805
- lastPhase = phase;
8806
- input2.progress.phase(phase);
8807
- }
8808
- emitLiveDebugTableHints({
8809
- event,
8810
- playName: input2.playName,
8811
- runId: input2.workflowId,
8812
- jsonOutput: input2.jsonOutput,
8813
- state: input2.state,
8814
- progress: input2.progress
9723
+ let reconnectAttempt = 0;
9724
+ const withDashboardUrl = (status) => input2.dashboardUrl ? { ...status, dashboardUrl: input2.dashboardUrl } : status;
9725
+ const remainingWaitMs = () => input2.waitTimeoutMs === null ? null : input2.waitTimeoutMs - (Date.now() - input2.startedAt);
9726
+ const fetchTerminalStatus = async () => {
9727
+ let refreshed;
9728
+ try {
9729
+ refreshed = await input2.client.getPlayStatus(input2.workflowId, {
9730
+ billing: false
8815
9731
  });
8816
- printPlayLogLines({
8817
- lines: getLogLinesFromLiveEvent(event),
8818
- status: null,
8819
- jsonOutput: input2.jsonOutput,
8820
- emitLogs: input2.emitLogs,
9732
+ } catch (error) {
9733
+ if (isTransientPlayStreamError(error)) {
9734
+ return null;
9735
+ }
9736
+ throw error;
9737
+ }
9738
+ return TERMINAL_PLAY_STATUSES2.has(refreshed.status) ? withDashboardUrl(refreshed) : null;
9739
+ };
9740
+ const handleLiveEvent = async (event) => {
9741
+ assertPlayWaitNotTimedOut({ ...input2, lastPhase });
9742
+ const phase = describeLiveEventPhase(event);
9743
+ if (phase) {
9744
+ lastPhase = phase;
9745
+ input2.progress.phase(phase);
9746
+ }
9747
+ emitLiveDebugTableHints({
9748
+ event,
9749
+ playName: input2.playName,
9750
+ runId: input2.workflowId,
9751
+ jsonOutput: input2.jsonOutput,
9752
+ state: input2.state,
9753
+ progress: input2.progress
9754
+ });
9755
+ printPlayLogLines({
9756
+ lines: getLogLinesFromLiveEvent(event),
9757
+ status: null,
9758
+ jsonOutput: input2.jsonOutput,
9759
+ emitLogs: input2.emitLogs,
9760
+ state: input2.state,
9761
+ progress: input2.progress
9762
+ });
9763
+ if (!input2.jsonOutput) {
9764
+ const progressLines = getProgressLinesFromLiveEvent(event);
9765
+ printPlayProgressLines({
9766
+ lines: progressLines,
8821
9767
  state: input2.state,
8822
9768
  progress: input2.progress
8823
9769
  });
8824
- if (!input2.jsonOutput) {
8825
- const progressLines = getProgressLinesFromLiveEvent(event);
8826
- printPlayProgressLines({
8827
- lines: progressLines,
9770
+ if (progressLines.length === 0) {
9771
+ printPlayStatusHeartbeat({
9772
+ event,
9773
+ playName: input2.playName,
8828
9774
  state: input2.state,
8829
9775
  progress: input2.progress
8830
9776
  });
8831
- if (progressLines.length === 0) {
8832
- printPlayStatusHeartbeat({
8833
- event,
8834
- playName: input2.playName,
8835
- state: input2.state,
8836
- progress: input2.progress
8837
- });
8838
- }
8839
9777
  }
8840
- const finalStatus = getFinalStatusFromLiveEvent(event);
8841
- if (finalStatus) {
8842
- return input2.dashboardUrl ? { ...finalStatus, dashboardUrl: input2.dashboardUrl } : finalStatus;
9778
+ }
9779
+ const finalStatus = getFinalStatusFromLiveEvent(event);
9780
+ if (finalStatus) {
9781
+ return withDashboardUrl(finalStatus);
9782
+ }
9783
+ const status = getStatusFromLiveEvent(event);
9784
+ if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
9785
+ const refreshedStatus = await input2.client.getPlayStatus(
9786
+ input2.workflowId,
9787
+ {
9788
+ billing: false
9789
+ }
9790
+ );
9791
+ if (TERMINAL_PLAY_STATUSES2.has(refreshedStatus.status)) {
9792
+ return withDashboardUrl(refreshedStatus);
8843
9793
  }
8844
- const status = getStatusFromLiveEvent(event);
8845
- if (status && TERMINAL_PLAY_STATUSES2.has(status)) {
8846
- const refreshedStatus = await input2.client.getPlayStatus(
8847
- input2.workflowId,
8848
- {
8849
- billing: false
8850
- }
8851
- );
8852
- if (TERMINAL_PLAY_STATUSES2.has(refreshedStatus.status)) {
8853
- return input2.dashboardUrl ? { ...refreshedStatus, dashboardUrl: input2.dashboardUrl } : refreshedStatus;
9794
+ }
9795
+ return null;
9796
+ };
9797
+ const watchViaObserveTransport = async () => {
9798
+ const controller = new AbortController();
9799
+ let timedOut = false;
9800
+ const remaining = remainingWaitMs();
9801
+ const timeout = remaining === null ? null : setTimeout(
9802
+ () => {
9803
+ timedOut = true;
9804
+ controller.abort();
9805
+ },
9806
+ Math.max(1, remaining)
9807
+ );
9808
+ try {
9809
+ for await (const event of input2.client.observeRunEvents(
9810
+ input2.workflowId,
9811
+ {
9812
+ signal: controller.signal,
9813
+ onNotice: input2.jsonOutput ? void 0 : (message) => input2.progress.writeLine(message)
9814
+ }
9815
+ )) {
9816
+ const terminal2 = await handleLiveEvent(event);
9817
+ if (terminal2) {
9818
+ return terminal2;
8854
9819
  }
8855
9820
  }
9821
+ } finally {
9822
+ if (timeout) {
9823
+ clearTimeout(timeout);
9824
+ }
9825
+ }
9826
+ const terminal = await fetchTerminalStatus();
9827
+ if (terminal) {
9828
+ return terminal;
8856
9829
  }
8857
- } catch (error) {
8858
9830
  if (timedOut) {
8859
9831
  assertPlayWaitNotTimedOut({ ...input2, lastPhase });
8860
9832
  }
8861
- throw error;
8862
- } finally {
8863
- if (timeout) {
8864
- clearTimeout(timeout);
9833
+ throw new DeeplineError(
9834
+ `Run observation for ${input2.workflowId} ended before a terminal status.`,
9835
+ void 0,
9836
+ "PLAY_LIVE_STREAM_ENDED",
9837
+ { runId: input2.workflowId, workflowId: input2.workflowId }
9838
+ );
9839
+ };
9840
+ try {
9841
+ return await watchViaObserveTransport();
9842
+ } catch (error) {
9843
+ if (!(error instanceof RunObserveTransportUnavailableError)) {
9844
+ throw error;
8865
9845
  }
8866
- }
8867
- const phaseSuffix = lastPhase && lastPhase.trim() ? ` (last observed phase: ${lastPhase.trim()})` : "";
8868
- throw new DeeplineError(
8869
- `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}'.`,
8870
- void 0,
8871
- "PLAY_LIVE_STREAM_ENDED",
8872
- {
8873
- runId: input2.workflowId,
9846
+ if (!input2.jsonOutput) {
9847
+ input2.progress.writeLine(
9848
+ `[play watch] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
9849
+ );
9850
+ }
9851
+ recordCliTrace({
9852
+ phase: "cli.play_observe_transport_fallback",
9853
+ ms: Date.now() - input2.startedAt,
9854
+ ok: true,
9855
+ playName: input2.playName,
8874
9856
  workflowId: input2.workflowId,
8875
- ...lastPhase ? { phase: lastPhase } : {}
9857
+ reason: error.reason
9858
+ });
9859
+ }
9860
+ const streamOneWindow = async () => {
9861
+ const controller = new AbortController();
9862
+ let timedOut = false;
9863
+ let sawEvent = false;
9864
+ const remaining = remainingWaitMs();
9865
+ const timeout = remaining === null ? null : setTimeout(
9866
+ () => {
9867
+ timedOut = true;
9868
+ controller.abort();
9869
+ },
9870
+ Math.max(1, remaining)
9871
+ );
9872
+ try {
9873
+ for await (const event of input2.client.streamPlayRunEvents(
9874
+ input2.workflowId,
9875
+ { signal: controller.signal }
9876
+ )) {
9877
+ sawEvent = true;
9878
+ const terminal = await handleLiveEvent(event);
9879
+ if (terminal) {
9880
+ return { kind: "terminal", status: terminal };
9881
+ }
9882
+ }
9883
+ } catch (error) {
9884
+ if (timedOut) {
9885
+ return { kind: "timed_out", sawEvent };
9886
+ }
9887
+ if (!isTransientPlayStreamError(error)) {
9888
+ throw error;
9889
+ }
9890
+ return {
9891
+ kind: "ended",
9892
+ sawEvent,
9893
+ reason: error instanceof Error ? error.message : String(error)
9894
+ };
9895
+ } finally {
9896
+ if (timeout) {
9897
+ clearTimeout(timeout);
9898
+ }
8876
9899
  }
8877
- );
9900
+ return {
9901
+ kind: "ended",
9902
+ sawEvent,
9903
+ reason: "stream window ended before a terminal event"
9904
+ };
9905
+ };
9906
+ for (; ; ) {
9907
+ const connectedAt = Date.now();
9908
+ const window = await streamOneWindow();
9909
+ if (window.kind === "terminal") {
9910
+ return window.status;
9911
+ }
9912
+ const terminal = await fetchTerminalStatus();
9913
+ if (terminal) {
9914
+ return terminal;
9915
+ }
9916
+ assertPlayWaitNotTimedOut({ ...input2, lastPhase });
9917
+ if (window.sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
9918
+ reconnectAttempt = 0;
9919
+ }
9920
+ const delayMs = streamReconnectDelayMs(reconnectAttempt);
9921
+ reconnectAttempt += 1;
9922
+ const reason = window.kind === "ended" ? window.reason : "wait timeout raced the stream";
9923
+ if (!input2.jsonOutput) {
9924
+ input2.progress.writeLine(
9925
+ `[play watch] stream ended without a terminal status; reconnecting to run ${input2.workflowId} (${reason})`
9926
+ );
9927
+ }
9928
+ recordCliTrace({
9929
+ phase: "cli.play_tail_stream_reconnect",
9930
+ ms: Date.now() - input2.startedAt,
9931
+ ok: true,
9932
+ playName: input2.playName,
9933
+ workflowId: input2.workflowId,
9934
+ attempt: reconnectAttempt,
9935
+ delayMs,
9936
+ reason
9937
+ });
9938
+ const remainingBeforeSleep = remainingWaitMs();
9939
+ await sleep4(
9940
+ remainingBeforeSleep === null ? delayMs : Math.min(delayMs, Math.max(1, remainingBeforeSleep))
9941
+ );
9942
+ }
8878
9943
  }
8879
9944
  async function startAndWaitForPlayCompletionByStream(input2) {
8880
9945
  for (let attempt = 0; attempt <= PLAY_START_TRANSIENT_RETRY_DELAYS_MS.length; attempt += 1) {
@@ -9819,6 +10884,10 @@ function buildRunPackageTextLines(packaged) {
9819
10884
  const lines = [
9820
10885
  `${status === "completed" ? "\u2713" : status === "failed" ? "\u2717" : "\u2022"} ${status} ${runId}`
9821
10886
  ];
10887
+ const runError = typeof run.error === "string" && run.error.trim() ? run.error.trim() : null;
10888
+ if (runError && (status === "failed" || status === "cancelled")) {
10889
+ lines.push(` error: ${runError.slice(0, 200)}`);
10890
+ }
9822
10891
  if (playName) {
9823
10892
  lines.push(` play: ${playName}`);
9824
10893
  }
@@ -9920,18 +10989,33 @@ function writePlayResult(status, jsonOutput, options) {
9920
10989
  ` }
9921
10990
  );
9922
10991
  }
10992
+ function playRunPackageStepCount(pkg) {
10993
+ if (!pkg) return 0;
10994
+ const steps = pkg.steps;
10995
+ return Array.isArray(steps) ? steps.length : 0;
10996
+ }
9923
10997
  async function resolvePlayRunOutputStatus(input2) {
9924
- if (!input2.fullJson || !getPlayRunPackage(input2.status)) {
9925
- return input2.status;
9926
- }
9927
10998
  const runId = input2.status.runId;
9928
10999
  if (!runId) {
9929
11000
  return input2.status;
9930
11001
  }
9931
- const refreshedStatus = await input2.client.getPlayStatus(runId, {
11002
+ const streamedPackage = getPlayRunPackage(input2.status);
11003
+ const refreshForFullJson = input2.fullJson && streamedPackage !== null;
11004
+ const streamedTextPackageIncomplete = !input2.jsonOutput && input2.status.status === "completed" && playRunPackageStepCount(streamedPackage) === 0;
11005
+ if (!refreshForFullJson && !streamedTextPackageIncomplete) {
11006
+ return input2.status;
11007
+ }
11008
+ let refreshedStatus = await input2.client.getPlayStatus(runId, {
9932
11009
  billing: false,
9933
11010
  full: input2.fullJson
9934
11011
  });
11012
+ for (let attempt = 0; attempt < 3 && streamedTextPackageIncomplete && refreshedStatus.status === "completed" && playRunPackageStepCount(getPlayRunPackage(refreshedStatus)) === 0; attempt += 1) {
11013
+ await sleep4(250);
11014
+ refreshedStatus = await input2.client.getPlayStatus(runId, {
11015
+ billing: false,
11016
+ full: input2.fullJson
11017
+ });
11018
+ }
9935
11019
  const dashboardUrl = input2.status.dashboardUrl;
9936
11020
  return typeof dashboardUrl === "string" ? { ...refreshedStatus, dashboardUrl } : refreshedStatus;
9937
11021
  }
@@ -10185,7 +11269,7 @@ function extractPlayValidationErrors(value) {
10185
11269
  if (value instanceof DeeplineError) {
10186
11270
  return extractPlayValidationErrors(value.details);
10187
11271
  }
10188
- if (!isRecord4(value)) {
11272
+ if (!isRecord5(value)) {
10189
11273
  return [];
10190
11274
  }
10191
11275
  const directErrors = stringArrayField(value, "errors");
@@ -10543,7 +11627,7 @@ function shouldUseLocalOnlyPlayCheck() {
10543
11627
  const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
10544
11628
  return value === "1" || value === "true" || value === "yes" || value === "on";
10545
11629
  }
10546
- function isRecord4(value) {
11630
+ function isRecord5(value) {
10547
11631
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
10548
11632
  }
10549
11633
  function stringValue2(value) {
@@ -10553,14 +11637,14 @@ function asArray(value) {
10553
11637
  return Array.isArray(value) ? value : [];
10554
11638
  }
10555
11639
  function extractionEntries2(value) {
10556
- if (Array.isArray(value)) return value.filter(isRecord4);
10557
- if (!isRecord4(value)) return [];
11640
+ if (Array.isArray(value)) return value.filter(isRecord5);
11641
+ if (!isRecord5(value)) return [];
10558
11642
  return Object.entries(value).map(
10559
- ([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
11643
+ ([name, entry]) => isRecord5(entry) ? { name, ...entry } : { name }
10560
11644
  );
10561
11645
  }
10562
11646
  function firstRawPath(entry) {
10563
- const details = isRecord4(entry.details) ? entry.details : {};
11647
+ const details = isRecord5(entry.details) ? entry.details : {};
10564
11648
  const paths = [
10565
11649
  ...asArray(details.rawToolOutputPaths),
10566
11650
  ...asArray(details.raw_tool_output_paths),
@@ -10578,12 +11662,12 @@ function checkHintRawPath(value) {
10578
11662
  function collectStaticPipelineToolIds(staticPipeline) {
10579
11663
  const seen = /* @__PURE__ */ new Set();
10580
11664
  const visitPipeline = (pipeline) => {
10581
- if (!isRecord4(pipeline)) return;
11665
+ if (!isRecord5(pipeline)) return;
10582
11666
  for (const step of [
10583
11667
  ...asArray(pipeline.stages),
10584
11668
  ...asArray(pipeline.substeps)
10585
11669
  ]) {
10586
- if (!isRecord4(step)) continue;
11670
+ if (!isRecord5(step)) continue;
10587
11671
  if (step.type === "tool") {
10588
11672
  const toolId = stringValue2(step.toolId) || stringValue2(step.tool);
10589
11673
  if (toolId) seen.add(toolId);
@@ -10597,9 +11681,9 @@ function collectStaticPipelineToolIds(staticPipeline) {
10597
11681
  return [...seen].sort();
10598
11682
  }
10599
11683
  function toolGetterHintFromMetadata(toolId, tool) {
10600
- const usageGuidance = isRecord4(tool.usageGuidance) ? tool.usageGuidance : {};
10601
- const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
10602
- const toolResponse = isRecord4(resultGuidance.toolResponse) ? resultGuidance.toolResponse : isRecord4(resultGuidance.tool_response) ? resultGuidance.tool_response : {};
11684
+ const usageGuidance = isRecord5(tool.usageGuidance) ? tool.usageGuidance : {};
11685
+ const resultGuidance = isRecord5(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord5(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
11686
+ const toolResponse = isRecord5(resultGuidance.toolResponse) ? resultGuidance.toolResponse : isRecord5(resultGuidance.tool_response) ? resultGuidance.tool_response : {};
10603
11687
  const lists = extractionEntries2(
10604
11688
  resultGuidance.extractedLists ?? resultGuidance.extracted_lists
10605
11689
  ).map((entry) => ({
@@ -10895,7 +11979,8 @@ async function handleFileBackedRun(options) {
10895
11979
  await resolvePlayRunOutputStatus({
10896
11980
  client,
10897
11981
  status: finalStatus,
10898
- fullJson: options.fullJson
11982
+ fullJson: options.fullJson,
11983
+ jsonOutput: options.jsonOutput
10899
11984
  }),
10900
11985
  playName
10901
11986
  );
@@ -11045,7 +12130,8 @@ async function handleNamedRun(options) {
11045
12130
  await resolvePlayRunOutputStatus({
11046
12131
  client,
11047
12132
  status: finalStatus,
11048
- fullJson: options.fullJson
12133
+ fullJson: options.fullJson,
12134
+ jsonOutput: options.jsonOutput
11049
12135
  }),
11050
12136
  playName
11051
12137
  );
@@ -11226,8 +12312,17 @@ async function handleRunTail(args) {
11226
12312
  }
11227
12313
  }
11228
12314
  const client = new DeeplineClient();
11229
- const status = await client.runs.tail(runId);
11230
- writePlayResult(status, argsWantJson(args));
12315
+ const jsonOutput = argsWantJson(args);
12316
+ const status = await client.runs.tail(runId, {
12317
+ // Human mode only: in --json mode emit nothing non-protocol.
12318
+ onReconnect: jsonOutput ? void 0 : ({ reason }) => {
12319
+ process.stderr.write(
12320
+ `[runs tail] stream ended without a terminal status; reconnecting to run ${runId} (${reason})
12321
+ `
12322
+ );
12323
+ }
12324
+ });
12325
+ writePlayResult(status, jsonOutput);
11231
12326
  return status.status === "failed" ? 1 : 0;
11232
12327
  }
11233
12328
  async function handleRunLogs(args) {
@@ -11252,21 +12347,28 @@ async function handleRunLogs(args) {
11252
12347
  }
11253
12348
  }
11254
12349
  const client = new DeeplineClient();
11255
- const status = await client.runs.get(runId, { full: true });
11256
- const logs = status.progress?.logs ?? [];
11257
12350
  if (outPath) {
12351
+ const result2 = await client.runs.logs(runId, { all: true });
12352
+ const logs = result2.entries;
11258
12353
  (0, import_node_fs10.writeFileSync)(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
11259
12354
  printCommandEnvelope(
11260
12355
  {
11261
- runId: status.runId,
12356
+ runId: result2.runId,
11262
12357
  log_path: outPath,
11263
12358
  lineCount: logs.length,
12359
+ totalCount: result2.totalCount,
12360
+ ...result2.logsTruncated ? { logsTruncated: true } : {},
11264
12361
  local: { log_path: outPath },
11265
12362
  render: {
11266
12363
  sections: [
11267
12364
  {
11268
12365
  title: "run logs",
11269
- lines: [`Wrote ${logs.length} log lines to ${outPath}`]
12366
+ lines: [
12367
+ `Wrote ${logs.length} log lines to ${outPath}`,
12368
+ ...result2.logsTruncated ? [
12369
+ `Run crossed the log retention cap: ${result2.totalCount} lines were emitted, stored bodies end at the truncation marker.`
12370
+ ] : []
12371
+ ]
11270
12372
  }
11271
12373
  ]
11272
12374
  }
@@ -11275,25 +12377,26 @@ async function handleRunLogs(args) {
11275
12377
  );
11276
12378
  return 0;
11277
12379
  }
11278
- const entries = logs.slice(Math.max(0, logs.length - limit));
12380
+ const result = await client.runs.logs(runId, { limit });
11279
12381
  printCommandEnvelope(
11280
12382
  {
11281
- runId: status.runId,
11282
- totalCount: logs.length,
11283
- returnedCount: entries.length,
11284
- firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
11285
- lastSequence: logs.length === 0 ? null : logs.length,
11286
- truncated: logs.length > entries.length,
11287
- hasMore: logs.length > entries.length,
11288
- entries,
12383
+ runId: result.runId,
12384
+ totalCount: result.totalCount,
12385
+ returnedCount: result.returnedCount,
12386
+ firstSequence: result.firstSequence,
12387
+ lastSequence: result.lastSequence,
12388
+ truncated: result.truncated,
12389
+ hasMore: result.hasMore,
12390
+ ...result.logsTruncated ? { logsTruncated: true } : {},
12391
+ entries: result.entries,
11289
12392
  next: {
11290
- export: `deepline runs logs ${status.runId} --out run.log --json`
12393
+ export: `deepline runs logs ${result.runId} --out run.log --json`
11291
12394
  },
11292
- render: { sections: [{ title: "run logs", lines: entries }] }
12395
+ render: { sections: [{ title: "run logs", lines: result.entries }] }
11293
12396
  },
11294
12397
  {
11295
12398
  json: argsWantJson(args),
11296
- text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`
12399
+ text: `${result.entries.join("\n")}${result.entries.length > 0 ? "\n" : ""}`
11297
12400
  }
11298
12401
  );
11299
12402
  return 0;
@@ -11316,6 +12419,34 @@ async function handleRunStop(args) {
11316
12419
  }
11317
12420
  const client = new DeeplineClient();
11318
12421
  const result = await client.runs.stop(runId, { reason });
12422
+ const stopNotConfirmed = result.stopped === false || result.staleSchedulerState === true;
12423
+ if (stopNotConfirmed) {
12424
+ 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";
12425
+ process.stderr.write(
12426
+ `Failed to stop run ${runId}: ${detail}. Retry 'deepline runs stop ${runId}' or inspect the run with 'deepline runs get ${runId} --full --json'.
12427
+ `
12428
+ );
12429
+ printCommandEnvelope(
12430
+ {
12431
+ ...result,
12432
+ render: {
12433
+ sections: [
12434
+ {
12435
+ title: "run stop",
12436
+ lines: [
12437
+ `\u2717 stop not confirmed for ${result.runId || runId}`,
12438
+ ` ${detail}`,
12439
+ ` retry: deepline runs stop ${runId}`,
12440
+ ` inspect: deepline runs get ${runId} --full --json`
12441
+ ]
12442
+ }
12443
+ ]
12444
+ }
12445
+ },
12446
+ { json: argsWantJson(args) }
12447
+ );
12448
+ return 1;
12449
+ }
11319
12450
  const lines = [
11320
12451
  `Stopped ${result.runId}`,
11321
12452
  ...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
@@ -14957,7 +16088,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
14957
16088
  }
14958
16089
  function extractionContractEntries(entries) {
14959
16090
  return entries.flatMap((entry) => {
14960
- if (!isRecord5(entry)) return [];
16091
+ if (!isRecord6(entry)) return [];
14961
16092
  const name = stringField(entry, "name");
14962
16093
  const expression = stringField(entry, "expression");
14963
16094
  return name && expression ? [{ name, expression }] : [];
@@ -14965,8 +16096,8 @@ function extractionContractEntries(entries) {
14965
16096
  }
14966
16097
  function printCompactToolContract(tool, requestedToolId) {
14967
16098
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
14968
- const cost = isRecord5(contract.cost) ? contract.cost : {};
14969
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16099
+ const cost = isRecord6(contract.cost) ? contract.cost : {};
16100
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
14970
16101
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
14971
16102
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
14972
16103
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
@@ -14983,7 +16114,7 @@ function printCompactToolContract(tool, requestedToolId) {
14983
16114
  console.log("");
14984
16115
  console.log("Inputs:");
14985
16116
  for (const field of inputFields) {
14986
- if (!isRecord5(field)) continue;
16117
+ if (!isRecord6(field)) continue;
14987
16118
  const name = stringField(field, "name");
14988
16119
  if (!name) continue;
14989
16120
  const required = field.required ? "*" : "";
@@ -14996,7 +16127,7 @@ function printCompactToolContract(tool, requestedToolId) {
14996
16127
  }
14997
16128
  console.log("");
14998
16129
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
14999
- const starterScript = isRecord5(contract.starterScript) ? contract.starterScript : {};
16130
+ const starterScript = isRecord6(contract.starterScript) ? contract.starterScript : {};
15000
16131
  const starterPath = stringField(starterScript, "path");
15001
16132
  if (starterPath) {
15002
16133
  console.log("");
@@ -15010,14 +16141,14 @@ function printCompactToolContract(tool, requestedToolId) {
15010
16141
  console.log("Getters:");
15011
16142
  if (listGetters.length) console.log("Lists:");
15012
16143
  for (const entry of listGetters) {
15013
- if (isRecord5(entry))
16144
+ if (isRecord6(entry))
15014
16145
  console.log(
15015
16146
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15016
16147
  );
15017
16148
  }
15018
16149
  if (valueGetters.length) console.log("Values:");
15019
16150
  for (const entry of valueGetters) {
15020
- if (isRecord5(entry))
16151
+ if (isRecord6(entry))
15021
16152
  console.log(
15022
16153
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15023
16154
  );
@@ -15030,7 +16161,7 @@ function printCompactToolContract(tool, requestedToolId) {
15030
16161
  }
15031
16162
  function printToolPricingOnly(tool, requestedToolId, options = {}) {
15032
16163
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15033
- const cost = isRecord5(contract.cost) ? contract.cost : {};
16164
+ const cost = isRecord6(contract.cost) ? contract.cost : {};
15034
16165
  const pricingModel = stringField(cost, "pricingModel") || "unknown";
15035
16166
  const billingMode = stringField(cost, "billingMode") || "unknown";
15036
16167
  const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
@@ -15050,7 +16181,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
15050
16181
  }
15051
16182
  console.log("Inputs:");
15052
16183
  for (const field of inputFields) {
15053
- if (!isRecord5(field)) continue;
16184
+ if (!isRecord6(field)) continue;
15054
16185
  const name = stringField(field, "name");
15055
16186
  if (!name) continue;
15056
16187
  const required = field.required ? "*" : "";
@@ -15076,10 +16207,10 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
15076
16207
  ` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
15077
16208
  );
15078
16209
  console.log("});");
15079
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16210
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15080
16211
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15081
16212
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15082
- const firstGetter = [...valueGetters, ...listGetters].find(isRecord5);
16213
+ const firstGetter = [...valueGetters, ...listGetters].find(isRecord6);
15083
16214
  if (firstGetter) {
15084
16215
  const name = stringField(firstGetter, "name") || "value";
15085
16216
  const expression = stringField(firstGetter, "expression");
@@ -15096,7 +16227,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
15096
16227
  }
15097
16228
  function printToolGettersOnly(tool, requestedToolId) {
15098
16229
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15099
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16230
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15100
16231
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15101
16232
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15102
16233
  console.log(`Getters: ${contract.toolId}`);
@@ -15109,7 +16240,7 @@ function printToolGettersOnly(tool, requestedToolId) {
15109
16240
  if (listGetters.length) {
15110
16241
  console.log("Lists:");
15111
16242
  for (const entry of listGetters) {
15112
- if (isRecord5(entry))
16243
+ if (isRecord6(entry))
15113
16244
  console.log(
15114
16245
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15115
16246
  );
@@ -15118,7 +16249,7 @@ function printToolGettersOnly(tool, requestedToolId) {
15118
16249
  if (valueGetters.length) {
15119
16250
  console.log("Values:");
15120
16251
  for (const entry of valueGetters) {
15121
- if (isRecord5(entry))
16252
+ if (isRecord6(entry))
15122
16253
  console.log(
15123
16254
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15124
16255
  );
@@ -15144,7 +16275,7 @@ function sampleValueForField(field) {
15144
16275
  function samplePayloadForInputFields(fields) {
15145
16276
  return Object.fromEntries(
15146
16277
  fields.slice(0, 4).flatMap((field) => {
15147
- if (!isRecord5(field)) return [];
16278
+ if (!isRecord6(field)) return [];
15148
16279
  const name = stringField(field, "name");
15149
16280
  if (!name) return [];
15150
16281
  return [[name, sampleValueForField(field)]];
@@ -15258,12 +16389,12 @@ function formatListedToolCost(tool) {
15258
16389
  }
15259
16390
  function toolInputFieldsForDisplay(inputSchema) {
15260
16391
  if (Array.isArray(inputSchema.fields))
15261
- return inputSchema.fields.filter(isRecord5);
15262
- const jsonSchema = isRecord5(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
15263
- const properties = isRecord5(jsonSchema.properties) ? jsonSchema.properties : {};
16392
+ return inputSchema.fields.filter(isRecord6);
16393
+ const jsonSchema = isRecord6(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
16394
+ const properties = isRecord6(jsonSchema.properties) ? jsonSchema.properties : {};
15264
16395
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
15265
16396
  return Object.entries(properties).map(([name, value]) => {
15266
- const property = isRecord5(value) ? value : {};
16397
+ const property = isRecord6(value) ? value : {};
15267
16398
  return {
15268
16399
  name,
15269
16400
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -15292,15 +16423,15 @@ function printJsonPreview(label, payload) {
15292
16423
  }
15293
16424
  function samplePayload(samples, key) {
15294
16425
  const entry = samples[key];
15295
- if (!isRecord5(entry)) return void 0;
16426
+ if (!isRecord6(entry)) return void 0;
15296
16427
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
15297
16428
  }
15298
16429
  function commandEnvelopeFromRawResponse(rawResponse) {
15299
- return isRecord5(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
16430
+ return isRecord6(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
15300
16431
  }
15301
16432
  function listExtractorPathsFromUsageGuidance(tool) {
15302
16433
  const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
15303
- const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord5(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
16434
+ const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord6(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
15304
16435
  return extractedLists.flatMap((entry) => {
15305
16436
  const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
15306
16437
  if (!Array.isArray(paths)) return [];
@@ -15316,7 +16447,7 @@ function formatDecimal(value) {
15316
16447
  function formatUsd(value) {
15317
16448
  return `$${formatDecimal(value)}`;
15318
16449
  }
15319
- function isRecord5(value) {
16450
+ function isRecord6(value) {
15320
16451
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
15321
16452
  }
15322
16453
  function stringField(source, ...keys) {
@@ -15343,7 +16474,7 @@ function arrayField(source, ...keys) {
15343
16474
  function recordField(source, ...keys) {
15344
16475
  for (const key of keys) {
15345
16476
  const value = source[key];
15346
- if (isRecord5(value)) return value;
16477
+ if (isRecord6(value)) return value;
15347
16478
  }
15348
16479
  return {};
15349
16480
  }
@@ -15409,7 +16540,7 @@ function parseJsonObjectArgument(raw, flagName) {
15409
16540
  }
15410
16541
  throw invalidJsonError(flagName, message);
15411
16542
  }
15412
- if (!isRecord5(parsed)) {
16543
+ if (!isRecord6(parsed)) {
15413
16544
  throw invalidJsonError(flagName, "expected an object.");
15414
16545
  }
15415
16546
  return parsed;
@@ -15532,7 +16663,7 @@ function buildToolExecuteBaseEnvelope(input2) {
15532
16663
  kind: summaryEntries.length > 0 ? "object" : "raw",
15533
16664
  summary: input2.summary
15534
16665
  };
15535
- const envelopeHasCanonicalOutput = isRecord5(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
16666
+ const envelopeHasCanonicalOutput = isRecord6(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
15536
16667
  const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
15537
16668
  const actions = input2.listConversion ? [
15538
16669
  {
@@ -15641,7 +16772,7 @@ async function executeTool(args) {
15641
16772
  {
15642
16773
  ...baseEnvelope,
15643
16774
  local: {
15644
- ...isRecord5(baseEnvelope.local) ? baseEnvelope.local : {},
16775
+ ...isRecord6(baseEnvelope.local) ? baseEnvelope.local : {},
15645
16776
  payload_file: jsonPath
15646
16777
  }
15647
16778
  },