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.
@@ -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.90",
209
+ version: "0.1.93",
210
210
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.90",
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
  }
@@ -9929,18 +10998,33 @@ function writePlayResult(status, jsonOutput, options) {
9929
10998
  ` }
9930
10999
  );
9931
11000
  }
11001
+ function playRunPackageStepCount(pkg) {
11002
+ if (!pkg) return 0;
11003
+ const steps = pkg.steps;
11004
+ return Array.isArray(steps) ? steps.length : 0;
11005
+ }
9932
11006
  async function resolvePlayRunOutputStatus(input2) {
9933
- if (!input2.fullJson || !getPlayRunPackage(input2.status)) {
9934
- return input2.status;
9935
- }
9936
11007
  const runId = input2.status.runId;
9937
11008
  if (!runId) {
9938
11009
  return input2.status;
9939
11010
  }
9940
- const refreshedStatus = await input2.client.getPlayStatus(runId, {
11011
+ const streamedPackage = getPlayRunPackage(input2.status);
11012
+ const refreshForFullJson = input2.fullJson && streamedPackage !== null;
11013
+ const streamedTextPackageIncomplete = !input2.jsonOutput && input2.status.status === "completed" && playRunPackageStepCount(streamedPackage) === 0;
11014
+ if (!refreshForFullJson && !streamedTextPackageIncomplete) {
11015
+ return input2.status;
11016
+ }
11017
+ let refreshedStatus = await input2.client.getPlayStatus(runId, {
9941
11018
  billing: false,
9942
11019
  full: input2.fullJson
9943
11020
  });
11021
+ for (let attempt = 0; attempt < 3 && streamedTextPackageIncomplete && refreshedStatus.status === "completed" && playRunPackageStepCount(getPlayRunPackage(refreshedStatus)) === 0; attempt += 1) {
11022
+ await sleep4(250);
11023
+ refreshedStatus = await input2.client.getPlayStatus(runId, {
11024
+ billing: false,
11025
+ full: input2.fullJson
11026
+ });
11027
+ }
9944
11028
  const dashboardUrl = input2.status.dashboardUrl;
9945
11029
  return typeof dashboardUrl === "string" ? { ...refreshedStatus, dashboardUrl } : refreshedStatus;
9946
11030
  }
@@ -10194,7 +11278,7 @@ function extractPlayValidationErrors(value) {
10194
11278
  if (value instanceof DeeplineError) {
10195
11279
  return extractPlayValidationErrors(value.details);
10196
11280
  }
10197
- if (!isRecord4(value)) {
11281
+ if (!isRecord5(value)) {
10198
11282
  return [];
10199
11283
  }
10200
11284
  const directErrors = stringArrayField(value, "errors");
@@ -10552,7 +11636,7 @@ function shouldUseLocalOnlyPlayCheck() {
10552
11636
  const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
10553
11637
  return value === "1" || value === "true" || value === "yes" || value === "on";
10554
11638
  }
10555
- function isRecord4(value) {
11639
+ function isRecord5(value) {
10556
11640
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
10557
11641
  }
10558
11642
  function stringValue2(value) {
@@ -10562,14 +11646,14 @@ function asArray(value) {
10562
11646
  return Array.isArray(value) ? value : [];
10563
11647
  }
10564
11648
  function extractionEntries2(value) {
10565
- if (Array.isArray(value)) return value.filter(isRecord4);
10566
- if (!isRecord4(value)) return [];
11649
+ if (Array.isArray(value)) return value.filter(isRecord5);
11650
+ if (!isRecord5(value)) return [];
10567
11651
  return Object.entries(value).map(
10568
- ([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
11652
+ ([name, entry]) => isRecord5(entry) ? { name, ...entry } : { name }
10569
11653
  );
10570
11654
  }
10571
11655
  function firstRawPath(entry) {
10572
- const details = isRecord4(entry.details) ? entry.details : {};
11656
+ const details = isRecord5(entry.details) ? entry.details : {};
10573
11657
  const paths = [
10574
11658
  ...asArray(details.rawToolOutputPaths),
10575
11659
  ...asArray(details.raw_tool_output_paths),
@@ -10587,12 +11671,12 @@ function checkHintRawPath(value) {
10587
11671
  function collectStaticPipelineToolIds(staticPipeline) {
10588
11672
  const seen = /* @__PURE__ */ new Set();
10589
11673
  const visitPipeline = (pipeline) => {
10590
- if (!isRecord4(pipeline)) return;
11674
+ if (!isRecord5(pipeline)) return;
10591
11675
  for (const step of [
10592
11676
  ...asArray(pipeline.stages),
10593
11677
  ...asArray(pipeline.substeps)
10594
11678
  ]) {
10595
- if (!isRecord4(step)) continue;
11679
+ if (!isRecord5(step)) continue;
10596
11680
  if (step.type === "tool") {
10597
11681
  const toolId = stringValue2(step.toolId) || stringValue2(step.tool);
10598
11682
  if (toolId) seen.add(toolId);
@@ -10606,9 +11690,9 @@ function collectStaticPipelineToolIds(staticPipeline) {
10606
11690
  return [...seen].sort();
10607
11691
  }
10608
11692
  function toolGetterHintFromMetadata(toolId, tool) {
10609
- const usageGuidance = isRecord4(tool.usageGuidance) ? tool.usageGuidance : {};
10610
- const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
10611
- 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 : {};
10612
11696
  const lists = extractionEntries2(
10613
11697
  resultGuidance.extractedLists ?? resultGuidance.extracted_lists
10614
11698
  ).map((entry) => ({
@@ -10904,7 +11988,8 @@ async function handleFileBackedRun(options) {
10904
11988
  await resolvePlayRunOutputStatus({
10905
11989
  client,
10906
11990
  status: finalStatus,
10907
- fullJson: options.fullJson
11991
+ fullJson: options.fullJson,
11992
+ jsonOutput: options.jsonOutput
10908
11993
  }),
10909
11994
  playName
10910
11995
  );
@@ -11054,7 +12139,8 @@ async function handleNamedRun(options) {
11054
12139
  await resolvePlayRunOutputStatus({
11055
12140
  client,
11056
12141
  status: finalStatus,
11057
- fullJson: options.fullJson
12142
+ fullJson: options.fullJson,
12143
+ jsonOutput: options.jsonOutput
11058
12144
  }),
11059
12145
  playName
11060
12146
  );
@@ -11235,8 +12321,17 @@ async function handleRunTail(args) {
11235
12321
  }
11236
12322
  }
11237
12323
  const client = new DeeplineClient();
11238
- const status = await client.runs.tail(runId);
11239
- 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);
11240
12335
  return status.status === "failed" ? 1 : 0;
11241
12336
  }
11242
12337
  async function handleRunLogs(args) {
@@ -11261,21 +12356,28 @@ async function handleRunLogs(args) {
11261
12356
  }
11262
12357
  }
11263
12358
  const client = new DeeplineClient();
11264
- const status = await client.runs.get(runId, { full: true });
11265
- const logs = status.progress?.logs ?? [];
11266
12359
  if (outPath) {
12360
+ const result2 = await client.runs.logs(runId, { all: true });
12361
+ const logs = result2.entries;
11267
12362
  writeFileSync7(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
11268
12363
  printCommandEnvelope(
11269
12364
  {
11270
- runId: status.runId,
12365
+ runId: result2.runId,
11271
12366
  log_path: outPath,
11272
12367
  lineCount: logs.length,
12368
+ totalCount: result2.totalCount,
12369
+ ...result2.logsTruncated ? { logsTruncated: true } : {},
11273
12370
  local: { log_path: outPath },
11274
12371
  render: {
11275
12372
  sections: [
11276
12373
  {
11277
12374
  title: "run logs",
11278
- 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
+ ]
11279
12381
  }
11280
12382
  ]
11281
12383
  }
@@ -11284,25 +12386,26 @@ async function handleRunLogs(args) {
11284
12386
  );
11285
12387
  return 0;
11286
12388
  }
11287
- const entries = logs.slice(Math.max(0, logs.length - limit));
12389
+ const result = await client.runs.logs(runId, { limit });
11288
12390
  printCommandEnvelope(
11289
12391
  {
11290
- runId: status.runId,
11291
- totalCount: logs.length,
11292
- returnedCount: entries.length,
11293
- firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
11294
- lastSequence: logs.length === 0 ? null : logs.length,
11295
- truncated: logs.length > entries.length,
11296
- hasMore: logs.length > entries.length,
11297
- 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,
11298
12401
  next: {
11299
- export: `deepline runs logs ${status.runId} --out run.log --json`
12402
+ export: `deepline runs logs ${result.runId} --out run.log --json`
11300
12403
  },
11301
- render: { sections: [{ title: "run logs", lines: entries }] }
12404
+ render: { sections: [{ title: "run logs", lines: result.entries }] }
11302
12405
  },
11303
12406
  {
11304
12407
  json: argsWantJson(args),
11305
- text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`
12408
+ text: `${result.entries.join("\n")}${result.entries.length > 0 ? "\n" : ""}`
11306
12409
  }
