@dogpile/sdk 0.4.0 → 0.6.0
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/CHANGELOG.md +92 -0
- package/dist/browser/index.js +4156 -4611
- package/dist/browser/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/openai-compatible.d.ts.map +1 -1
- package/dist/providers/openai-compatible.js +6 -1
- package/dist/providers/openai-compatible.js.map +1 -1
- package/dist/runtime/audit.d.ts +42 -0
- package/dist/runtime/audit.d.ts.map +1 -0
- package/dist/runtime/audit.js +73 -0
- package/dist/runtime/audit.js.map +1 -0
- package/dist/runtime/broadcast.d.ts +1 -0
- package/dist/runtime/broadcast.d.ts.map +1 -1
- package/dist/runtime/broadcast.js +171 -105
- package/dist/runtime/broadcast.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +9 -2
- package/dist/runtime/coordinator.d.ts.map +1 -1
- package/dist/runtime/coordinator.js +164 -78
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/defaults.d.ts.map +1 -1
- package/dist/runtime/defaults.js +14 -5
- package/dist/runtime/defaults.js.map +1 -1
- package/dist/runtime/engine.d.ts +17 -4
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +577 -52
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/health.d.ts +51 -0
- package/dist/runtime/health.d.ts.map +1 -0
- package/dist/runtime/health.js +85 -0
- package/dist/runtime/health.js.map +1 -0
- package/dist/runtime/introspection.d.ts +96 -0
- package/dist/runtime/introspection.d.ts.map +1 -0
- package/dist/runtime/introspection.js +31 -0
- package/dist/runtime/introspection.js.map +1 -0
- package/dist/runtime/metrics.d.ts +44 -0
- package/dist/runtime/metrics.d.ts.map +1 -0
- package/dist/runtime/metrics.js +12 -0
- package/dist/runtime/metrics.js.map +1 -0
- package/dist/runtime/model.d.ts.map +1 -1
- package/dist/runtime/model.js +40 -10
- package/dist/runtime/model.js.map +1 -1
- package/dist/runtime/provenance.d.ts +25 -0
- package/dist/runtime/provenance.d.ts.map +1 -0
- package/dist/runtime/provenance.js +13 -0
- package/dist/runtime/provenance.js.map +1 -0
- package/dist/runtime/redaction.d.ts +13 -0
- package/dist/runtime/redaction.d.ts.map +1 -0
- package/dist/runtime/redaction.js +278 -0
- package/dist/runtime/redaction.js.map +1 -0
- package/dist/runtime/sanitization.d.ts +4 -0
- package/dist/runtime/sanitization.d.ts.map +1 -0
- package/dist/runtime/sanitization.js +63 -0
- package/dist/runtime/sanitization.js.map +1 -0
- package/dist/runtime/sequential.d.ts.map +1 -1
- package/dist/runtime/sequential.js +39 -36
- package/dist/runtime/sequential.js.map +1 -1
- package/dist/runtime/shared.d.ts +1 -0
- package/dist/runtime/shared.d.ts.map +1 -1
- package/dist/runtime/shared.js +167 -101
- package/dist/runtime/shared.js.map +1 -1
- package/dist/runtime/tools/built-in.d.ts +2 -0
- package/dist/runtime/tools/built-in.d.ts.map +1 -1
- package/dist/runtime/tools/built-in.js +153 -15
- package/dist/runtime/tools/built-in.js.map +1 -1
- package/dist/runtime/tools.d.ts.map +1 -1
- package/dist/runtime/tools.js +29 -7
- package/dist/runtime/tools.js.map +1 -1
- package/dist/runtime/tracing.d.ts +31 -0
- package/dist/runtime/tracing.d.ts.map +1 -0
- package/dist/runtime/tracing.js +18 -0
- package/dist/runtime/tracing.js.map +1 -0
- package/dist/runtime/validation.d.ts.map +1 -1
- package/dist/runtime/validation.js +3 -0
- package/dist/runtime/validation.js.map +1 -1
- package/dist/types/events.d.ts +13 -7
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/replay.d.ts +5 -1
- package/dist/types/replay.d.ts.map +1 -1
- package/dist/types.d.ts +144 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +46 -1
- package/src/index.ts +5 -0
- package/src/providers/openai-compatible.ts +6 -1
- package/src/runtime/audit.ts +121 -0
- package/src/runtime/broadcast.ts +195 -108
- package/src/runtime/coordinator.ts +197 -86
- package/src/runtime/defaults.ts +15 -5
- package/src/runtime/engine.ts +725 -58
- package/src/runtime/health.ts +136 -0
- package/src/runtime/introspection.ts +122 -0
- package/src/runtime/metrics.ts +45 -0
- package/src/runtime/model.ts +44 -9
- package/src/runtime/provenance.ts +43 -0
- package/src/runtime/redaction.ts +355 -0
- package/src/runtime/sanitization.ts +81 -0
- package/src/runtime/sequential.ts +40 -37
- package/src/runtime/shared.ts +191 -104
- package/src/runtime/tools/built-in.ts +168 -15
- package/src/runtime/tools.ts +39 -8
- package/src/runtime/tracing.ts +35 -0
- package/src/runtime/validation.ts +3 -0
- package/src/types/events.ts +13 -7
- package/src/types/replay.ts +5 -1
- package/src/types.ts +152 -1
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
lastCostBearingEventCost,
|
|
47
47
|
nextProviderCallId
|
|
48
48
|
} from "./defaults.js";
|
|
49
|
+
import { computeHealth, DEFAULT_HEALTH_THRESHOLDS } from "./health.js";
|
|
49
50
|
import {
|
|
50
51
|
classifyAbortReason,
|
|
51
52
|
classifyChildTimeoutSource,
|
|
@@ -64,6 +65,11 @@ import { createWrapUpHintController } from "./wrap-up.js";
|
|
|
64
65
|
* in by `engine.ts` so coordinator avoids a circular import.
|
|
65
66
|
*/
|
|
66
67
|
export type RunProtocolFn = (input: {
|
|
68
|
+
/**
|
|
69
|
+
* Planned child run id emitted on sub-run lifecycle events before dispatch.
|
|
70
|
+
* The engine callback uses this to look up the matching sub-run span.
|
|
71
|
+
*/
|
|
72
|
+
readonly runId: string;
|
|
67
73
|
readonly intent: string;
|
|
68
74
|
readonly protocol: ProtocolSelection;
|
|
69
75
|
readonly tier: Tier;
|
|
@@ -81,6 +87,7 @@ export type RunProtocolFn = (input: {
|
|
|
81
87
|
readonly currentDepth?: number;
|
|
82
88
|
readonly effectiveMaxDepth?: number;
|
|
83
89
|
readonly effectiveMaxConcurrentChildren?: number;
|
|
90
|
+
readonly effectiveMaxConcurrentAgentTurns?: number;
|
|
84
91
|
readonly onChildFailure?: DogpileOptions["onChildFailure"];
|
|
85
92
|
/**
|
|
86
93
|
* Root-run deadline (epoch ms). Children inherit `parentDeadlineMs - now()`
|
|
@@ -120,11 +127,12 @@ interface CoordinatorRunOptions {
|
|
|
120
127
|
*/
|
|
121
128
|
readonly currentDepth?: number;
|
|
122
129
|
/**
|
|
123
|
-
* Effective max recursion depth resolved at run start
|
|
124
|
-
*
|
|
130
|
+
* Effective max recursion depth resolved at run start and enforced before
|
|
131
|
+
* delegate dispatch.
|
|
125
132
|
*/
|
|
126
133
|
readonly effectiveMaxDepth?: number;
|
|
127
134
|
readonly effectiveMaxConcurrentChildren?: number;
|
|
135
|
+
readonly effectiveMaxConcurrentAgentTurns?: number;
|
|
128
136
|
readonly onChildFailure?: DogpileOptions["onChildFailure"];
|
|
129
137
|
/**
|
|
130
138
|
* Engine `runProtocol` callback used by the delegate dispatch loop to
|
|
@@ -156,6 +164,7 @@ interface CoordinatorRunOptions {
|
|
|
156
164
|
*/
|
|
157
165
|
const MAX_DISPATCH_PER_TURN = 8;
|
|
158
166
|
const DEFAULT_MAX_CONCURRENT_CHILDREN = 4;
|
|
167
|
+
const DEFAULT_MAX_CONCURRENT_AGENT_TURNS = 4;
|
|
159
168
|
|
|
160
169
|
type DispatchWaveFailure = {
|
|
161
170
|
readonly childRunId: string;
|
|
@@ -177,7 +186,8 @@ interface Semaphore {
|
|
|
177
186
|
|
|
178
187
|
function createSemaphore(maxConcurrent: number): Semaphore {
|
|
179
188
|
let inFlight = 0;
|
|
180
|
-
const waiters: Array<() => void> = [];
|
|
189
|
+
const waiters: Array<(() => void) | undefined> = [];
|
|
190
|
+
let waiterHead = 0;
|
|
181
191
|
return {
|
|
182
192
|
acquire(): Promise<void> {
|
|
183
193
|
if (inFlight < maxConcurrent) {
|
|
@@ -193,8 +203,14 @@ function createSemaphore(maxConcurrent: number): Semaphore {
|
|
|
193
203
|
},
|
|
194
204
|
release(): void {
|
|
195
205
|
inFlight -= 1;
|
|
196
|
-
const next = waiters
|
|
206
|
+
const next = waiters[waiterHead];
|
|
197
207
|
if (next !== undefined) {
|
|
208
|
+
waiters[waiterHead] = undefined;
|
|
209
|
+
waiterHead += 1;
|
|
210
|
+
if (waiterHead > 32 && waiterHead * 2 > waiters.length) {
|
|
211
|
+
waiters.splice(0, waiterHead);
|
|
212
|
+
waiterHead = 0;
|
|
213
|
+
}
|
|
198
214
|
next();
|
|
199
215
|
}
|
|
200
216
|
},
|
|
@@ -202,7 +218,7 @@ function createSemaphore(maxConcurrent: number): Semaphore {
|
|
|
202
218
|
return inFlight;
|
|
203
219
|
},
|
|
204
220
|
get queued() {
|
|
205
|
-
return waiters.length;
|
|
221
|
+
return waiters.length - waiterHead;
|
|
206
222
|
}
|
|
207
223
|
};
|
|
208
224
|
}
|
|
@@ -212,15 +228,16 @@ function createSemaphore(maxConcurrent: number): Semaphore {
|
|
|
212
228
|
* whose metadata.locality === "local", or undefined if none found.
|
|
213
229
|
*
|
|
214
230
|
* Walk order (forward-compat): options.model first, then options.agents in
|
|
215
|
-
* declaration order. AgentSpec has no `model` field today
|
|
216
|
-
*
|
|
217
|
-
*
|
|
231
|
+
* declaration order. AgentSpec has no `model` field today; the agent walk
|
|
232
|
+
* uses optional chaining and effectively no-ops until a future caller-defined
|
|
233
|
+
* tree surface adds AgentSpec.model.
|
|
218
234
|
*/
|
|
219
235
|
function findFirstLocalProvider(options: CoordinatorRunOptions): ConfiguredModelProvider | undefined {
|
|
220
236
|
if (options.model.metadata?.locality === "local") {
|
|
221
237
|
return options.model;
|
|
222
238
|
}
|
|
223
|
-
// Forward-compat: AgentSpec.model not yet declared
|
|
239
|
+
// Forward-compat: AgentSpec.model is not yet declared; this no-ops today and
|
|
240
|
+
// is ready for a future caller-defined tree surface.
|
|
224
241
|
for (const agent of options.agents) {
|
|
225
242
|
const agentModel = (agent as { readonly model?: ConfiguredModelProvider }).model;
|
|
226
243
|
if (agentModel?.metadata?.locality === "local") {
|
|
@@ -357,13 +374,12 @@ export async function runCoordinator(options: CoordinatorRunOptions): Promise<Ru
|
|
|
357
374
|
|
|
358
375
|
if (coordinator) {
|
|
359
376
|
if (!stopIfNeeded()) {
|
|
360
|
-
// Delegate dispatch
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
// error contract is unchanged.
|
|
377
|
+
// Delegate dispatch is restricted to the coordinator's plan turn.
|
|
378
|
+
// Workers and final synthesis cannot delegate. The loop re-issues the
|
|
379
|
+
// coordinator plan turn after each successful sub-run with the tagged
|
|
380
|
+
// child result in the next prompt and a synthetic transcript entry
|
|
381
|
+
// already appended. Failed children use the local tee buffer for
|
|
382
|
+
// partialTrace capture; runProtocol's error contract stays unchanged.
|
|
367
383
|
let dispatchInput = buildCoordinatorPlanInput(options.intent, coordinator);
|
|
368
384
|
let dispatchCount = 0;
|
|
369
385
|
while (true) {
|
|
@@ -638,29 +654,39 @@ export async function runCoordinator(options: CoordinatorRunOptions): Promise<Ru
|
|
|
638
654
|
const workers = activeAgents.slice(1);
|
|
639
655
|
const providerCallSlots: ReplayTraceProviderCall[] = [];
|
|
640
656
|
const planTranscript = [...transcript];
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
657
|
+
const fanout = createFanoutAbortController(options.signal);
|
|
658
|
+
const workerResults = await (async () => {
|
|
659
|
+
try {
|
|
660
|
+
return await mapWithConcurrency(
|
|
661
|
+
workers,
|
|
662
|
+
options.effectiveMaxConcurrentAgentTurns ?? DEFAULT_MAX_CONCURRENT_AGENT_TURNS,
|
|
663
|
+
fanout,
|
|
664
|
+
(agent, index) =>
|
|
665
|
+
runCoordinatorWorkerTurn({
|
|
666
|
+
agent,
|
|
667
|
+
coordinator,
|
|
668
|
+
input: buildWorkerInput(options.intent, planTranscript, coordinator),
|
|
669
|
+
options,
|
|
670
|
+
runId,
|
|
671
|
+
turn: transcript.length + index + 1,
|
|
672
|
+
providerCallId: providerCallIdFor(runId, providerCalls.length + index + 1),
|
|
673
|
+
providerCallIndex: index,
|
|
674
|
+
providerCallSlots,
|
|
675
|
+
toolExecutor,
|
|
676
|
+
toolAvailability,
|
|
677
|
+
totalCost,
|
|
678
|
+
events,
|
|
679
|
+
transcript: planTranscript,
|
|
680
|
+
startedAtMs,
|
|
681
|
+
wrapUpHint,
|
|
682
|
+
emit,
|
|
683
|
+
fanoutSignal: fanout.signal
|
|
684
|
+
})
|
|
685
|
+
);
|
|
686
|
+
} finally {
|
|
687
|
+
fanout.cleanup();
|
|
688
|
+
}
|
|
689
|
+
})();
|
|
664
690
|
providerCalls.push(...providerCallSlots.filter((call): call is ReplayTraceProviderCall => call !== undefined));
|
|
665
691
|
|
|
666
692
|
for (const result of workerResults) {
|
|
@@ -715,11 +741,11 @@ export async function runCoordinator(options: CoordinatorRunOptions): Promise<Ru
|
|
|
715
741
|
recordProtocolDecision
|
|
716
742
|
});
|
|
717
743
|
totalCost = synthesisOutcome.totalCost;
|
|
718
|
-
//
|
|
744
|
+
// Final synthesis is terminal and cannot dispatch children.
|
|
719
745
|
if (Array.isArray(synthesisOutcome.decision) || synthesisOutcome.decision?.type === "delegate") {
|
|
720
746
|
throw new DogpileError({
|
|
721
747
|
code: "invalid-configuration",
|
|
722
|
-
message: "Coordinator final-synthesis turn cannot emit a delegate decision
|
|
748
|
+
message: "Coordinator final-synthesis turn cannot emit a delegate decision.",
|
|
723
749
|
retryable: false,
|
|
724
750
|
detail: {
|
|
725
751
|
kind: "delegate-validation",
|
|
@@ -748,46 +774,47 @@ export async function runCoordinator(options: CoordinatorRunOptions): Promise<Ru
|
|
|
748
774
|
transcriptEntryCount: transcript.length
|
|
749
775
|
});
|
|
750
776
|
const finalEvent = events.at(-1);
|
|
777
|
+
const trace: Trace = {
|
|
778
|
+
schemaVersion: "1.0",
|
|
779
|
+
runId,
|
|
780
|
+
protocol: "coordinator",
|
|
781
|
+
tier: options.tier,
|
|
782
|
+
modelProviderId: options.model.id,
|
|
783
|
+
agentsUsed: activeAgents,
|
|
784
|
+
inputs: createReplayTraceRunInputs({
|
|
785
|
+
intent: options.intent,
|
|
786
|
+
protocol: options.protocol,
|
|
787
|
+
tier: options.tier,
|
|
788
|
+
modelProviderId: options.model.id,
|
|
789
|
+
agents: activeAgents,
|
|
790
|
+
temperature: options.temperature
|
|
791
|
+
}),
|
|
792
|
+
budget: createReplayTraceBudget({
|
|
793
|
+
tier: options.tier,
|
|
794
|
+
...(options.budget ? { caps: options.budget } : {}),
|
|
795
|
+
...(options.terminate ? { termination: options.terminate } : {})
|
|
796
|
+
}),
|
|
797
|
+
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
798
|
+
seed: createReplayTraceSeed(options.seed),
|
|
799
|
+
protocolDecisions,
|
|
800
|
+
providerCalls,
|
|
801
|
+
finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
|
|
802
|
+
type: "final",
|
|
803
|
+
runId,
|
|
804
|
+
at: "",
|
|
805
|
+
output,
|
|
806
|
+
cost: totalCost,
|
|
807
|
+
transcript: createTranscriptLink(transcript)
|
|
808
|
+
}),
|
|
809
|
+
...(triggeringFailureForAbortMode !== undefined ? { triggeringFailureForAbortMode } : {}),
|
|
810
|
+
events,
|
|
811
|
+
transcript
|
|
812
|
+
};
|
|
751
813
|
|
|
752
814
|
return {
|
|
753
815
|
output,
|
|
754
816
|
eventLog: createRunEventLog(runId, "coordinator", events),
|
|
755
|
-
trace
|
|
756
|
-
schemaVersion: "1.0",
|
|
757
|
-
runId,
|
|
758
|
-
protocol: "coordinator",
|
|
759
|
-
tier: options.tier,
|
|
760
|
-
modelProviderId: options.model.id,
|
|
761
|
-
agentsUsed: activeAgents,
|
|
762
|
-
inputs: createReplayTraceRunInputs({
|
|
763
|
-
intent: options.intent,
|
|
764
|
-
protocol: options.protocol,
|
|
765
|
-
tier: options.tier,
|
|
766
|
-
modelProviderId: options.model.id,
|
|
767
|
-
agents: activeAgents,
|
|
768
|
-
temperature: options.temperature
|
|
769
|
-
}),
|
|
770
|
-
budget: createReplayTraceBudget({
|
|
771
|
-
tier: options.tier,
|
|
772
|
-
...(options.budget ? { caps: options.budget } : {}),
|
|
773
|
-
...(options.terminate ? { termination: options.terminate } : {})
|
|
774
|
-
}),
|
|
775
|
-
budgetStateChanges: createReplayTraceBudgetStateChanges(events),
|
|
776
|
-
seed: createReplayTraceSeed(options.seed),
|
|
777
|
-
protocolDecisions,
|
|
778
|
-
providerCalls,
|
|
779
|
-
finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
|
|
780
|
-
type: "final",
|
|
781
|
-
runId,
|
|
782
|
-
at: "",
|
|
783
|
-
output,
|
|
784
|
-
cost: totalCost,
|
|
785
|
-
transcript: createTranscriptLink(transcript)
|
|
786
|
-
}),
|
|
787
|
-
...(triggeringFailureForAbortMode !== undefined ? { triggeringFailureForAbortMode } : {}),
|
|
788
|
-
events,
|
|
789
|
-
transcript
|
|
790
|
-
},
|
|
817
|
+
trace,
|
|
791
818
|
transcript,
|
|
792
819
|
usage: createRunUsage(totalCost),
|
|
793
820
|
metadata: createRunMetadata({
|
|
@@ -805,7 +832,8 @@ export async function runCoordinator(options: CoordinatorRunOptions): Promise<Ru
|
|
|
805
832
|
cost: totalCost,
|
|
806
833
|
events
|
|
807
834
|
}),
|
|
808
|
-
cost: totalCost
|
|
835
|
+
cost: totalCost,
|
|
836
|
+
health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
|
|
809
837
|
};
|
|
810
838
|
|
|
811
839
|
function stopIfNeeded(): boolean {
|
|
@@ -1008,6 +1036,7 @@ interface CoordinatorWorkerTurnOptions {
|
|
|
1008
1036
|
readonly startedAtMs: number;
|
|
1009
1037
|
readonly wrapUpHint: ReturnType<typeof createWrapUpHintController>;
|
|
1010
1038
|
readonly emit: (event: RunEvent) => void;
|
|
1039
|
+
readonly fanoutSignal?: AbortSignal;
|
|
1011
1040
|
}
|
|
1012
1041
|
|
|
1013
1042
|
interface CoordinatorWorkerTurnResult {
|
|
@@ -1021,10 +1050,11 @@ interface CoordinatorWorkerTurnResult {
|
|
|
1021
1050
|
|
|
1022
1051
|
async function runCoordinatorWorkerTurn(turn: CoordinatorWorkerTurnOptions): Promise<CoordinatorWorkerTurnResult> {
|
|
1023
1052
|
throwIfAborted(turn.options.signal, turn.options.model.id);
|
|
1053
|
+
throwIfAborted(turn.fanoutSignal, turn.options.model.id);
|
|
1024
1054
|
|
|
1025
1055
|
const request: ModelRequest = {
|
|
1026
1056
|
temperature: turn.options.temperature,
|
|
1027
|
-
...(turn.options.signal !== undefined ? { signal: turn.options.signal } : {}),
|
|
1057
|
+
...(turn.fanoutSignal !== undefined ? { signal: turn.fanoutSignal } : turn.options.signal !== undefined ? { signal: turn.options.signal } : {}),
|
|
1028
1058
|
metadata: {
|
|
1029
1059
|
runId: turn.runId,
|
|
1030
1060
|
protocol: "coordinator",
|
|
@@ -1077,7 +1107,7 @@ async function runCoordinatorWorkerTurn(turn: CoordinatorWorkerTurnOptions): Pro
|
|
|
1077
1107
|
if (Array.isArray(decision) || decision?.type === "delegate") {
|
|
1078
1108
|
throw new DogpileError({
|
|
1079
1109
|
code: "invalid-configuration",
|
|
1080
|
-
message: "Workers cannot emit delegate decisions
|
|
1110
|
+
message: "Workers cannot emit delegate decisions.",
|
|
1081
1111
|
retryable: false,
|
|
1082
1112
|
detail: {
|
|
1083
1113
|
kind: "delegate-validation",
|
|
@@ -1097,6 +1127,7 @@ async function runCoordinatorWorkerTurn(turn: CoordinatorWorkerTurnOptions): Pro
|
|
|
1097
1127
|
}
|
|
1098
1128
|
});
|
|
1099
1129
|
throwIfAborted(turn.options.signal, turn.options.model.id);
|
|
1130
|
+
throwIfAborted(turn.fanoutSignal, turn.options.model.id);
|
|
1100
1131
|
|
|
1101
1132
|
return {
|
|
1102
1133
|
agent: turn.agent,
|
|
@@ -1185,6 +1216,76 @@ function responseCost(response: ModelResponse): CostSummary {
|
|
|
1185
1216
|
};
|
|
1186
1217
|
}
|
|
1187
1218
|
|
|
1219
|
+
interface FanoutAbortController {
|
|
1220
|
+
readonly signal: AbortSignal;
|
|
1221
|
+
abort(reason: unknown): void;
|
|
1222
|
+
cleanup(): void;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function createFanoutAbortController(parentSignal: AbortSignal | undefined): FanoutAbortController {
|
|
1226
|
+
const controller = new AbortController();
|
|
1227
|
+
let removeParentListener = (): void => {};
|
|
1228
|
+
|
|
1229
|
+
if (parentSignal?.aborted) {
|
|
1230
|
+
controller.abort(parentSignal.reason);
|
|
1231
|
+
} else if (parentSignal !== undefined) {
|
|
1232
|
+
const abortFromParent = (): void => {
|
|
1233
|
+
controller.abort(parentSignal.reason);
|
|
1234
|
+
};
|
|
1235
|
+
parentSignal.addEventListener("abort", abortFromParent, { once: true });
|
|
1236
|
+
removeParentListener = (): void => {
|
|
1237
|
+
parentSignal.removeEventListener("abort", abortFromParent);
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
return {
|
|
1242
|
+
signal: controller.signal,
|
|
1243
|
+
abort(reason: unknown): void {
|
|
1244
|
+
if (!controller.signal.aborted) {
|
|
1245
|
+
controller.abort(reason);
|
|
1246
|
+
}
|
|
1247
|
+
},
|
|
1248
|
+
cleanup(): void {
|
|
1249
|
+
removeParentListener();
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
async function mapWithConcurrency<T, R>(
|
|
1255
|
+
items: readonly T[],
|
|
1256
|
+
maxConcurrent: number,
|
|
1257
|
+
fanout: FanoutAbortController,
|
|
1258
|
+
mapper: (item: T, index: number) => Promise<R>
|
|
1259
|
+
): Promise<R[]> {
|
|
1260
|
+
if (items.length === 0) {
|
|
1261
|
+
return [];
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
const results: R[] = new Array(items.length);
|
|
1265
|
+
let nextIndex = 0;
|
|
1266
|
+
let firstError: unknown;
|
|
1267
|
+
const workerCount = Math.min(maxConcurrent, items.length);
|
|
1268
|
+
|
|
1269
|
+
await Promise.all(Array.from({ length: workerCount }, async () => {
|
|
1270
|
+
while (nextIndex < items.length && firstError === undefined) {
|
|
1271
|
+
const index = nextIndex;
|
|
1272
|
+
nextIndex += 1;
|
|
1273
|
+
try {
|
|
1274
|
+
results[index] = await mapper(items[index]!, index);
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
firstError ??= error;
|
|
1277
|
+
fanout.abort(error);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}));
|
|
1281
|
+
|
|
1282
|
+
if (firstError !== undefined) {
|
|
1283
|
+
throw firstError;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
return results;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1188
1289
|
interface DispatchDelegateOptions {
|
|
1189
1290
|
readonly decision: DelegateAgentDecision;
|
|
1190
1291
|
readonly childRunId?: string;
|
|
@@ -1230,7 +1331,7 @@ interface DispatchedChild {
|
|
|
1230
1331
|
startedAtMs: number;
|
|
1231
1332
|
childTimeoutMs: number | undefined;
|
|
1232
1333
|
failure: DispatchWaveFailure | undefined;
|
|
1233
|
-
/**
|
|
1334
|
+
/** Reserved child stream handle slot; do not use. */
|
|
1234
1335
|
readonly streamHandle?: never;
|
|
1235
1336
|
}
|
|
1236
1337
|
|
|
@@ -1316,7 +1417,7 @@ async function dispatchDelegate(input: DispatchDelegateOptions): Promise<Dispatc
|
|
|
1316
1417
|
});
|
|
1317
1418
|
}
|
|
1318
1419
|
|
|
1319
|
-
// Buffered tee
|
|
1420
|
+
// Buffered tee captures the child event prefix used for partialTrace.
|
|
1320
1421
|
const childEvents = input.dispatchedChild.childEvents;
|
|
1321
1422
|
const parentEmit = input.emit;
|
|
1322
1423
|
const teedEmit = (event: RunEvent): void => {
|
|
@@ -1373,7 +1474,7 @@ async function dispatchDelegate(input: DispatchDelegateOptions): Promise<Dispatc
|
|
|
1373
1474
|
// BUDGET-01 / D-07: derive a per-child AbortController so child engines see
|
|
1374
1475
|
// their own signal. Listener forwards parent.signal.reason verbatim, so
|
|
1375
1476
|
// detail.reason classification (parent-aborted vs timeout) is preserved.
|
|
1376
|
-
//
|
|
1477
|
+
// Reserved per-child stream cancel hook attaches here if that surface ships.
|
|
1377
1478
|
const parentSignal = options.signal;
|
|
1378
1479
|
let removeParentAbortListener: (() => void) | undefined;
|
|
1379
1480
|
if (parentSignal !== undefined) {
|
|
@@ -1404,6 +1505,7 @@ async function dispatchDelegate(input: DispatchDelegateOptions): Promise<Dispatc
|
|
|
1404
1505
|
: undefined;
|
|
1405
1506
|
|
|
1406
1507
|
const childOptions = {
|
|
1508
|
+
runId: childRunId,
|
|
1407
1509
|
intent: decision.intent,
|
|
1408
1510
|
protocol: decision.protocol,
|
|
1409
1511
|
tier: options.tier,
|
|
@@ -1420,6 +1522,9 @@ async function dispatchDelegate(input: DispatchDelegateOptions): Promise<Dispatc
|
|
|
1420
1522
|
...(options.effectiveMaxConcurrentChildren !== undefined
|
|
1421
1523
|
? { effectiveMaxConcurrentChildren: options.effectiveMaxConcurrentChildren }
|
|
1422
1524
|
: {}),
|
|
1525
|
+
...(options.effectiveMaxConcurrentAgentTurns !== undefined
|
|
1526
|
+
? { effectiveMaxConcurrentAgentTurns: options.effectiveMaxConcurrentAgentTurns }
|
|
1527
|
+
: {}),
|
|
1423
1528
|
...(options.onChildFailure !== undefined ? { onChildFailure: options.onChildFailure } : {}),
|
|
1424
1529
|
// BUDGET-02 / D-12: forward the ROOT deadline so depth-N grandchildren
|
|
1425
1530
|
// see the same `parentDeadlineMs` rather than a fresh per-level snapshot.
|
|
@@ -1614,8 +1719,8 @@ async function dispatchDelegate(input: DispatchDelegateOptions): Promise<Dispatc
|
|
|
1614
1719
|
function renderSubRunResult(childRunId: string, subResult: RunResult): string {
|
|
1615
1720
|
const turns = subResult.transcript.length;
|
|
1616
1721
|
const costUsd = subResult.cost.usd ?? 0;
|
|
1617
|
-
const startedAt = subResult.trace.events[0]
|
|
1618
|
-
const endedAt = subResult.trace.events.at(-1)
|
|
1722
|
+
const startedAt = eventTimestamp(subResult.trace.events[0]);
|
|
1723
|
+
const endedAt = eventTimestamp(subResult.trace.events.at(-1));
|
|
1619
1724
|
const durationMs =
|
|
1620
1725
|
startedAt && endedAt
|
|
1621
1726
|
? Math.max(0, Date.parse(endedAt) - Date.parse(startedAt))
|
|
@@ -1626,10 +1731,16 @@ function renderSubRunResult(childRunId: string, subResult: RunResult): string {
|
|
|
1626
1731
|
].join("\n");
|
|
1627
1732
|
}
|
|
1628
1733
|
|
|
1734
|
+
function eventTimestamp(event: RunEvent | undefined): string | undefined {
|
|
1735
|
+
if (event === undefined) return undefined;
|
|
1736
|
+
if ("at" in event) return event.at;
|
|
1737
|
+
return event.type === "model-response" ? event.completedAt : event.startedAt;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1629
1740
|
/**
|
|
1630
1741
|
* Build a JSON-serializable {@link Trace} for `sub-run-failed.partialTrace`
|
|
1631
1742
|
* from a buffered tee of child emits. Keeps `runProtocol`'s error contract
|
|
1632
|
-
* unchanged
|
|
1743
|
+
* unchanged.
|
|
1633
1744
|
*/
|
|
1634
1745
|
function buildPartialTrace(input: {
|
|
1635
1746
|
readonly childRunId: string;
|
package/src/runtime/defaults.ts
CHANGED
|
@@ -236,8 +236,8 @@ export function createRunMetadata(options: {
|
|
|
236
236
|
tier: options.tier,
|
|
237
237
|
modelProviderId: options.modelProviderId,
|
|
238
238
|
agentsUsed: options.agentsUsed,
|
|
239
|
-
startedAt: firstEvent
|
|
240
|
-
completedAt: lastEvent
|
|
239
|
+
startedAt: eventTimestamp(firstEvent) ?? "",
|
|
240
|
+
completedAt: eventTimestamp(lastEvent) ?? ""
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
|
|
@@ -365,7 +365,7 @@ export function createReplayTraceProtocolDecision(
|
|
|
365
365
|
eventType: event.type,
|
|
366
366
|
protocol,
|
|
367
367
|
decision: options.decision ?? defaultProtocolDecision(event),
|
|
368
|
-
at: event
|
|
368
|
+
at: eventTimestamp(event),
|
|
369
369
|
...(options.turn !== undefined ? { turn: options.turn } : {}),
|
|
370
370
|
...(options.phase !== undefined ? { phase: options.phase } : {}),
|
|
371
371
|
...(options.round !== undefined ? { round: options.round } : {}),
|
|
@@ -404,7 +404,8 @@ export function createReplayTraceProtocolDecision(
|
|
|
404
404
|
agentId: event.agentId,
|
|
405
405
|
role: event.role,
|
|
406
406
|
input: event.input,
|
|
407
|
-
output: event.
|
|
407
|
+
output: event.text,
|
|
408
|
+
outputLength: event.outputLength
|
|
408
409
|
};
|
|
409
410
|
case "tool-call":
|
|
410
411
|
return {
|
|
@@ -550,7 +551,7 @@ export function createReplayTraceFinalOutput(output: string, event: RunEvent): R
|
|
|
550
551
|
kind: "replay-trace-final-output",
|
|
551
552
|
output,
|
|
552
553
|
cost: emptyCost(),
|
|
553
|
-
completedAt: event
|
|
554
|
+
completedAt: eventTimestamp(event),
|
|
554
555
|
transcript: {
|
|
555
556
|
kind: "trace-transcript",
|
|
556
557
|
entryCount: 0,
|
|
@@ -559,6 +560,14 @@ export function createReplayTraceFinalOutput(output: string, event: RunEvent): R
|
|
|
559
560
|
};
|
|
560
561
|
}
|
|
561
562
|
|
|
563
|
+
function eventTimestamp(event: RunEvent): string;
|
|
564
|
+
function eventTimestamp(event: RunEvent | undefined): string | undefined;
|
|
565
|
+
function eventTimestamp(event: RunEvent | undefined): string | undefined {
|
|
566
|
+
if (event === undefined) return undefined;
|
|
567
|
+
if ("at" in event) return event.at;
|
|
568
|
+
return event.type === "model-response" ? event.completedAt : event.startedAt;
|
|
569
|
+
}
|
|
570
|
+
|
|
562
571
|
export function nextProviderCallId(
|
|
563
572
|
runId: string,
|
|
564
573
|
providerCalls: readonly ReplayTraceProviderCall[]
|
|
@@ -589,6 +598,7 @@ export function canonicalizeRunResult(result: RunResult): RunResult {
|
|
|
589
598
|
cost: canonicalizeSerializable(result.cost),
|
|
590
599
|
...(result.evaluation !== undefined ? { evaluation: canonicalizeSerializable(result.evaluation) } : {}),
|
|
591
600
|
eventLog,
|
|
601
|
+
health: canonicalizeSerializable(result.health),
|
|
592
602
|
metadata: canonicalizeSerializable(result.metadata),
|
|
593
603
|
output: result.output,
|
|
594
604
|
...(result.quality !== undefined ? { quality: canonicalizeSerializable(result.quality) } : {}),
|