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/index.mjs CHANGED
@@ -179,10 +179,10 @@ import { join as join2 } from "path";
179
179
 
180
180
  // src/release.ts
181
181
  var SDK_RELEASE = {
182
- version: "0.1.90",
182
+ version: "0.1.93",
183
183
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
184
184
  supportPolicy: {
185
- latest: "0.1.90",
185
+ latest: "0.1.93",
186
186
  minimumSupported: "0.1.53",
187
187
  deprecatedBelow: "0.1.53"
188
188
  }
@@ -537,6 +537,807 @@ function sleep(ms) {
537
537
  return new Promise((resolve2) => setTimeout(resolve2, ms));
538
538
  }
539
539
 
540
+ // src/stream-reconnect.ts
541
+ var STREAM_RECONNECT_BASE_DELAY_MS = 500;
542
+ var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
543
+ var STREAM_HEALTHY_CONNECTION_MS = 3e4;
544
+ function streamReconnectDelayMs(attempt) {
545
+ const cappedExponentialMs = Math.min(
546
+ STREAM_RECONNECT_MAX_DELAY_MS,
547
+ STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt)
548
+ );
549
+ return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
550
+ }
551
+ function isTransientPlayStreamError(error) {
552
+ if (error instanceof DeeplineError && typeof error.statusCode === "number") {
553
+ return error.statusCode >= 500 && error.statusCode < 600;
554
+ }
555
+ const text = error instanceof Error ? error.message : String(error);
556
+ return /auth validation backend timed out|fetch failed|eaddrnotavail|econnreset|etimedout|eai_again|socket hang up/i.test(
557
+ text
558
+ );
559
+ }
560
+
561
+ // ../shared_libs/play-runtime/live-events.ts
562
+ function resolveTimingWindow(input) {
563
+ const startedAt = typeof input.startedAt === "number" && Number.isFinite(input.startedAt) ? input.startedAt : null;
564
+ const completedAt = typeof input.completedAt === "number" && Number.isFinite(input.completedAt) ? input.completedAt : null;
565
+ const updatedAt = typeof input.updatedAt === "number" && Number.isFinite(input.updatedAt) ? input.updatedAt : null;
566
+ return {
567
+ startedAt,
568
+ completedAt,
569
+ updatedAt
570
+ };
571
+ }
572
+
573
+ // ../shared_libs/play-runtime/run-ledger.ts
574
+ var LOG_TAIL_LIMIT = 100;
575
+ function isRecord(value) {
576
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
577
+ }
578
+ function finiteNumber(value) {
579
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
580
+ }
581
+ function optionalFiniteNumber(value) {
582
+ const normalized = finiteNumber(value);
583
+ return normalized === null ? void 0 : normalized;
584
+ }
585
+ function optionalString(value) {
586
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
587
+ }
588
+ function optionalNullableString(value) {
589
+ if (value === null) return null;
590
+ return optionalString(value);
591
+ }
592
+ function normalizePlayRunLedgerStatus(value) {
593
+ const normalized = String(value ?? "").trim().toLowerCase();
594
+ switch (normalized) {
595
+ case "queued":
596
+ case "pending":
597
+ return "queued";
598
+ case "running":
599
+ case "started":
600
+ return "running";
601
+ case "waiting":
602
+ return "waiting";
603
+ case "completed":
604
+ case "complete":
605
+ case "succeeded":
606
+ return "completed";
607
+ case "failed":
608
+ case "error":
609
+ return "failed";
610
+ case "cancelled":
611
+ case "canceled":
612
+ return "cancelled";
613
+ case "terminated":
614
+ return "terminated";
615
+ case "timed_out":
616
+ case "timeout":
617
+ return "timed_out";
618
+ default:
619
+ return "unknown";
620
+ }
621
+ }
622
+ function createEmptyPlayRunLedgerSnapshot(input) {
623
+ const status = normalizePlayRunLedgerStatus(input.status ?? "unknown");
624
+ const startedAt = finiteNumber(input.startedAt) ?? null;
625
+ const finishedAt = finiteNumber(input.finishedAt) ?? null;
626
+ return {
627
+ runId: input.runId,
628
+ playName: input.playName ?? null,
629
+ status,
630
+ error: optionalNullableString(input.error) ?? null,
631
+ createdAt: finiteNumber(input.createdAt) ?? null,
632
+ startedAt,
633
+ updatedAt: finiteNumber(input.updatedAt) ?? finishedAt ?? startedAt ?? finiteNumber(input.createdAt) ?? null,
634
+ finishedAt,
635
+ durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : null,
636
+ orderedStepIds: [],
637
+ stepsById: {},
638
+ logTail: [],
639
+ totalLogCount: 0,
640
+ logsTruncated: false,
641
+ activeStepId: null,
642
+ activeArtifactTableNamespace: null,
643
+ resultTableNamespace: null
644
+ };
645
+ }
646
+ function normalizePlayRunLedgerSnapshot(value, fallback) {
647
+ if (!isRecord(value)) {
648
+ return createEmptyPlayRunLedgerSnapshot(fallback);
649
+ }
650
+ const orderedStepIds = Array.isArray(value.orderedStepIds) ? value.orderedStepIds.filter(
651
+ (entry) => typeof entry === "string" && Boolean(entry.trim())
652
+ ) : [];
653
+ const rawSteps = isRecord(value.stepsById) ? value.stepsById : {};
654
+ const stepsById = {};
655
+ for (const [stepId, rawStep] of Object.entries(rawSteps)) {
656
+ if (!stepId.trim() || !isRecord(rawStep)) continue;
657
+ const rawStatus = normalizeStepStatus(rawStep.status);
658
+ if (!rawStatus) continue;
659
+ const completedAt = finiteNumber(rawStep.completedAt);
660
+ const status = rawStatus === "running" && completedAt !== null ? "completed" : rawStatus;
661
+ const rawProgress = isRecord(rawStep.progress) ? rawStep.progress : null;
662
+ stepsById[stepId] = {
663
+ stepId,
664
+ label: optionalString(rawStep.label),
665
+ kind: optionalString(rawStep.kind),
666
+ status,
667
+ artifactTableNamespace: optionalNullableString(
668
+ rawStep.artifactTableNamespace
669
+ ),
670
+ startedAt: finiteNumber(rawStep.startedAt),
671
+ completedAt,
672
+ updatedAt: finiteNumber(rawStep.updatedAt),
673
+ progress: rawProgress ? normalizeStepProgress(rawProgress) : null
674
+ };
675
+ }
676
+ const createdAt = finiteNumber(value.createdAt) ?? fallback.createdAt ?? null;
677
+ const startedAt = finiteNumber(value.startedAt) ?? fallback.startedAt ?? null;
678
+ const finishedAt = finiteNumber(value.finishedAt) ?? fallback.finishedAt ?? null;
679
+ const updatedAt = finiteNumber(value.updatedAt) ?? fallback.updatedAt ?? finishedAt ?? startedAt ?? createdAt ?? null;
680
+ const error = Object.prototype.hasOwnProperty.call(value, "error") ? optionalNullableString(value.error) ?? null : fallback.error ?? null;
681
+ const rawTail = Array.isArray(value.logTail) ? value.logTail : value.logs;
682
+ const logTail = Array.isArray(rawTail) ? rawTail.filter((line) => typeof line === "string") : [];
683
+ return {
684
+ runId: optionalString(value.runId) ?? fallback.runId,
685
+ playName: optionalNullableString(value.playName) ?? fallback.playName ?? null,
686
+ status: normalizePlayRunLedgerStatus(value.status ?? fallback.status),
687
+ error,
688
+ createdAt,
689
+ startedAt,
690
+ updatedAt,
691
+ finishedAt,
692
+ durationMs: startedAt !== null && finishedAt !== null ? Math.max(0, finishedAt - startedAt) : finiteNumber(value.durationMs),
693
+ orderedStepIds: orderedStepIds.filter((stepId) => stepsById[stepId]),
694
+ stepsById,
695
+ logTail: logTail.slice(-LOG_TAIL_LIMIT),
696
+ // Snapshots persisted before totalLogCount existed only know the retained
697
+ // tail, so the best lower bound for the cumulative count is the tail size.
698
+ totalLogCount: Math.max(
699
+ finiteNumber(value.totalLogCount) ?? logTail.length,
700
+ logTail.length
701
+ ),
702
+ logsTruncated: value.logsTruncated === true,
703
+ activeStepId: optionalNullableString(value.activeStepId),
704
+ activeArtifactTableNamespace: optionalNullableString(
705
+ value.activeArtifactTableNamespace
706
+ ),
707
+ resultTableNamespace: optionalNullableString(value.resultTableNamespace),
708
+ resultSummary: value.resultSummary,
709
+ result: value.result
710
+ };
711
+ }
712
+ function normalizeStepStatus(value) {
713
+ const normalized = String(value ?? "").trim().toLowerCase();
714
+ if (normalized === "running" || normalized === "completed" || normalized === "failed" || normalized === "skipped") {
715
+ return normalized;
716
+ }
717
+ return null;
718
+ }
719
+ function normalizeStepProgress(value) {
720
+ return {
721
+ ...optionalFiniteNumber(value.completed) !== void 0 ? { completed: optionalFiniteNumber(value.completed) } : {},
722
+ ...optionalFiniteNumber(value.total) !== void 0 ? { total: optionalFiniteNumber(value.total) } : {},
723
+ ...optionalFiniteNumber(value.failed) !== void 0 ? { failed: optionalFiniteNumber(value.failed) } : {},
724
+ ...optionalString(value.message) ? { message: optionalString(value.message) } : {},
725
+ ...optionalNullableString(value.artifactTableNamespace) !== void 0 ? {
726
+ artifactTableNamespace: optionalNullableString(
727
+ value.artifactTableNamespace
728
+ )
729
+ } : {},
730
+ ...finiteNumber(value.updatedAt) !== null ? { updatedAt: finiteNumber(value.updatedAt) } : {}
731
+ };
732
+ }
733
+
734
+ // ../shared_libs/play-runtime/run-snapshot-stream.ts
735
+ function normalizePlayRunLiveStatus(value) {
736
+ const normalized = String(value ?? "").trim().toLowerCase();
737
+ switch (normalized) {
738
+ case "queued":
739
+ case "pending":
740
+ return "running";
741
+ case "running":
742
+ case "started":
743
+ return "running";
744
+ case "completed":
745
+ case "complete":
746
+ case "succeeded":
747
+ return "completed";
748
+ case "failed":
749
+ case "error":
750
+ return "failed";
751
+ case "cancelled":
752
+ case "canceled":
753
+ return "cancelled";
754
+ case "terminated":
755
+ return "terminated";
756
+ case "timed_out":
757
+ case "timeout":
758
+ return "timed_out";
759
+ default:
760
+ return "unknown";
761
+ }
762
+ }
763
+ function isTerminalPlayRunLiveStatus(status) {
764
+ return status === "completed" || status === "failed" || status === "cancelled" || status === "terminated" || status === "timed_out";
765
+ }
766
+ function buildSnapshotFromLedger(snapshot) {
767
+ const nodeStates = snapshot.orderedStepIds.map((stepId) => snapshot.stepsById[stepId]).filter((step) => Boolean(step)).map((step) => ({
768
+ nodeId: step.stepId,
769
+ status: step.status,
770
+ artifactTableNamespace: step.artifactTableNamespace ?? null,
771
+ progress: step.progress ? {
772
+ completed: step.progress.completed,
773
+ total: step.progress.total,
774
+ failed: step.progress.failed,
775
+ message: step.progress.message,
776
+ artifactTableNamespace: step.progress.artifactTableNamespace ?? step.artifactTableNamespace ?? null,
777
+ startedAt: step.startedAt ?? null,
778
+ completedAt: step.completedAt ?? null,
779
+ updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null
780
+ } : null,
781
+ startedAt: step.startedAt ?? null,
782
+ completedAt: step.completedAt ?? null,
783
+ updatedAt: step.updatedAt ?? null
784
+ }));
785
+ return {
786
+ runId: snapshot.runId,
787
+ status: normalizePlayRunLiveStatus(snapshot.status),
788
+ updatedAt: snapshot.updatedAt ?? snapshot.finishedAt ?? snapshot.startedAt ?? null,
789
+ logs: snapshot.logTail,
790
+ totalLogCount: snapshot.totalLogCount,
791
+ ...snapshot.logsTruncated ? { logsTruncated: true } : {},
792
+ activeArtifactTableNamespace: snapshot.activeArtifactTableNamespace ?? null,
793
+ resultTableNamespace: snapshot.resultTableNamespace ?? null,
794
+ nodeStates,
795
+ activeNodeId: snapshot.activeStepId ?? null
796
+ };
797
+ }
798
+ function buildPlayRunStatusSnapshot(input) {
799
+ const ledgerSnapshot = normalizePlayRunLedgerSnapshot(input.run.runSnapshot, {
800
+ runId: input.run.workflowId,
801
+ playName: input.run.name ?? null,
802
+ status: input.run.status,
803
+ createdAt: input.run.createdAt ?? null,
804
+ startedAt: input.run.startedAt ?? null,
805
+ updatedAt: input.run.updatedAt ?? null,
806
+ finishedAt: input.run.finishedAt ?? null
807
+ });
808
+ return buildSnapshotFromLedger(ledgerSnapshot);
809
+ }
810
+ function makeRunStreamEvent(input) {
811
+ return {
812
+ ...input,
813
+ scope: "play",
814
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString()
815
+ };
816
+ }
817
+ var EMPTY_PLAY_RUN_STREAM_DIFF_STATE = {
818
+ runSignature: "",
819
+ snapshotSignature: "",
820
+ stepStatusSignature: "",
821
+ stepProgressSignature: "",
822
+ lastLogSeq: 0
823
+ };
824
+ function getSnapshotCursor(snapshot) {
825
+ return String(snapshot.updatedAt ?? Date.now());
826
+ }
827
+ function getRunSignature(snapshot) {
828
+ return [snapshot.runId, snapshot.status, snapshot.updatedAt ?? 0].join(":");
829
+ }
830
+ function getStepStatusSignature(snapshot) {
831
+ return snapshot.nodeStates.map(
832
+ (state) => [
833
+ state.nodeId,
834
+ state.status,
835
+ state.artifactTableNamespace ?? "",
836
+ state.startedAt ?? "",
837
+ state.completedAt ?? "",
838
+ state.progress?.startedAt ?? "",
839
+ state.progress?.completedAt ?? "",
840
+ state.updatedAt ?? "",
841
+ state.progress?.updatedAt ?? ""
842
+ ].join(":")
843
+ ).join("|");
844
+ }
845
+ function getStepProgressSignature(snapshot) {
846
+ return snapshot.nodeStates.map(
847
+ (state) => [
848
+ state.nodeId,
849
+ state.progress?.completed ?? "",
850
+ state.progress?.total ?? "",
851
+ state.progress?.failed ?? "",
852
+ state.progress?.artifactTableNamespace ?? "",
853
+ state.progress?.startedAt ?? "",
854
+ state.progress?.completedAt ?? "",
855
+ state.progress?.updatedAt ?? "",
856
+ state.progress?.message ?? ""
857
+ ].join(":")
858
+ ).join("|");
859
+ }
860
+ function getSnapshotSignature(snapshot) {
861
+ return JSON.stringify(snapshot);
862
+ }
863
+ function resolvePlayRunLogGap(snapshot, lastLogSeq) {
864
+ if (snapshot.totalLogCount <= lastLogSeq) {
865
+ return null;
866
+ }
867
+ const tailFirstSeq = snapshot.totalLogCount - snapshot.logs.length + 1;
868
+ if (lastLogSeq + 1 >= tailFirstSeq) {
869
+ return null;
870
+ }
871
+ return {
872
+ missingCount: tailFirstSeq - 1 - lastLogSeq,
873
+ tailFirstSeq
874
+ };
875
+ }
876
+ function diffLogLines(input) {
877
+ const { logs, totalLogCount } = input.snapshot;
878
+ if (totalLogCount <= input.lastLogSeq) {
879
+ return { lines: [], lastLogSeq: input.lastLogSeq, firstSeq: null };
880
+ }
881
+ const tailFirstSeq = totalLogCount - logs.length + 1;
882
+ if (input.lastLogSeq + 1 >= tailFirstSeq) {
883
+ return {
884
+ lines: logs.slice(input.lastLogSeq + 1 - tailFirstSeq),
885
+ lastLogSeq: totalLogCount,
886
+ firstSeq: input.lastLogSeq + 1
887
+ };
888
+ }
889
+ const missingCount = tailFirstSeq - 1 - input.lastLogSeq;
890
+ return {
891
+ lines: [
892
+ `[stream] ${missingCount} log lines not retained in the live window; full logs via runs logs`,
893
+ ...logs
894
+ ],
895
+ lastLogSeq: totalLogCount,
896
+ firstSeq: null
897
+ };
898
+ }
899
+ function diffPlayRunStreamEvents(input) {
900
+ const { snapshot, streamId, previous } = input;
901
+ const cursor = getSnapshotCursor(snapshot);
902
+ const logDiff = diffLogLines({
903
+ snapshot,
904
+ lastLogSeq: previous.lastLogSeq
905
+ });
906
+ const next = {
907
+ runSignature: getRunSignature(snapshot),
908
+ stepStatusSignature: getStepStatusSignature(snapshot),
909
+ stepProgressSignature: getStepProgressSignature(snapshot),
910
+ snapshotSignature: getSnapshotSignature(snapshot),
911
+ lastLogSeq: logDiff.lastLogSeq
912
+ };
913
+ const events = [];
914
+ if (next.stepStatusSignature !== previous.stepStatusSignature) {
915
+ for (const state of snapshot.nodeStates) {
916
+ if (state.status === "idle") {
917
+ continue;
918
+ }
919
+ const persistedStartedAt = state.startedAt ?? state.progress?.startedAt ?? null;
920
+ const persistedCompletedAt = state.completedAt ?? state.progress?.completedAt ?? null;
921
+ events.push(
922
+ makeRunStreamEvent({
923
+ cursor,
924
+ streamId,
925
+ type: "play.step.status",
926
+ payload: {
927
+ runId: snapshot.runId,
928
+ stepId: state.nodeId,
929
+ status: state.status,
930
+ artifactTableNamespace: state.artifactTableNamespace ?? null,
931
+ ...resolveTimingWindow({
932
+ startedAt: persistedStartedAt,
933
+ completedAt: persistedCompletedAt,
934
+ updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
935
+ })
936
+ }
937
+ })
938
+ );
939
+ }
940
+ }
941
+ if (next.stepProgressSignature !== previous.stepProgressSignature) {
942
+ for (const state of snapshot.nodeStates) {
943
+ if (!state.progress) {
944
+ continue;
945
+ }
946
+ events.push(
947
+ makeRunStreamEvent({
948
+ cursor: String(
949
+ state.progress.updatedAt ?? snapshot.updatedAt ?? Date.now()
950
+ ),
951
+ streamId,
952
+ type: "play.step.progress",
953
+ payload: {
954
+ runId: snapshot.runId,
955
+ stepId: state.nodeId,
956
+ completed: state.progress.completed,
957
+ total: state.progress.total,
958
+ failed: state.progress.failed,
959
+ message: state.progress.message,
960
+ artifactTableNamespace: state.progress.artifactTableNamespace ?? state.artifactTableNamespace ?? null,
961
+ ...resolveTimingWindow({
962
+ startedAt: state.startedAt ?? state.progress.startedAt ?? null,
963
+ completedAt: state.completedAt ?? state.progress.completedAt ?? null,
964
+ updatedAt: state.progress.updatedAt ?? snapshot.updatedAt ?? null
965
+ })
966
+ }
967
+ })
968
+ );
969
+ }
970
+ }
971
+ if (logDiff.lines.length > 0) {
972
+ events.push(
973
+ makeRunStreamEvent({
974
+ cursor,
975
+ streamId,
976
+ type: "play.run.log",
977
+ payload: {
978
+ runId: snapshot.runId,
979
+ lines: logDiff.lines,
980
+ source: "worker",
981
+ ...logDiff.firstSeq !== null ? { firstSeq: logDiff.firstSeq } : {},
982
+ totalLogCount: snapshot.totalLogCount
983
+ }
984
+ })
985
+ );
986
+ }
987
+ if (next.snapshotSignature !== previous.snapshotSignature) {
988
+ const enrichedNodeStates = snapshot.nodeStates.map((state) => {
989
+ const timing = resolveTimingWindow({
990
+ startedAt: state.startedAt ?? state.progress?.startedAt ?? null,
991
+ completedAt: state.completedAt ?? state.progress?.completedAt ?? null,
992
+ updatedAt: state.updatedAt ?? state.progress?.updatedAt ?? snapshot.updatedAt ?? null
993
+ });
994
+ return {
995
+ ...state,
996
+ ...timing,
997
+ progress: state.progress ? {
998
+ ...state.progress,
999
+ ...resolveTimingWindow({
1000
+ startedAt: state.progress.startedAt ?? state.startedAt ?? null,
1001
+ completedAt: state.progress.completedAt ?? state.completedAt ?? null,
1002
+ updatedAt: state.progress.updatedAt ?? state.updatedAt ?? snapshot.updatedAt ?? null
1003
+ })
1004
+ } : state.progress
1005
+ };
1006
+ });
1007
+ events.push(
1008
+ makeRunStreamEvent({
1009
+ cursor,
1010
+ streamId,
1011
+ type: "play.run.snapshot",
1012
+ payload: { ...snapshot, nodeStates: enrichedNodeStates }
1013
+ })
1014
+ );
1015
+ }
1016
+ if (next.runSignature !== previous.runSignature) {
1017
+ events.push(
1018
+ makeRunStreamEvent({
1019
+ cursor,
1020
+ streamId,
1021
+ type: "play.run.status",
1022
+ payload: {
1023
+ runId: snapshot.runId,
1024
+ status: snapshot.status,
1025
+ updatedAt: snapshot.updatedAt
1026
+ }
1027
+ })
1028
+ );
1029
+ }
1030
+ return { events, next };
1031
+ }
1032
+
1033
+ // src/runs/observe-transport.ts
1034
+ var RunObserveTransportUnavailableError = class extends Error {
1035
+ constructor(message, reason) {
1036
+ super(message);
1037
+ this.reason = reason;
1038
+ this.name = "RunObserveTransportUnavailableError";
1039
+ }
1040
+ reason;
1041
+ };
1042
+ var OBSERVE_BOOTSTRAP_TIMEOUT_MS = 1e4;
1043
+ var OBSERVE_RECONNECT_NOTICE_MS = 1e4;
1044
+ var OBSERVE_STALE_WARNING_MS = 12e4;
1045
+ var OBSERVE_WATCHDOG_TICK_MS = 5e3;
1046
+ var GRANT_REFRESH_MARGIN_MS = 5 * 6e4;
1047
+ var BACKFILL_PAGE_LIMIT = 1e3;
1048
+ var BACKFILL_MAX_PAGES = 30;
1049
+ var OBSERVER_SNAPSHOT_QUERY = "runObservers:getPlayRunSnapshotForObserver";
1050
+ var OBSERVER_LOG_PAGE_QUERY = "runObservers:getRunLogPageForObserver";
1051
+ function errorText(error) {
1052
+ return error instanceof Error ? error.message : String(error);
1053
+ }
1054
+ async function mintRunObserveGrant(http, runId) {
1055
+ let response;
1056
+ try {
1057
+ response = await http.post(
1058
+ `/api/v2/runs/${encodeURIComponent(runId)}/observe-grant`,
1059
+ {}
1060
+ );
1061
+ } catch (error) {
1062
+ if (error instanceof DeeplineError) {
1063
+ if (error.statusCode === 401 || error.statusCode === 403) {
1064
+ throw error;
1065
+ }
1066
+ throw new RunObserveTransportUnavailableError(
1067
+ `observe-grant endpoint unavailable (${error.statusCode ?? "network"}): ${error.message}`,
1068
+ "grant_endpoint_unavailable"
1069
+ );
1070
+ }
1071
+ throw new RunObserveTransportUnavailableError(
1072
+ `observe-grant request failed: ${errorText(error)}`,
1073
+ "grant_request_failed"
1074
+ );
1075
+ }
1076
+ const grant = response;
1077
+ if (!grant || typeof grant.convexUrl !== "string" || !grant.convexUrl.trim() || typeof grant.token !== "string" || !grant.token.trim() || typeof grant.expiresAt !== "number") {
1078
+ throw new RunObserveTransportUnavailableError(
1079
+ "observe-grant endpoint returned an invalid grant payload.",
1080
+ "grant_payload_invalid"
1081
+ );
1082
+ }
1083
+ return grant;
1084
+ }
1085
+ async function backfillLogGap(input) {
1086
+ const lines = [];
1087
+ let cursor = input.lastLogSeq;
1088
+ for (let page = 0; page < BACKFILL_MAX_PAGES; page += 1) {
1089
+ if (cursor >= input.tailFirstSeq - 1) {
1090
+ break;
1091
+ }
1092
+ let logPage;
1093
+ try {
1094
+ logPage = await input.queryLogPage(
1095
+ cursor,
1096
+ Math.min(BACKFILL_PAGE_LIMIT, input.tailFirstSeq - 1 - cursor)
1097
+ );
1098
+ } catch {
1099
+ return null;
1100
+ }
1101
+ const entries = (logPage?.entries ?? []).filter(
1102
+ (entry) => entry.seq > cursor && entry.seq < input.tailFirstSeq
1103
+ );
1104
+ if (entries.length === 0) {
1105
+ break;
1106
+ }
1107
+ for (const entry of entries) {
1108
+ if (entry.seq !== cursor + 1) {
1109
+ return null;
1110
+ }
1111
+ lines.push(entry.line);
1112
+ cursor = entry.seq;
1113
+ }
1114
+ }
1115
+ if (cursor < input.tailFirstSeq - 1) {
1116
+ return null;
1117
+ }
1118
+ return lines;
1119
+ }
1120
+ async function* observeRunEvents(options) {
1121
+ const { http, runId } = options;
1122
+ let grant = await mintRunObserveGrant(http, runId);
1123
+ let convexBrowser;
1124
+ let convexServer;
1125
+ try {
1126
+ convexBrowser = await import("convex/browser");
1127
+ convexServer = await import("convex/server");
1128
+ } catch (error) {
1129
+ throw new RunObserveTransportUnavailableError(
1130
+ `convex client module unavailable: ${errorText(error)}`,
1131
+ "convex_module_unavailable"
1132
+ );
1133
+ }
1134
+ let webSocketConstructor;
1135
+ if (typeof WebSocket === "undefined") {
1136
+ try {
1137
+ const wsModuleName = "ws";
1138
+ const ws = await import(wsModuleName);
1139
+ webSocketConstructor = ws.default;
1140
+ } catch (error) {
1141
+ throw new RunObserveTransportUnavailableError(
1142
+ `no WebSocket implementation available: ${errorText(error)}`,
1143
+ "websocket_unavailable"
1144
+ );
1145
+ }
1146
+ }
1147
+ const snapshotQuery = convexServer.makeFunctionReference(
1148
+ OBSERVER_SNAPSHOT_QUERY
1149
+ );
1150
+ const logPageQuery = convexServer.makeFunctionReference(
1151
+ OBSERVER_LOG_PAGE_QUERY
1152
+ );
1153
+ const client = new convexBrowser.ConvexClient(grant.convexUrl, {
1154
+ ...webSocketConstructor ? { webSocketConstructor } : {},
1155
+ unsavedChangesWarning: false
1156
+ });
1157
+ const queue = [];
1158
+ let wake = null;
1159
+ const push = (item) => {
1160
+ queue.push(item);
1161
+ wake?.();
1162
+ wake = null;
1163
+ };
1164
+ let lastForcedRefreshAt = 0;
1165
+ client.setAuth(async ({ forceRefreshToken }) => {
1166
+ const now = Date.now();
1167
+ if (!forceRefreshToken && grant.expiresAt - now > GRANT_REFRESH_MARGIN_MS) {
1168
+ return grant.token;
1169
+ }
1170
+ if (forceRefreshToken && now - lastForcedRefreshAt < 5e3) {
1171
+ push({
1172
+ kind: "error",
1173
+ error: new DeeplineError(
1174
+ `Run observe grant for ${runId} was rejected after a re-mint. The server and Convex deployment disagree on the grant issuer/JWKS.`,
1175
+ 401,
1176
+ "RUN_OBSERVE_GRANT_REJECTED"
1177
+ )
1178
+ });
1179
+ return null;
1180
+ }
1181
+ if (forceRefreshToken) {
1182
+ lastForcedRefreshAt = now;
1183
+ }
1184
+ try {
1185
+ grant = await mintRunObserveGrant(http, runId);
1186
+ return grant.token;
1187
+ } catch (error) {
1188
+ push({ kind: "error", error });
1189
+ return null;
1190
+ }
1191
+ });
1192
+ const unsubscribe = client.onUpdate(
1193
+ snapshotQuery,
1194
+ { workflowId: runId },
1195
+ (run) => push({ kind: "run", run: run ?? null }),
1196
+ (error) => push({ kind: "error", error })
1197
+ );
1198
+ let lastUpdateAt = Date.now();
1199
+ let lastStatusTerminal = false;
1200
+ let disconnectedSince = null;
1201
+ let warnedReconnecting = false;
1202
+ let warnedStale = false;
1203
+ const watchdog = setInterval(() => {
1204
+ const now = Date.now();
1205
+ try {
1206
+ const connectionState = client.connectionState();
1207
+ if (connectionState.isWebSocketConnected) {
1208
+ disconnectedSince = null;
1209
+ warnedReconnecting = false;
1210
+ } else {
1211
+ disconnectedSince ??= now;
1212
+ if (!warnedReconnecting && now - disconnectedSince >= OBSERVE_RECONNECT_NOTICE_MS) {
1213
+ warnedReconnecting = true;
1214
+ options.onNotice?.(
1215
+ `[observe] connection lost; reconnecting to live run ${runId}\u2026`
1216
+ );
1217
+ }
1218
+ }
1219
+ } catch {
1220
+ }
1221
+ if (!lastStatusTerminal && !warnedStale && now - lastUpdateAt >= OBSERVE_STALE_WARNING_MS) {
1222
+ warnedStale = true;
1223
+ options.onNotice?.(
1224
+ `[observe] no live updates for ${Math.round((now - lastUpdateAt) / 1e3)}s; run ${runId} may be stalled (status checks continue)`
1225
+ );
1226
+ }
1227
+ }, OBSERVE_WATCHDOG_TICK_MS);
1228
+ watchdog.unref?.();
1229
+ const abortListener = () => push({
1230
+ kind: "error",
1231
+ error: new DeeplineError(
1232
+ "Run observation aborted.",
1233
+ void 0,
1234
+ "ABORTED"
1235
+ )
1236
+ });
1237
+ options.signal?.addEventListener("abort", abortListener, { once: true });
1238
+ let diffState = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
1239
+ const streamId = ["observe", runId].join(":");
1240
+ let sawFirstSnapshot = false;
1241
+ try {
1242
+ for (; ; ) {
1243
+ if (queue.length === 0) {
1244
+ const waitForItem = new Promise((resolve2) => {
1245
+ wake = resolve2;
1246
+ });
1247
+ if (!sawFirstSnapshot) {
1248
+ const timedOut = await Promise.race([
1249
+ waitForItem.then(() => false),
1250
+ new Promise(
1251
+ (resolve2) => setTimeout(() => resolve2(true), OBSERVE_BOOTSTRAP_TIMEOUT_MS)
1252
+ )
1253
+ ]);
1254
+ if (timedOut && queue.length === 0) {
1255
+ throw new RunObserveTransportUnavailableError(
1256
+ `no snapshot from Convex at ${grant.convexUrl} within ${OBSERVE_BOOTSTRAP_TIMEOUT_MS}ms`,
1257
+ "convex_unreachable"
1258
+ );
1259
+ }
1260
+ } else {
1261
+ await waitForItem;
1262
+ }
1263
+ continue;
1264
+ }
1265
+ const item = queue.shift();
1266
+ if (item.kind === "error") {
1267
+ if (options.signal?.aborted) {
1268
+ return;
1269
+ }
1270
+ throw item.error;
1271
+ }
1272
+ sawFirstSnapshot = true;
1273
+ lastUpdateAt = Date.now();
1274
+ warnedStale = false;
1275
+ if (item.run === null) {
1276
+ throw new DeeplineError(
1277
+ `Run ${runId} was not found (or is not visible to this grant).`,
1278
+ 404,
1279
+ "RUN_NOT_FOUND"
1280
+ );
1281
+ }
1282
+ const snapshot = buildPlayRunStatusSnapshot({ run: item.run });
1283
+ lastStatusTerminal = isTerminalPlayRunLiveStatus(snapshot.status);
1284
+ const gap = resolvePlayRunLogGap(snapshot, diffState.lastLogSeq);
1285
+ if (gap && diffState.lastLogSeq > 0) {
1286
+ const backfilled = await backfillLogGap({
1287
+ queryLogPage: (afterSeq, limit) => client.query(logPageQuery, {
1288
+ workflowId: runId,
1289
+ afterSeq,
1290
+ limit
1291
+ }),
1292
+ lastLogSeq: diffState.lastLogSeq,
1293
+ tailFirstSeq: gap.tailFirstSeq
1294
+ });
1295
+ if (backfilled && backfilled.length > 0) {
1296
+ yield {
1297
+ cursor: String(snapshot.updatedAt ?? Date.now()),
1298
+ streamId,
1299
+ scope: "play",
1300
+ type: "play.run.log",
1301
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1302
+ payload: {
1303
+ runId: snapshot.runId,
1304
+ lines: backfilled,
1305
+ source: "worker",
1306
+ firstSeq: diffState.lastLogSeq + 1,
1307
+ totalLogCount: snapshot.totalLogCount
1308
+ }
1309
+ };
1310
+ diffState = { ...diffState, lastLogSeq: gap.tailFirstSeq - 1 };
1311
+ }
1312
+ }
1313
+ const { events, next } = diffPlayRunStreamEvents({
1314
+ streamId,
1315
+ snapshot,
1316
+ previous: diffState
1317
+ });
1318
+ diffState = next;
1319
+ const ordered = [
1320
+ ...events.filter((event) => event.type === "play.run.snapshot"),
1321
+ ...events.filter((event) => event.type !== "play.run.snapshot")
1322
+ ];
1323
+ for (const event of ordered) {
1324
+ yield event;
1325
+ }
1326
+ if (lastStatusTerminal) {
1327
+ return;
1328
+ }
1329
+ }
1330
+ } finally {
1331
+ clearInterval(watchdog);
1332
+ options.signal?.removeEventListener("abort", abortListener);
1333
+ try {
1334
+ unsubscribe();
1335
+ } catch {
1336
+ }
1337
+ await client.close().catch(() => void 0);
1338
+ }
1339
+ }
1340
+
540
1341
  // src/client.ts