11307
12410
  );
11308
12411
  return 0;
@@ -11325,6 +12428,34 @@ async function handleRunStop(args) {
11325
12428
  }
11326
12429
  const client = new DeeplineClient();
11327
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
+ }
11328
12459
  const lines = [
11329
12460
  `Stopped ${result.runId}`,
11330
12461
  ...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
@@ -14972,7 +16103,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
14972
16103
  }
14973
16104
  function extractionContractEntries(entries) {
14974
16105
  return entries.flatMap((entry) => {
14975
- if (!isRecord5(entry)) return [];
16106
+ if (!isRecord6(entry)) return [];
14976
16107
  const name = stringField(entry, "name");
14977
16108
  const expression = stringField(entry, "expression");
14978
16109
  return name && expression ? [{ name, expression }] : [];
@@ -14980,8 +16111,8 @@ function extractionContractEntries(entries) {
14980
16111
  }
14981
16112
  function printCompactToolContract(tool, requestedToolId) {
14982
16113
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
14983
- const cost = isRecord5(contract.cost) ? contract.cost : {};
14984
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16114
+ const cost = isRecord6(contract.cost) ? contract.cost : {};
16115
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
14985
16116
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
14986
16117
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
14987
16118
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
@@ -14998,7 +16129,7 @@ function printCompactToolContract(tool, requestedToolId) {
14998
16129
  console.log("");
14999
16130
  console.log("Inputs:");
15000
16131
  for (const field of inputFields) {
15001
- if (!isRecord5(field)) continue;
16132
+ if (!isRecord6(field)) continue;
15002
16133
  const name = stringField(field, "name");
15003
16134
  if (!name) continue;
15004
16135
  const required = field.required ? "*" : "";
@@ -15011,7 +16142,7 @@ function printCompactToolContract(tool, requestedToolId) {
15011
16142
  }
15012
16143
  console.log("");
15013
16144
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
15014
- const starterScript = isRecord5(contract.starterScript) ? contract.starterScript : {};
16145
+ const starterScript = isRecord6(contract.starterScript) ? contract.starterScript : {};
15015
16146
  const starterPath = stringField(starterScript, "path");
15016
16147
  if (starterPath) {
15017
16148
  console.log("");
@@ -15025,14 +16156,14 @@ function printCompactToolContract(tool, requestedToolId) {
15025
16156
  console.log("Getters:");
15026
16157
  if (listGetters.length) console.log("Lists:");
15027
16158
  for (const entry of listGetters) {
15028
- if (isRecord5(entry))
16159
+ if (isRecord6(entry))
15029
16160
  console.log(
15030
16161
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15031
16162
  );
15032
16163
  }
15033
16164
  if (valueGetters.length) console.log("Values:");
15034
16165
  for (const entry of valueGetters) {
15035
- if (isRecord5(entry))
16166
+ if (isRecord6(entry))
15036
16167
  console.log(
15037
16168
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15038
16169
  );
@@ -15045,7 +16176,7 @@ function printCompactToolContract(tool, requestedToolId) {
15045
16176
  }
15046
16177
  function printToolPricingOnly(tool, requestedToolId, options = {}) {
15047
16178
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15048
- const cost = isRecord5(contract.cost) ? contract.cost : {};
16179
+ const cost = isRecord6(contract.cost) ? contract.cost : {};
15049
16180
  const pricingModel = stringField(cost, "pricingModel") || "unknown";
15050
16181
  const billingMode = stringField(cost, "billingMode") || "unknown";
15051
16182
  const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
@@ -15065,7 +16196,7 @@ function printToolSchemaOnly(tool, requestedToolId) {
15065
16196
  }
15066
16197
  console.log("Inputs:");
15067
16198
  for (const field of inputFields) {
15068
- if (!isRecord5(field)) continue;
16199
+ if (!isRecord6(field)) continue;
15069
16200
  const name = stringField(field, "name");
15070
16201
  if (!name) continue;
15071
16202
  const required = field.required ? "*" : "";
@@ -15091,10 +16222,10 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
15091
16222
  ` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
15092
16223
  );
15093
16224
  console.log("});");
15094
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16225
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15095
16226
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15096
16227
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15097
- const firstGetter = [...valueGetters, ...listGetters].find(isRecord5);
16228
+ const firstGetter = [...valueGetters, ...listGetters].find(isRecord6);
15098
16229
  if (firstGetter) {
15099
16230
  const name = stringField(firstGetter, "name") || "value";
15100
16231
  const expression = stringField(firstGetter, "expression");
@@ -15111,7 +16242,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
15111
16242
  }
15112
16243
  function printToolGettersOnly(tool, requestedToolId) {
15113
16244
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
15114
- const getters = isRecord5(contract.getters) ? contract.getters : {};
16245
+ const getters = isRecord6(contract.getters) ? contract.getters : {};
15115
16246
  const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
15116
16247
  const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
15117
16248
  console.log(`Getters: ${contract.toolId}`);
@@ -15124,7 +16255,7 @@ function printToolGettersOnly(tool, requestedToolId) {
15124
16255
  if (listGetters.length) {
15125
16256
  console.log("Lists:");
15126
16257
  for (const entry of listGetters) {
15127
- if (isRecord5(entry))
16258
+ if (isRecord6(entry))
15128
16259
  console.log(
15129
16260
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15130
16261
  );
@@ -15133,7 +16264,7 @@ function printToolGettersOnly(tool, requestedToolId) {
15133
16264
  if (valueGetters.length) {
15134
16265
  console.log("Values:");
15135
16266
  for (const entry of valueGetters) {
15136
- if (isRecord5(entry))
16267
+ if (isRecord6(entry))
15137
16268
  console.log(
15138
16269
  `- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
15139
16270
  );
@@ -15159,7 +16290,7 @@ function sampleValueForField(field) {
15159
16290
  function samplePayloadForInputFields(fields) {
15160
16291
  return Object.fromEntries(
15161
16292
  fields.slice(0, 4).flatMap((field) => {
15162
- if (!isRecord5(field)) return [];
16293
+ if (!isRecord6(field)) return [];
15163
16294
  const name = stringField(field, "name");
15164
16295
  if (!name) return [];
15165
16296
  return [[name, sampleValueForField(field)]];
@@ -15273,12 +16404,12 @@ function formatListedToolCost(tool) {
15273
16404
  }
15274
16405
  function toolInputFieldsForDisplay(inputSchema) {
15275
16406
  if (Array.isArray(inputSchema.fields))
15276
- return inputSchema.fields.filter(isRecord5);
15277
- const jsonSchema = isRecord5(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
15278
- 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 : {};
15279
16410
  const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
15280
16411
  return Object.entries(properties).map(([name, value]) => {
15281
- const property = isRecord5(value) ? value : {};
16412
+ const property = isRecord6(value) ? value : {};
15282
16413
  return {
15283
16414
  name,
15284
16415
  type: typeof property.type === "string" ? property.type : "unknown",
@@ -15307,15 +16438,15 @@ function printJsonPreview(label, payload) {
15307
16438
  }
15308
16439
  function samplePayload(samples, key) {
15309
16440
  const entry = samples[key];
15310
- if (!isRecord5(entry)) return void 0;
16441
+ if (!isRecord6(entry)) return void 0;
15311
16442
  return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
15312
16443
  }
15313
16444
  function commandEnvelopeFromRawResponse(rawResponse) {
15314
- return isRecord5(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
16445
+ return isRecord6(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
15315
16446
  }
15316
16447
  function listExtractorPathsFromUsageGuidance(tool) {
15317
16448
  const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
15318
- 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) : [];
15319
16450
  return extractedLists.flatMap((entry) => {
15320
16451
  const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
15321
16452
  if (!Array.isArray(paths)) return [];
@@ -15331,7 +16462,7 @@ function formatDecimal(value) {
15331
16462
  function formatUsd(value) {
15332
16463
  return `$${formatDecimal(value)}`;
15333
16464
  }
15334
- function isRecord5(value) {
16465
+ function isRecord6(value) {
15335
16466
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
15336
16467
  }
15337
16468
  function stringField(source, ...keys) {
@@ -15358,7 +16489,7 @@ function arrayField(source, ...keys) {
15358
16489
  function recordField(source, ...keys) {
15359
16490
  for (const key of keys) {
15360
16491
  const value = source[key];
15361
- if (isRecord5(value)) return value;
16492
+ if (isRecord6(value)) return value;
15362
16493
  }
15363
16494
  return {};
15364
16495
  }
@@ -15424,7 +16555,7 @@ function parseJsonObjectArgument(raw, flagName) {
15424
16555
  }
15425
16556
  throw invalidJsonError(flagName, message);
15426
16557
  }
15427
- if (!isRecord5(parsed)) {
16558
+ if (!isRecord6(parsed)) {
15428
16559
  throw invalidJsonError(flagName, "expected an object.");
15429
16560
  }
15430
16561
  return parsed;
@@ -15547,7 +16678,7 @@ function buildToolExecuteBaseEnvelope(input2) {
15547
16678
  kind: summaryEntries.length > 0 ? "object" : "raw",
15548
16679
  summary: input2.summary
15549
16680
  };
15550
- 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");
15551
16682
  const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote(JSON.stringify(input2.params))} --json`;
15552
16683
  const actions = input2.listConversion ? [
15553
16684
  {
@@ -15656,7 +16787,7 @@ async function executeTool(args) {
15656
16787
  {
15657
16788
  ...baseEnvelope,
15658
16789
  local: {
15659
- ...isRecord5(baseEnvelope.local) ? baseEnvelope.local : {},
16790
+ ...isRecord6(baseEnvelope.local) ? baseEnvelope.local : {},
15660
16791
  payload_file: jsonPath
15661
16792
  }
15662
16793
  },