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