541
1342
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
542
1343
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -555,7 +1356,8 @@ function isTransientCompileManifestError(error) {
555
1356
  message
556
1357
  );
557
1358
  }
558
- function isRecord(value) {
1359
+ var RUN_LOGS_PAGE_LIMIT = 1e3;
1360
+ function isRecord2(value) {
559
1361
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
560
1362
  }
561
1363
  function isPrebuiltPlayDescription(play) {
@@ -625,10 +1427,31 @@ function normalizeLiveStatus(value) {
625
1427
  }
626
1428
  return null;
627
1429
  }
1430
+ function appendPlayLiveLogLines(state, payload) {
1431
+ const lines = readStringArray(payload.lines);
1432
+ if (lines.length === 0) {
1433
+ return;
1434
+ }
1435
+ const firstSeq = typeof payload.firstSeq === "number" && Number.isFinite(payload.firstSeq) && payload.firstSeq >= 1 ? Math.trunc(payload.firstSeq) : null;
1436
+ if (firstSeq === null) {
1437
+ state.logs.push(...lines);
1438
+ const totalLogCount = typeof payload.totalLogCount === "number" && Number.isFinite(payload.totalLogCount) ? Math.trunc(payload.totalLogCount) : null;
1439
+ if (totalLogCount !== null) {
1440
+ state.lastLogSeq = Math.max(state.lastLogSeq, totalLogCount);
1441
+ }
1442
+ return;
1443
+ }
1444
+ const skip = Math.max(0, state.lastLogSeq + 1 - firstSeq);
1445
+ if (skip >= lines.length) {
1446
+ return;
1447
+ }
1448
+ state.logs.push(...lines.slice(skip));
1449
+ state.lastLogSeq = Math.max(state.lastLogSeq, firstSeq + lines.length - 1);
1450
+ }
628
1451
  function updatePlayLiveStatusState(state, event) {
629
1452
  const payload = getPlayLiveEventPayload(event);
630
1453
  if (event.type === "play.run.log") {
631
- state.logs.push(...readStringArray(payload.lines));
1454
+ appendPlayLiveLogLines(state, payload);
632
1455
  return null;
633
1456
  }
634
1457
  if (event.type !== "play.run.snapshot" && event.type !== "play.run.status" && event.type !== "play.run.final_status") {
@@ -636,12 +1459,14 @@ function updatePlayLiveStatusState(state, event) {
636
1459
  }
637
1460
  const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
638
1461
  const status = normalizeLiveStatus(payload.status) ?? (isPlayRunPackage(payload) ? normalizeLiveStatus(payload.run.status) : null) ?? state.status;
639
- const progressPayload = isRecord(payload.progress) ? payload.progress : {};
640
- const payloadLogs = readStringArray(payload.logs);
641
- const progressLogs = readStringArray(progressPayload.logs);
642
- const logs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
643
- if (logs.length > 0 || event.type === "play.run.snapshot" || event.type === "play.run.final_status" && !isPlayRunPackage(payload)) {
644
- state.logs = logs;
1462
+ const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
1463
+ if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
1464
+ const payloadLogs = readStringArray(payload.logs);
1465
+ const progressLogs = readStringArray(progressPayload.logs);
1466
+ const seedLogs = payloadLogs.length > 0 ? payloadLogs : progressLogs;
1467
+ if (seedLogs.length > 0) {
1468
+ state.logs = seedLogs;
1469
+ }
645
1470
  }
646
1471
  if ("result" in payload) {
647
1472
  state.result = payload.result;
@@ -735,9 +1560,9 @@ var DeeplineClient = class {
735
1560
  return fields.length > 0 ? { fields } : schema;
736
1561
  }
737
1562
  schemaMetadata(schema, key) {
738
- if (!isRecord(schema)) return null;
1563
+ if (!isRecord2(schema)) return null;
739
1564
  const value = schema[key];
740
- return isRecord(value) ? value : null;
1565
+ return isRecord2(value) ? value : null;
741
1566
  }
742
1567
  playRunCommand(play, options) {
743
1568
  const target = play.reference || play.name;
@@ -784,7 +1609,7 @@ var DeeplineClient = class {
784
1609
  aliases,
785
1610
  inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
786
1611
  outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
787
- staticPipeline: isRecord(play.staticPipeline) ? play.staticPipeline : isRecord(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
1612
+ staticPipeline: isRecord2(play.staticPipeline) ? play.staticPipeline : isRecord2(play.currentRevision?.staticPipeline) ? play.currentRevision.staticPipeline : isRecord2(play.liveRevision?.staticPipeline) ? play.liveRevision.staticPipeline : null,
788
1613
  ...csvInput ? { csvInput } : {},
789
1614
  ...rowOutputSchema ? { rowOutputSchema } : {},
790
1615
  runCommand,
@@ -1468,43 +2293,140 @@ var DeeplineClient = class {
1468
2293
  );
1469
2294
  return response.runs ?? [];
1470
2295
  }
1471
- /** Read the canonical run stream and return the latest run snapshot. */
1472
- async tailRun(runId, options) {
2296
+ /**
2297
+ * Observe one run's live events through the Convex Run Snapshot
2298
+ * subscription transport (ADR-0008). Yields the same `play.*` event
2299
+ * envelopes as {@link streamPlayRunEvents} and ends after the terminal
2300
+ * snapshot. Throws {@link RunObserveTransportUnavailableError} when this
2301
+ * server cannot serve the transport (older server, unconfigured grants, or
2302
+ * unreachable Convex) — callers fall back to the SSE stream with a notice.
2303
+ */
2304
+ observeRunEvents(runId, options) {
2305
+ return observeRunEvents({
2306
+ http: this.http,
2307
+ runId,
2308
+ signal: options?.signal,
2309
+ onNotice: options?.onNotice
2310
+ });
2311
+ }
2312
+ /**
2313
+ * Tail one run through the subscription transport until terminal, then
2314
+ * return one durable REST status read (the final Run Response Package).
2315
+ */
2316
+ async tailRunViaObserveTransport(runId, options) {
1473
2317
  const state = {
1474
2318
  runId,
1475
2319
  status: "running",
1476
2320
  logs: [],
2321
+ lastLogSeq: 0,
1477
2322
  latest: null
1478
2323
  };
1479
- let terminal = false;
1480
- for await (const event of this.streamPlayRunEvents(runId, {
1481
- mode: "cli",
1482
- signal: options?.signal
2324
+ for await (const event of this.observeRunEvents(runId, {
2325
+ signal: options?.signal,
2326
+ onNotice: options?.onNotice
1483
2327
  })) {
1484
2328
  const status = updatePlayLiveStatusState(state, event);
1485
- if (!status) {
2329
+ if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
1486
2330
  continue;
1487
2331
  }
1488
- terminal = TERMINAL_PLAY_STATUSES.has(status.status);
1489
- if (terminal) {
1490
- break;
1491
- }
1492
- }
1493
- if (terminal && state.latest) {
1494
- return await this.getRunStatus(state.latest.runId || runId).catch(
2332
+ return await this.getRunStatus(status.runId || runId).catch(
1495
2333
  () => state.latest ?? playRunStatusFromState(state)
1496
2334
  );
1497
2335
  }
1498
- if (state.latest) {
1499
- return state.latest;
2336
+ if (options?.signal?.aborted) {
2337
+ throw new DeeplineError("Run observation aborted.", void 0, "ABORTED");
2338
+ }
2339
+ const refreshed = await this.getRunStatus(runId);
2340
+ if (TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2341
+ return refreshed;
1500
2342
  }
1501
2343
  throw new DeeplineError(
1502
- `Run stream for ${runId} ended before the initial snapshot.`,
2344
+ `Run observation for ${runId} ended before a terminal status.`,
1503
2345
  void 0,
1504
- "PLAY_RUN_STREAM_EMPTY",
1505
- { runId }
2346
+ "PLAY_LIVE_STREAM_ENDED"
1506
2347
  );
1507
2348
  }
2349
+ /**
2350
+ * Read the canonical run stream until a terminal run status is observed.
2351
+ *
2352
+ * Tries the Convex Run Snapshot subscription transport first (ADR-0008);
2353
+ * when the server cannot serve it (grant endpoint missing/unconfigured or
2354
+ * Convex unreachable) it falls back — with one `onNotice` message — to the
2355
+ * support-window SSE stream below.
2356
+ *
2357
+ * Server stream windows are finite: they end cleanly at the function
2358
+ * ceiling even while the run keeps executing. A window that ends (cleanly
2359
+ * or via transient network error) without a terminal event triggers one
2360
+ * durable-status re-check followed by a backed-off reconnect, so long runs
2361
+ * tail to completion. Abort via `options.signal` to stop waiting.
2362
+ */
2363
+ async tailRun(runId, options) {
2364
+ try {
2365
+ return await this.tailRunViaObserveTransport(runId, options);
2366
+ } catch (error) {
2367
+ if (!(error instanceof RunObserveTransportUnavailableError)) {
2368
+ throw error;
2369
+ }
2370
+ options?.onNotice?.(
2371
+ `[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`
2372
+ );
2373
+ }
2374
+ const state = {
2375
+ runId,
2376
+ status: "running",
2377
+ logs: [],
2378
+ lastLogSeq: 0,
2379
+ latest: null
2380
+ };
2381
+ let reconnectAttempt = 0;
2382
+ for (; ; ) {
2383
+ const connectedAt = Date.now();
2384
+ let sawEvent = false;
2385
+ let endedReason = "stream window ended before a terminal event";
2386
+ try {
2387
+ for await (const event of this.streamPlayRunEvents(runId, {
2388
+ mode: "cli",
2389
+ signal: options?.signal
2390
+ })) {
2391
+ sawEvent = true;
2392
+ const status = updatePlayLiveStatusState(state, event);
2393
+ if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
2394
+ continue;
2395
+ }
2396
+ return await this.getRunStatus(status.runId || runId).catch(
2397
+ () => state.latest ?? playRunStatusFromState(state)
2398
+ );
2399
+ }
2400
+ } catch (error) {
2401
+ if (options?.signal?.aborted || !isTransientPlayStreamError(error)) {
2402
+ throw error;
2403
+ }
2404
+ endedReason = error instanceof Error ? error.message : String(error);
2405
+ }
2406
+ let refreshed = null;
2407
+ try {
2408
+ refreshed = await this.getRunStatus(runId);
2409
+ } catch (error) {
2410
+ if (!isTransientPlayStreamError(error)) {
2411
+ throw error;
2412
+ }
2413
+ }
2414
+ if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2415
+ return refreshed;
2416
+ }
2417
+ if (sawEvent || Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS) {
2418
+ reconnectAttempt = 0;
2419
+ }
2420
+ const delayMs = streamReconnectDelayMs(reconnectAttempt);
2421
+ reconnectAttempt += 1;
2422
+ options?.onReconnect?.({
2423
+ attempt: reconnectAttempt,
2424
+ delayMs,
2425
+ reason: endedReason
2426
+ });
2427
+ await sleep2(delayMs);
2428
+ }
2429
+ }
1508
2430
  /**
1509
2431
  * Fetch persisted logs for a run using the public runs resource model.
1510
2432
  *
@@ -1515,19 +2437,40 @@ var DeeplineClient = class {
1515
2437
  * ```
1516
2438
  */
1517
2439
  async getRunLogs(runId, options) {
1518
- const status = await this.getRunStatus(runId, { full: true });
1519
- const logs = status.progress?.logs ?? [];
1520
- const limit = typeof options?.limit === "number" && Number.isFinite(options.limit) ? Math.max(0, Math.trunc(options.limit)) : 200;
1521
- const entries = logs.slice(Math.max(0, logs.length - limit));
2440
+ const limit = options?.all ? Number.MAX_SAFE_INTEGER : typeof options?.limit === "number" && Number.isFinite(options.limit) && options.limit > 0 ? Math.trunc(options.limit) : 200;
2441
+ const fetchPage = (afterSeq2, pageLimit) => this.http.get(
2442
+ `/api/v2/runs/${encodeURIComponent(runId)}/logs?afterSeq=${afterSeq2}&limit=${pageLimit}`
2443
+ );
2444
+ const probe = await fetchPage(0, 1);
2445
+ const lastStoredSeq = probe.lastStoredSeq;
2446
+ let afterSeq = options?.all ? 0 : Math.max(0, lastStoredSeq - limit);
2447
+ const entries = [];
2448
+ while (entries.length < limit) {
2449
+ const page = await fetchPage(
2450
+ afterSeq,
2451
+ Math.min(RUN_LOGS_PAGE_LIMIT, limit - entries.length)
2452
+ );
2453
+ if (page.entries.length === 0) {
2454
+ break;
2455
+ }
2456
+ entries.push(...page.entries);
2457
+ afterSeq = page.entries[page.entries.length - 1].seq;
2458
+ if (!page.hasMore) {
2459
+ break;
2460
+ }
2461
+ }
2462
+ const firstSequence = entries.length > 0 ? entries[0].seq : null;
2463
+ const lastSequence = entries.length > 0 ? entries[entries.length - 1].seq : null;
1522
2464
  return {
1523
- runId: status.runId,
1524
- totalCount: logs.length,
2465
+ runId: probe.runId,
2466
+ totalCount: probe.totalLogCount,
1525
2467
  returnedCount: entries.length,
1526
- firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
1527
- lastSequence: logs.length === 0 ? null : logs.length,
1528
- truncated: logs.length > entries.length,
1529
- hasMore: logs.length > entries.length,
1530
- entries
2468
+ firstSequence,
2469
+ lastSequence,
2470
+ truncated: entries.length < probe.totalLogCount,
2471
+ hasMore: lastSequence !== null && lastSequence < lastStoredSeq,
2472
+ entries: entries.map((entry) => entry.line),
2473
+ ...probe.logsTruncated ? { logsTruncated: true } : {}
1531
2474
  };
1532
2475
  }
1533
2476
  /**
@@ -1818,6 +2761,7 @@ var DeeplineClient = class {
1818
2761
  runId: workflowId,
1819
2762
  status: "running",
1820
2763
  logs: [],
2764
+ lastLogSeq: 0,
1821
2765
  latest: null
1822
2766
  };
1823
2767
  if (options?.signal?.aborted) {
@@ -2188,7 +3132,7 @@ var TARGET_FALLBACK_KEYS = {
2188
3132
  status: [/^email_status$/i, /^status$/i],
2189
3133
  email_status: [/^email_status$/i, /^status$/i]
2190
3134
  };
2191
- function isRecord2(value) {
3135
+ function isRecord3(value) {
2192
3136
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2193
3137
  }
2194
3138
  function toV2RawToolOutputPath(path) {
@@ -2248,7 +3192,7 @@ function valuesAtSegments(current, segments, path = []) {
2248
3192
  if (!Array.isArray(current)) return [];
2249
3193
  return valuesAtSegments(current[segment], rest, [...path, segment]);
2250
3194
  }
2251
- if (!isRecord2(current)) return [];
3195
+ if (!isRecord3(current)) return [];
2252
3196
  const directMatches = valuesAtSegments(current[segment], rest, [
2253
3197
  ...path,
2254
3198
  segment
@@ -2278,7 +3222,7 @@ function getValuesAtPath(root, path) {
2278
3222
  return valuesAtSegments(root, parsePath(path)).map((entry) => entry.value);
2279
3223
  }
2280
3224
  function toResultEnvelope(value) {
2281
- if (isRecord2(value) && "data" in value) {
3225
+ if (isRecord3(value) && "data" in value) {
2282
3226
  const envelope = { data: value.data };
2283
3227
  if ("meta" in value) envelope.meta = value.meta;
2284
3228
  return envelope;
@@ -2331,7 +3275,7 @@ function getAtPath(root, path) {
2331
3275
  current = current[segment];
2332
3276
  continue;
2333
3277
  }
2334
- if (!isRecord2(current)) return void 0;
3278
+ if (!isRecord3(current)) return void 0;
2335
3279
  current = current[segment];
2336
3280
  }
2337
3281
  return current;
@@ -2348,7 +3292,7 @@ function normalizeString(value) {
2348
3292
  }
2349
3293
  function normalizeRows(value) {
2350
3294
  if (!Array.isArray(value)) return null;
2351
- return value.map((entry) => isRecord2(entry) ? entry : { value: entry });
3295
+ return value.map((entry) => isRecord3(entry) ? entry : { value: entry });
2352
3296
  }
2353
3297
  function findFirstTargetByPath(result, paths) {
2354
3298
  for (const path of paths ?? []) {
@@ -2407,7 +3351,7 @@ function findFirstTargetByKey(result, target, depth = 0, path = []) {
2407
3351
  }
2408
3352
  return null;
2409
3353
  }
2410
- if (!isRecord2(result)) return null;
3354
+ if (!isRecord3(result)) return null;
2411
3355
  const patterns = TARGET_FALLBACK_KEYS[target] ?? [
2412
3356
  new RegExp(`^${target}$`, "i")
2413
3357
  ];
@@ -2467,7 +3411,7 @@ function normalizeJobChangeStatus(value) {
2467
3411
  function firstExperienceDate(value) {
2468
3412
  if (!Array.isArray(value)) return null;
2469
3413
  for (const entry of value) {
2470
- if (!isRecord2(entry)) continue;
3414
+ if (!isRecord3(entry)) continue;
2471
3415
  const date = normalizeString(
2472
3416
  entry.start_date ?? entry.started_at ?? entry.startDate
2473
3417
  );
@@ -2476,10 +3420,10 @@ function firstExperienceDate(value) {
2476
3420
  return null;
2477
3421
  }
2478
3422
  function normalizeJobChange(value) {
2479
- const record = isRecord2(value) ? value : {};
2480
- const nested = isRecord2(record.job_change) ? record.job_change : record;
2481
- const output = isRecord2(nested.output) ? nested.output : nested;
2482
- const person = isRecord2(output.person) ? output.person : {};
3423
+ const record = isRecord3(value) ? value : {};
3424
+ const nested = isRecord3(record.job_change) ? record.job_change : record;
3425
+ const output = isRecord3(nested.output) ? nested.output : nested;
3426
+ const person = isRecord3(output.person) ? output.person : {};
2483
3427
  const status = normalizeJobChangeStatus(
2484
3428
  output.status ?? output.job_change_status ?? output.job_changed ?? output.changed
2485
3429
  );
@@ -3047,11 +3991,11 @@ var Deepline = class {
3047
3991
  return new DeeplineContext(options);
3048
3992
  }
3049
3993
  };
3050
- function isRecord3(value) {
3994
+ function isRecord4(value) {
3051
3995
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3052
3996
  }
3053
3997
  function stringArrayRecord(value) {
3054
- if (!isRecord3(value)) return {};
3998
+ if (!isRecord4(value)) return {};
3055
3999
  return Object.fromEntries(
3056
4000
  Object.entries(value).map(([key, paths]) => [
3057
4001
  key,
@@ -3063,10 +4007,10 @@ function stringArray(value) {
3063
4007
  return Array.isArray(value) ? value.map(String) : [];
3064
4008
  }
3065
4009
  function extractorDescriptorRecord(value) {
3066
- if (!isRecord3(value)) return {};
4010
+ if (!isRecord4(value)) return {};
3067
4011
  return Object.fromEntries(
3068
4012
  Object.entries(value).flatMap(([key, descriptor]) => {
3069
- if (!isRecord3(descriptor)) return [];
4013
+ if (!isRecord4(descriptor)) return [];
3070
4014
  const paths = stringArray(descriptor.paths).map((path) => path.trim()).filter(Boolean);
3071
4015
  if (paths.length === 0) return [];
3072
4016
  const transforms = stringArray(descriptor.transforms).map((transform) => transform.trim()).filter(Boolean);
@@ -3087,14 +4031,14 @@ function extractorDescriptorRecord(value) {
3087
4031
  function toolExecutionEnvelopeToResult(fallbackToolId, response) {
3088
4032
  const raw = response.toolResponse?.raw ?? null;
3089
4033
  const meta = response.toolResponse?.meta;
3090
- const metadata = isRecord3(response._metadata) ? response._metadata.tool : null;
3091
- const toolMetadata = isRecord3(metadata) ? metadata : {};
4034
+ const metadata = isRecord4(response._metadata) ? response._metadata.tool : null;
4035
+ const toolMetadata = isRecord4(metadata) ? metadata : {};
3092
4036
  return createToolExecuteResult({
3093
4037
  status: typeof response.status === "string" ? response.status : "completed",
3094
4038
  jobId: typeof response.job_id === "string" ? response.job_id : void 0,
3095
4039
  result: {
3096
4040
  data: raw,
3097
- ...isRecord3(meta) ? { meta } : {}
4041
+ ...isRecord4(meta) ? { meta } : {}
3098
4042
  },
3099
4043
  metadata: {
3100
4044
  toolId: typeof toolMetadata.toolId === "string" ? toolMetadata.toolId : fallbackToolId,
@@ -3108,7 +4052,7 @@ function toolExecutionEnvelopeToResult(fallbackToolId, response) {
3108
4052
  cached: false,
3109
4053
  source: "live"
3110
4054
  },
3111
- meta: isRecord3(response.meta) ? response.meta : void 0
4055
+ meta: isRecord4(response.meta) ? response.meta : void 0
3112
4056
  });
3113
4057
  }
3114
4058
  function defineInput(schema) {
@@ -3409,6 +4353,7 @@ export {
3409
4353
  PLAY_BOOTSTRAP_TEMPLATES,
3410
4354
  PROD_URL,
3411
4355
  RateLimitError,
4356
+ RunObserveTransportUnavailableError,
3412
4357
  SDK_API_CONTRACT,
3413
4358
  SDK_VERSION,
3414
4359
  defineInput,