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