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