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