@oni.bot/core 0.8.0 → 1.0.1
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 +15 -0
- package/README.md +59 -373
- package/dist/checkpointers/sqlite.d.ts.map +1 -1
- package/dist/checkpointers/sqlite.js +23 -8
- package/dist/checkpointers/sqlite.js.map +1 -1
- package/dist/cli/build.d.ts +11 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/build.js +61 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/dev.d.ts +5 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +54 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.js +16 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +18 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/inspect.d.ts +5 -0
- package/dist/cli/inspect.d.ts.map +1 -0
- package/dist/cli/inspect.js +85 -0
- package/dist/cli/inspect.js.map +1 -0
- package/dist/cli/router.d.ts +14 -0
- package/dist/cli/router.d.ts.map +1 -0
- package/dist/cli/router.js +107 -0
- package/dist/cli/router.js.map +1 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +53 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/templates.d.ts +1 -0
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +106 -66
- package/dist/cli/templates.js.map +1 -1
- package/dist/cli/test.d.ts +3 -0
- package/dist/cli/test.d.ts.map +1 -0
- package/dist/cli/test.js +29 -0
- package/dist/cli/test.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +37 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +180 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +74 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +5 -0
- package/dist/config/types.js.map +1 -0
- package/dist/coordination/pubsub.d.ts +4 -0
- package/dist/coordination/pubsub.d.ts.map +1 -1
- package/dist/coordination/pubsub.js +8 -0
- package/dist/coordination/pubsub.js.map +1 -1
- package/dist/coordination/request-reply.d.ts +3 -1
- package/dist/coordination/request-reply.d.ts.map +1 -1
- package/dist/coordination/request-reply.js +11 -2
- package/dist/coordination/request-reply.js.map +1 -1
- package/dist/errors.d.ts +7 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +18 -3
- package/dist/errors.js.map +1 -1
- package/dist/events/bridge.d.ts +17 -0
- package/dist/events/bridge.d.ts.map +1 -0
- package/dist/events/bridge.js +67 -0
- package/dist/events/bridge.js.map +1 -0
- package/dist/events/bus.d.ts +9 -0
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +39 -4
- package/dist/events/bus.js.map +1 -1
- package/dist/events/index.d.ts +2 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -1
- package/dist/events/types.d.ts +123 -1
- package/dist/events/types.d.ts.map +1 -1
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +16 -8
- package/dist/graph.js.map +1 -1
- package/dist/guardrails/types.d.ts +1 -1
- package/dist/guardrails/types.d.ts.map +1 -1
- package/dist/harness/agent-loop.d.ts.map +1 -1
- package/dist/harness/agent-loop.js +226 -29
- package/dist/harness/agent-loop.js.map +1 -1
- package/dist/harness/context-compactor.d.ts +21 -2
- package/dist/harness/context-compactor.d.ts.map +1 -1
- package/dist/harness/context-compactor.js +45 -6
- package/dist/harness/context-compactor.js.map +1 -1
- package/dist/harness/hooks-engine.d.ts +7 -1
- package/dist/harness/hooks-engine.d.ts.map +1 -1
- package/dist/harness/hooks-engine.js.map +1 -1
- package/dist/harness/index.d.ts +3 -2
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +2 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/skill-loader.d.ts +14 -1
- package/dist/harness/skill-loader.d.ts.map +1 -1
- package/dist/harness/skill-loader.js +33 -3
- package/dist/harness/skill-loader.js.map +1 -1
- package/dist/harness/types.d.ts +24 -2
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/validate-args.d.ts +16 -0
- package/dist/harness/validate-args.d.ts.map +1 -0
- package/dist/harness/validate-args.js +132 -0
- package/dist/harness/validate-args.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/internal/timeout.d.ts +2 -0
- package/dist/internal/timeout.d.ts.map +1 -0
- package/dist/internal/timeout.js +16 -0
- package/dist/internal/timeout.js.map +1 -0
- package/dist/lsp/client.d.ts +70 -0
- package/dist/lsp/client.d.ts.map +1 -0
- package/dist/lsp/client.js +421 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/lsp/index.d.ts +77 -0
- package/dist/lsp/index.d.ts.map +1 -0
- package/dist/lsp/index.js +183 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp/servers.d.ts +48 -0
- package/dist/lsp/servers.d.ts.map +1 -0
- package/dist/lsp/servers.js +108 -0
- package/dist/lsp/servers.js.map +1 -0
- package/dist/lsp/types.d.ts +142 -0
- package/dist/lsp/types.d.ts.map +1 -0
- package/dist/lsp/types.js +16 -0
- package/dist/lsp/types.js.map +1 -0
- package/dist/mcp/client.d.ts +56 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +170 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/convert.d.ts +26 -0
- package/dist/mcp/convert.d.ts.map +1 -0
- package/dist/mcp/convert.js +56 -0
- package/dist/mcp/convert.js.map +1 -0
- package/dist/mcp/index.d.ts +21 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +19 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/transport.d.ts +56 -0
- package/dist/mcp/transport.d.ts.map +1 -0
- package/dist/mcp/transport.js +204 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/mcp/types.d.ts +96 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +11 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/models/anthropic.d.ts.map +1 -1
- package/dist/models/anthropic.js +78 -41
- package/dist/models/anthropic.js.map +1 -1
- package/dist/models/google.d.ts.map +1 -1
- package/dist/models/google.js +47 -46
- package/dist/models/google.js.map +1 -1
- package/dist/models/http-error.d.ts +16 -0
- package/dist/models/http-error.d.ts.map +1 -0
- package/dist/models/http-error.js +67 -0
- package/dist/models/http-error.js.map +1 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -1
- package/dist/models/ollama.d.ts.map +1 -1
- package/dist/models/ollama.js +4 -3
- package/dist/models/ollama.js.map +1 -1
- package/dist/models/openai.d.ts.map +1 -1
- package/dist/models/openai.js +36 -44
- package/dist/models/openai.js.map +1 -1
- package/dist/models/openrouter.d.ts +13 -0
- package/dist/models/openrouter.d.ts.map +1 -1
- package/dist/models/openrouter.js +86 -47
- package/dist/models/openrouter.js.map +1 -1
- package/dist/models/sse.d.ts +9 -0
- package/dist/models/sse.d.ts.map +1 -0
- package/dist/models/sse.js +45 -0
- package/dist/models/sse.js.map +1 -0
- package/dist/models/types.d.ts +10 -0
- package/dist/models/types.d.ts.map +1 -1
- package/dist/pregel.d.ts +4 -0
- package/dist/pregel.d.ts.map +1 -1
- package/dist/pregel.js +121 -54
- package/dist/pregel.js.map +1 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +10 -3
- package/dist/retry.js.map +1 -1
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +17 -0
- package/dist/store/index.js.map +1 -1
- package/dist/streaming.d.ts +4 -1
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +21 -7
- package/dist/streaming.js.map +1 -1
- package/dist/swarm/graph.d.ts +81 -2
- package/dist/swarm/graph.d.ts.map +1 -1
- package/dist/swarm/graph.js +517 -32
- package/dist/swarm/graph.js.map +1 -1
- package/dist/swarm/index.d.ts +10 -2
- package/dist/swarm/index.d.ts.map +1 -1
- package/dist/swarm/index.js +6 -1
- package/dist/swarm/index.js.map +1 -1
- package/dist/swarm/mermaid.d.ts +10 -0
- package/dist/swarm/mermaid.d.ts.map +1 -0
- package/dist/swarm/mermaid.js +64 -0
- package/dist/swarm/mermaid.js.map +1 -0
- package/dist/swarm/pool.d.ts +9 -1
- package/dist/swarm/pool.d.ts.map +1 -1
- package/dist/swarm/pool.js +58 -10
- package/dist/swarm/pool.js.map +1 -1
- package/dist/swarm/registry.d.ts +11 -1
- package/dist/swarm/registry.d.ts.map +1 -1
- package/dist/swarm/registry.js +17 -3
- package/dist/swarm/registry.js.map +1 -1
- package/dist/swarm/scaling.d.ts +95 -0
- package/dist/swarm/scaling.d.ts.map +1 -0
- package/dist/swarm/scaling.js +214 -0
- package/dist/swarm/scaling.js.map +1 -0
- package/dist/swarm/snapshot.d.ts +51 -0
- package/dist/swarm/snapshot.d.ts.map +1 -0
- package/dist/swarm/snapshot.js +115 -0
- package/dist/swarm/snapshot.js.map +1 -0
- package/dist/swarm/supervisor.d.ts.map +1 -1
- package/dist/swarm/supervisor.js +82 -0
- package/dist/swarm/supervisor.js.map +1 -1
- package/dist/swarm/tracer.d.ts +57 -0
- package/dist/swarm/tracer.d.ts.map +1 -0
- package/dist/swarm/tracer.js +138 -0
- package/dist/swarm/tracer.js.map +1 -0
- package/dist/swarm/types.d.ts +23 -1
- package/dist/swarm/types.d.ts.map +1 -1
- package/dist/swarm/types.js.map +1 -1
- package/dist/tools/types.d.ts +2 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/package.json +160 -141
package/dist/swarm/graph.js
CHANGED
|
@@ -16,6 +16,7 @@ import { createSupervisorNode } from "./supervisor.js";
|
|
|
16
16
|
import { getInbox } from "./mailbox.js";
|
|
17
17
|
import { RequestReplyBroker } from "../coordination/request-reply.js";
|
|
18
18
|
import { PubSub } from "../coordination/pubsub.js";
|
|
19
|
+
import { runWithTimeout } from "../internal/timeout.js";
|
|
19
20
|
export const baseSwarmChannels = {
|
|
20
21
|
task: lastValue(() => ""),
|
|
21
22
|
context: mergeObject(() => ({})),
|
|
@@ -72,6 +73,8 @@ export class SwarmGraph {
|
|
|
72
73
|
rules: config.supervisor.rules,
|
|
73
74
|
systemPrompt: config.supervisor.systemPrompt,
|
|
74
75
|
maxRounds: config.supervisor.maxRounds,
|
|
76
|
+
deadlineMs: config.supervisor.deadlineMs,
|
|
77
|
+
autoRecover: config.supervisor.autoRecover,
|
|
75
78
|
});
|
|
76
79
|
return swarm;
|
|
77
80
|
}
|
|
@@ -82,29 +85,66 @@ export class SwarmGraph {
|
|
|
82
85
|
*/
|
|
83
86
|
static fanOut(config) {
|
|
84
87
|
const swarm = new SwarmGraph(config.channels);
|
|
88
|
+
const agentIds = config.agents.map((a) => a.id);
|
|
89
|
+
const maxConcurrency = config.maxConcurrency;
|
|
90
|
+
const timeoutMs = config.timeoutMs;
|
|
91
|
+
const weights = config.weights;
|
|
92
|
+
// Register agents in registry (but don't use addAgent — we wire manually)
|
|
85
93
|
for (const agentDef of config.agents) {
|
|
86
|
-
swarm.
|
|
94
|
+
swarm.registry.register(agentDef);
|
|
95
|
+
swarm.agentIds.add(agentDef.id);
|
|
87
96
|
}
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
// Single orchestrator node that runs agents with concurrency/timeout control
|
|
98
|
+
swarm.inner.addNode("__fanout_runner__", async (state, cfg) => {
|
|
99
|
+
const agentMap = new Map(config.agents.map((a) => [a.id, a]));
|
|
100
|
+
async function runAgent(id) {
|
|
101
|
+
const agent = agentMap.get(id);
|
|
102
|
+
try {
|
|
103
|
+
await agent.hooks?.onStart?.(id, state);
|
|
104
|
+
const result = await runWithTimeout(() => agent.skeleton.invoke({ ...state }, { ...cfg, agentId: id }), timeoutMs, () => new Error(`Agent "${id}" timed out after ${timeoutMs}ms`));
|
|
105
|
+
await agent.hooks?.onComplete?.(id, result);
|
|
106
|
+
return { id, result, error: null };
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
await agent.hooks?.onError?.(id, err);
|
|
110
|
+
return { id, result: null, error: err };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
let allResults;
|
|
114
|
+
if (maxConcurrency != null && maxConcurrency > 0) {
|
|
115
|
+
// Batched execution with concurrency limit
|
|
116
|
+
allResults = [];
|
|
117
|
+
const remaining = [...agentIds];
|
|
118
|
+
while (remaining.length > 0) {
|
|
119
|
+
const batch = remaining.splice(0, maxConcurrency);
|
|
120
|
+
const batchResults = await Promise.all(batch.map(runAgent));
|
|
121
|
+
allResults.push(...batchResults);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// All in parallel
|
|
126
|
+
allResults = await Promise.all(agentIds.map(runAgent));
|
|
127
|
+
}
|
|
128
|
+
// Collect results
|
|
129
|
+
const agentResults = { ...(state.agentResults ?? {}) };
|
|
130
|
+
for (const { id, result, error } of allResults) {
|
|
131
|
+
if (error) {
|
|
132
|
+
agentResults[id] = {
|
|
133
|
+
_error: true,
|
|
134
|
+
agent: id,
|
|
135
|
+
error: String(error instanceof Error ? error.message : error),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
agentResults[id] = result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Run reducer with weights
|
|
143
|
+
const reduced = config.reducer(agentResults, weights);
|
|
144
|
+
return { ...reduced, agentResults };
|
|
97
145
|
});
|
|
98
|
-
|
|
99
|
-
swarm.inner.addEdge(
|
|
100
|
-
// __fanout__ → Send to all agents in parallel
|
|
101
|
-
swarm.inner.addConditionalEdges("__fanout__", ((state) => agentIds.map((id) => new Send(id, { ...state }))));
|
|
102
|
-
// Each agent → __reducer__
|
|
103
|
-
for (const id of agentIds) {
|
|
104
|
-
swarm.inner.addEdge(id, "__reducer__");
|
|
105
|
-
}
|
|
106
|
-
// __reducer__ → END
|
|
107
|
-
swarm.inner.addEdge("__reducer__", END);
|
|
146
|
+
swarm.inner.addEdge(START, "__fanout_runner__");
|
|
147
|
+
swarm.inner.addEdge("__fanout_runner__", END);
|
|
108
148
|
return swarm;
|
|
109
149
|
}
|
|
110
150
|
// ---- Static factory: pipeline template ----
|
|
@@ -220,6 +260,8 @@ export class SwarmGraph {
|
|
|
220
260
|
for (const debater of config.debaters) {
|
|
221
261
|
swarm.addAgent(debater);
|
|
222
262
|
}
|
|
263
|
+
const scoreDebaters = config.judge.scoreDebaters ?? false;
|
|
264
|
+
const consensusThreshold = config.judge.consensusThreshold;
|
|
223
265
|
// Judge node: evaluates arguments, decides continue or consensus
|
|
224
266
|
swarm.inner.addNode("__judge__", async (state) => {
|
|
225
267
|
const round = state.supervisorRound ?? 0;
|
|
@@ -234,21 +276,61 @@ export class SwarmGraph {
|
|
|
234
276
|
const argsText = Object.entries(results)
|
|
235
277
|
.map(([id, r]) => `${id}: ${JSON.stringify(r)}`)
|
|
236
278
|
.join("\n\n");
|
|
279
|
+
const scoreInstruction = scoreDebaters
|
|
280
|
+
? `\nAlso provide a JSON object with scores for each debater and a verdict. Format: {"scores": {"debater_id": score}, "verdict": "CONTINUE" or "${consensusKeyword}"}`
|
|
281
|
+
: "";
|
|
237
282
|
const response = await config.judge.model.chat({
|
|
238
283
|
messages: [{
|
|
239
284
|
role: "user",
|
|
240
|
-
content: `Round ${round}. Evaluate these arguments:\n\n${argsText}\n\nRespond "${consensusKeyword}" if consensus reached, otherwise "CONTINUE"
|
|
285
|
+
content: `Round ${round}. Evaluate these arguments:\n\n${argsText}\n\nRespond "${consensusKeyword}" if consensus reached, otherwise "CONTINUE".${scoreInstruction}`,
|
|
241
286
|
}],
|
|
242
287
|
systemPrompt: config.judge.systemPrompt ?? "You are a debate judge.",
|
|
243
288
|
});
|
|
244
|
-
|
|
289
|
+
let isConsensus = false;
|
|
290
|
+
let roundScores;
|
|
291
|
+
const existingScores = (state.context.debateScores ?? []);
|
|
292
|
+
// Try to parse structured response (JSON with scores + verdict)
|
|
293
|
+
if (scoreDebaters) {
|
|
294
|
+
try {
|
|
295
|
+
const parsed = JSON.parse(response.content);
|
|
296
|
+
if (parsed.scores) {
|
|
297
|
+
roundScores = parsed.scores;
|
|
298
|
+
}
|
|
299
|
+
if (parsed.verdict) {
|
|
300
|
+
isConsensus = parsed.verdict.includes(consensusKeyword);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Fallback to keyword detection
|
|
305
|
+
isConsensus = response.content.includes(consensusKeyword);
|
|
306
|
+
}
|
|
307
|
+
// Check consensus threshold if scores available
|
|
308
|
+
if (!isConsensus && roundScores && consensusThreshold != null) {
|
|
309
|
+
const scoreValues = Object.values(roundScores);
|
|
310
|
+
if (scoreValues.length >= 2) {
|
|
311
|
+
const spread = Math.max(...scoreValues) - Math.min(...scoreValues);
|
|
312
|
+
if (spread <= consensusThreshold) {
|
|
313
|
+
isConsensus = true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
isConsensus = response.content.includes(consensusKeyword);
|
|
320
|
+
}
|
|
245
321
|
const nextRound = round + 1;
|
|
246
322
|
// Force done if consensus or max rounds exhausted
|
|
247
|
-
// (round 0 is kick-off only, so chat is called maxRounds times)
|
|
248
323
|
const isDone = isConsensus || nextRound > config.judge.maxRounds;
|
|
324
|
+
const updatedScores = roundScores
|
|
325
|
+
? [...existingScores, { round, scores: roundScores }]
|
|
326
|
+
: existingScores;
|
|
249
327
|
return {
|
|
250
328
|
done: isDone,
|
|
251
329
|
supervisorRound: nextRound,
|
|
330
|
+
context: {
|
|
331
|
+
...(state.context ?? {}),
|
|
332
|
+
...(scoreDebaters ? { debateScores: updatedScores } : {}),
|
|
333
|
+
},
|
|
252
334
|
messages: [{ role: "system", content: `Judge round ${round}: ${isDone ? "Consensus" : "Continue"}` }],
|
|
253
335
|
};
|
|
254
336
|
});
|
|
@@ -364,6 +446,248 @@ export class SwarmGraph {
|
|
|
364
446
|
});
|
|
365
447
|
return swarm;
|
|
366
448
|
}
|
|
449
|
+
// ---- Static factory: race template ----
|
|
450
|
+
/**
|
|
451
|
+
* Race all agents in parallel — first acceptable result wins.
|
|
452
|
+
* Optionally filter results with an `accept` predicate.
|
|
453
|
+
*/
|
|
454
|
+
static race(config) {
|
|
455
|
+
const swarm = new SwarmGraph(config.channels);
|
|
456
|
+
const agentIds = config.agents.map((a) => a.id);
|
|
457
|
+
const accept = config.accept ?? (() => true);
|
|
458
|
+
const timeoutMs = config.timeoutMs;
|
|
459
|
+
// Register agents so they appear in the registry
|
|
460
|
+
for (const agentDef of config.agents) {
|
|
461
|
+
swarm.registry.register(agentDef);
|
|
462
|
+
swarm.agentIds.add(agentDef.id);
|
|
463
|
+
}
|
|
464
|
+
// Single node that races all agents
|
|
465
|
+
swarm.inner.addNode("__race__", async (state, cfg) => {
|
|
466
|
+
const promises = config.agents.map(async (agent) => {
|
|
467
|
+
try {
|
|
468
|
+
const result = await agent.skeleton.invoke({ ...state }, { ...cfg, agentId: agent.id });
|
|
469
|
+
return { id: agent.id, result, error: null };
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
return { id: agent.id, result: null, error: err };
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
let allResults;
|
|
476
|
+
if (timeoutMs != null) {
|
|
477
|
+
// Wrap each promise with a timeout
|
|
478
|
+
const timedPromises = promises.map((p) => Promise.race([
|
|
479
|
+
p,
|
|
480
|
+
new Promise((resolve) => setTimeout(() => resolve({ id: "?", result: null, error: new Error("timeout") }), timeoutMs)),
|
|
481
|
+
]));
|
|
482
|
+
allResults = await Promise.all(timedPromises);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
allResults = await Promise.all(promises);
|
|
486
|
+
}
|
|
487
|
+
// Find the first acceptable result (in order of agent definition)
|
|
488
|
+
for (const { id, result, error } of allResults) {
|
|
489
|
+
if (!error && accept(result)) {
|
|
490
|
+
return {
|
|
491
|
+
agentResults: { [id]: result },
|
|
492
|
+
context: { ...(state.context ?? {}), raceWinner: id },
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// No acceptable result
|
|
497
|
+
return {
|
|
498
|
+
context: {
|
|
499
|
+
...(state.context ?? {}),
|
|
500
|
+
raceError: "No agent produced an acceptable result",
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
});
|
|
504
|
+
swarm.inner.addEdge(START, "__race__");
|
|
505
|
+
swarm.inner.addEdge("__race__", END);
|
|
506
|
+
return swarm;
|
|
507
|
+
}
|
|
508
|
+
// ---- Static factory: dag template ----
|
|
509
|
+
/**
|
|
510
|
+
* Execute agents in dependency order (directed acyclic graph).
|
|
511
|
+
* Agents with no dependencies run in parallel.
|
|
512
|
+
*/
|
|
513
|
+
static dag(config) {
|
|
514
|
+
const agentMap = new Map(config.agents.map((a) => [a.id, a]));
|
|
515
|
+
// Validate dependencies
|
|
516
|
+
for (const [node, deps] of Object.entries(config.dependencies)) {
|
|
517
|
+
for (const dep of deps) {
|
|
518
|
+
if (!agentMap.has(dep)) {
|
|
519
|
+
throw new Error(`Dependency "${dep}" not found in agents list.`);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// Cycle detection via topological sort
|
|
524
|
+
const visited = new Set();
|
|
525
|
+
const visiting = new Set();
|
|
526
|
+
const sorted = [];
|
|
527
|
+
function visit(id) {
|
|
528
|
+
if (visited.has(id))
|
|
529
|
+
return;
|
|
530
|
+
if (visiting.has(id))
|
|
531
|
+
throw new Error(`Cycle detected involving "${id}".`);
|
|
532
|
+
visiting.add(id);
|
|
533
|
+
for (const dep of config.dependencies[id] ?? []) {
|
|
534
|
+
visit(dep);
|
|
535
|
+
}
|
|
536
|
+
visiting.delete(id);
|
|
537
|
+
visited.add(id);
|
|
538
|
+
sorted.push(id);
|
|
539
|
+
}
|
|
540
|
+
for (const agent of config.agents) {
|
|
541
|
+
visit(agent.id);
|
|
542
|
+
}
|
|
543
|
+
// Build a standard swarm with wiring based on topological sort
|
|
544
|
+
const swarm = new SwarmGraph(config.channels);
|
|
545
|
+
for (const agent of config.agents) {
|
|
546
|
+
swarm.addAgent(agent);
|
|
547
|
+
}
|
|
548
|
+
// Group agents into layers based on dependencies
|
|
549
|
+
const deps = config.dependencies;
|
|
550
|
+
const rootIds = config.agents.filter((a) => !deps[a.id]?.length).map((a) => a.id);
|
|
551
|
+
const nonRootIds = config.agents.filter((a) => deps[a.id]?.length).map((a) => a.id);
|
|
552
|
+
// For DAG execution: use a single orchestrator node
|
|
553
|
+
swarm.inner.addNode("__dag_runner__", async (state, cfg) => {
|
|
554
|
+
const results = {};
|
|
555
|
+
const completed = new Set();
|
|
556
|
+
// Process in topological order
|
|
557
|
+
// Group into parallel batches
|
|
558
|
+
const remaining = new Set(sorted);
|
|
559
|
+
while (remaining.size > 0) {
|
|
560
|
+
// Find all nodes whose deps are satisfied
|
|
561
|
+
const ready = [];
|
|
562
|
+
for (const id of remaining) {
|
|
563
|
+
const idDeps = deps[id] ?? [];
|
|
564
|
+
if (idDeps.every((d) => completed.has(d))) {
|
|
565
|
+
ready.push(id);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
// Execute ready nodes in parallel
|
|
569
|
+
const batchResults = await Promise.all(ready.map(async (id) => {
|
|
570
|
+
const agent = agentMap.get(id);
|
|
571
|
+
const result = await agent.skeleton.invoke({ ...state, agentResults: { ...(state.agentResults ?? {}), ...results } }, { ...cfg, agentId: id });
|
|
572
|
+
return { id, result };
|
|
573
|
+
}));
|
|
574
|
+
for (const { id, result } of batchResults) {
|
|
575
|
+
results[id] = result;
|
|
576
|
+
completed.add(id);
|
|
577
|
+
remaining.delete(id);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
agentResults: { ...(state.agentResults ?? {}), ...results },
|
|
582
|
+
};
|
|
583
|
+
});
|
|
584
|
+
// Wire: START → __dag_runner__ → END
|
|
585
|
+
// Remove any edges added by addAgent
|
|
586
|
+
swarm.inner.edges = [];
|
|
587
|
+
swarm.inner.addEdge(START, "__dag_runner__");
|
|
588
|
+
swarm.inner.addEdge("__dag_runner__", END);
|
|
589
|
+
return swarm;
|
|
590
|
+
}
|
|
591
|
+
// ---- Static factory: pool template ----
|
|
592
|
+
/**
|
|
593
|
+
* Distribute N input items across poolSize copies of a single agent,
|
|
594
|
+
* then reduce all results.
|
|
595
|
+
*/
|
|
596
|
+
static pool(config) {
|
|
597
|
+
const swarm = new SwarmGraph(config.channels);
|
|
598
|
+
const poolSize = config.poolSize;
|
|
599
|
+
// Create pool copies
|
|
600
|
+
const poolIds = [];
|
|
601
|
+
for (let i = 0; i < poolSize; i++) {
|
|
602
|
+
const id = poolSize === 1 ? config.agent.id : `${config.agent.id}_${i}`;
|
|
603
|
+
poolIds.push(id);
|
|
604
|
+
swarm.addAgent({ ...config.agent, id });
|
|
605
|
+
}
|
|
606
|
+
// Orchestrator node: dispatches items to pool agents and reduces
|
|
607
|
+
swarm.inner.addNode("__pool_runner__", async (state, cfg) => {
|
|
608
|
+
const items = state[config.inputField];
|
|
609
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
610
|
+
return config.reducer({});
|
|
611
|
+
}
|
|
612
|
+
// Semaphore for concurrency control
|
|
613
|
+
let running = 0;
|
|
614
|
+
const results = {};
|
|
615
|
+
const queue = items.map((item, idx) => ({
|
|
616
|
+
item,
|
|
617
|
+
idx,
|
|
618
|
+
targetId: poolIds[idx % poolIds.length],
|
|
619
|
+
}));
|
|
620
|
+
await new Promise((resolve, reject) => {
|
|
621
|
+
let completed = 0;
|
|
622
|
+
const total = queue.length;
|
|
623
|
+
function processNext() {
|
|
624
|
+
while (running < poolSize && queue.length > 0) {
|
|
625
|
+
const work = queue.shift();
|
|
626
|
+
running++;
|
|
627
|
+
const agent = swarm.registry.getDef(work.targetId)
|
|
628
|
+
?? config.agent;
|
|
629
|
+
const skeleton = agent.skeleton;
|
|
630
|
+
skeleton.invoke({ ...state, task: String(work.item) }, { ...cfg, agentId: work.targetId }).then((result) => {
|
|
631
|
+
results[`item_${work.idx}`] = result;
|
|
632
|
+
running--;
|
|
633
|
+
completed++;
|
|
634
|
+
if (completed === total)
|
|
635
|
+
resolve();
|
|
636
|
+
else
|
|
637
|
+
processNext();
|
|
638
|
+
}, (err) => {
|
|
639
|
+
results[`item_${work.idx}`] = { _error: String(err) };
|
|
640
|
+
running--;
|
|
641
|
+
completed++;
|
|
642
|
+
if (completed === total)
|
|
643
|
+
resolve();
|
|
644
|
+
else
|
|
645
|
+
processNext();
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (total === 0)
|
|
650
|
+
resolve();
|
|
651
|
+
else
|
|
652
|
+
processNext();
|
|
653
|
+
});
|
|
654
|
+
return config.reducer(results);
|
|
655
|
+
});
|
|
656
|
+
// Wire: START → __pool_runner__ → END (bypass agent edges)
|
|
657
|
+
swarm.inner.edges = [];
|
|
658
|
+
swarm.inner.addEdge(START, "__pool_runner__");
|
|
659
|
+
swarm.inner.addEdge("__pool_runner__", END);
|
|
660
|
+
return swarm;
|
|
661
|
+
}
|
|
662
|
+
// ---- Static factory: compose template ----
|
|
663
|
+
/**
|
|
664
|
+
* Compose multiple sub-swarms as pipeline stages.
|
|
665
|
+
* Each stage runs a compiled sub-swarm, passing state through.
|
|
666
|
+
*/
|
|
667
|
+
static compose(config) {
|
|
668
|
+
const swarm = new SwarmGraph(config.channels);
|
|
669
|
+
const stageIds = config.stages.map((s) => s.id);
|
|
670
|
+
for (const stage of config.stages) {
|
|
671
|
+
const compiled = stage.swarm.compile();
|
|
672
|
+
swarm.inner.addNode(stage.id, async (state, cfg) => {
|
|
673
|
+
const stageResult = await compiled.invoke(state, cfg);
|
|
674
|
+
return {
|
|
675
|
+
...stageResult,
|
|
676
|
+
agentResults: {
|
|
677
|
+
...(state.agentResults ?? {}),
|
|
678
|
+
[stage.id]: stageResult,
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
// Wire pipeline: START → stage1 → stage2 → ... → END
|
|
684
|
+
swarm.inner.addEdge(START, stageIds[0]);
|
|
685
|
+
for (let i = 0; i < stageIds.length - 1; i++) {
|
|
686
|
+
swarm.inner.addEdge(stageIds[i], stageIds[i + 1]);
|
|
687
|
+
}
|
|
688
|
+
swarm.inner.addEdge(stageIds[stageIds.length - 1], END);
|
|
689
|
+
return swarm;
|
|
690
|
+
}
|
|
367
691
|
// ---- Agent registration ----
|
|
368
692
|
addAgent(def) {
|
|
369
693
|
this.registry.register(def);
|
|
@@ -372,27 +696,54 @@ export class SwarmGraph {
|
|
|
372
696
|
// The node executes the agent's skeleton and handles Handoff returns
|
|
373
697
|
const agentNode = async (state, config) => {
|
|
374
698
|
this.registry.markBusy(def.id);
|
|
699
|
+
// Fire onStart hook
|
|
700
|
+
await def.hooks?.onStart?.(def.id, state);
|
|
375
701
|
const maxRetries = def.maxRetries ?? 2;
|
|
702
|
+
const retryDelayMs = def.retryDelayMs ?? 0;
|
|
376
703
|
let lastError;
|
|
704
|
+
let lastAttempt = 0;
|
|
377
705
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
706
|
+
lastAttempt = attempt;
|
|
378
707
|
try {
|
|
708
|
+
// Get inbox and filter out already-consumed messages
|
|
709
|
+
const consumedIds = (state.context?.__consumedMsgIds ?? []);
|
|
710
|
+
const consumedSet = new Set(consumedIds);
|
|
711
|
+
const allInbox = getInbox(state.swarmMessages ?? [], def.id);
|
|
712
|
+
const inbox = allInbox.filter((m) => !consumedSet.has(m.id));
|
|
713
|
+
// Track consumed message IDs
|
|
714
|
+
const newConsumedIds = [...consumedIds, ...inbox.map((m) => m.id)];
|
|
379
715
|
const agentInput = {
|
|
380
716
|
...state,
|
|
381
717
|
context: {
|
|
382
718
|
...(state.context ?? {}),
|
|
383
|
-
inbox
|
|
719
|
+
inbox,
|
|
720
|
+
__consumedMsgIds: newConsumedIds,
|
|
384
721
|
},
|
|
385
722
|
};
|
|
386
|
-
|
|
723
|
+
let effectiveTimeout = def.timeout;
|
|
724
|
+
// Clamp agent timeout to remaining deadline (if set)
|
|
725
|
+
const deadlineAbs = state.context?.__deadlineAbsolute;
|
|
726
|
+
if (deadlineAbs != null) {
|
|
727
|
+
const remaining = deadlineAbs - Date.now();
|
|
728
|
+
if (remaining <= 0) {
|
|
729
|
+
throw new Error(`Agent "${def.id}" deadline expired`);
|
|
730
|
+
}
|
|
731
|
+
if (effectiveTimeout == null || remaining < effectiveTimeout) {
|
|
732
|
+
effectiveTimeout = remaining;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const result = await runWithTimeout(() => def.skeleton.invoke(agentInput, {
|
|
387
736
|
...config,
|
|
388
737
|
agentId: def.id,
|
|
389
|
-
});
|
|
738
|
+
}), effectiveTimeout, () => new Error(`Agent "${def.id}" timed out after ${effectiveTimeout}ms`));
|
|
390
739
|
this.registry.markIdle(def.id);
|
|
391
740
|
// ---- Handoff detection ----
|
|
392
741
|
if (result instanceof Handoff || (result && result.isHandoff)) {
|
|
393
742
|
const handoff = result instanceof Handoff
|
|
394
743
|
? result
|
|
395
744
|
: new Handoff(result.opts);
|
|
745
|
+
// Fire onComplete for handoffs too
|
|
746
|
+
await def.hooks?.onComplete?.(def.id, result);
|
|
396
747
|
return new Command({
|
|
397
748
|
update: {
|
|
398
749
|
context: { ...(state.context ?? {}), ...(handoff.context ?? {}) },
|
|
@@ -409,9 +760,15 @@ export class SwarmGraph {
|
|
|
409
760
|
goto: handoff.to,
|
|
410
761
|
});
|
|
411
762
|
}
|
|
763
|
+
// Fire onComplete hook
|
|
764
|
+
await def.hooks?.onComplete?.(def.id, result);
|
|
412
765
|
// ---- Normal result ----
|
|
413
766
|
return {
|
|
414
767
|
...result,
|
|
768
|
+
context: {
|
|
769
|
+
...(result.context ?? {}),
|
|
770
|
+
__consumedMsgIds: newConsumedIds,
|
|
771
|
+
},
|
|
415
772
|
agentResults: {
|
|
416
773
|
...(state.agentResults ?? {}),
|
|
417
774
|
[def.id]: result,
|
|
@@ -432,18 +789,43 @@ export class SwarmGraph {
|
|
|
432
789
|
lastError = err;
|
|
433
790
|
this.registry.markError(def.id);
|
|
434
791
|
if (attempt < maxRetries) {
|
|
792
|
+
// Backoff delay between retries — clamped to remaining deadline
|
|
793
|
+
if (retryDelayMs > 0) {
|
|
794
|
+
let delay = retryDelayMs;
|
|
795
|
+
const dl = state.context?.__deadlineAbsolute;
|
|
796
|
+
if (dl != null) {
|
|
797
|
+
const remaining = dl - Date.now();
|
|
798
|
+
if (remaining <= 0)
|
|
799
|
+
break; // deadline expired, stop retrying
|
|
800
|
+
delay = Math.min(delay, remaining);
|
|
801
|
+
}
|
|
802
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
803
|
+
}
|
|
435
804
|
continue; // retry
|
|
436
805
|
}
|
|
437
806
|
}
|
|
438
807
|
}
|
|
439
|
-
// All retries exhausted —
|
|
440
|
-
|
|
808
|
+
// All retries exhausted — fire onError hook
|
|
809
|
+
await def.hooks?.onError?.(def.id, lastError);
|
|
810
|
+
// Keep agent in error status (don't reset to idle)
|
|
811
|
+
// Build structured error context
|
|
812
|
+
const errStr = String(lastError instanceof Error ? lastError.message : lastError);
|
|
813
|
+
const errType = errStr.includes("timeout") ? "timeout" :
|
|
814
|
+
errStr.includes("model") ? "model_error" :
|
|
815
|
+
errStr.includes("tool") ? "tool_error" :
|
|
816
|
+
"unknown";
|
|
441
817
|
if (this.hasSupervisor) {
|
|
442
818
|
return new Command({
|
|
443
819
|
update: {
|
|
444
820
|
context: {
|
|
445
821
|
...(state.context ?? {}),
|
|
446
|
-
lastAgentError: {
|
|
822
|
+
lastAgentError: {
|
|
823
|
+
agent: def.id,
|
|
824
|
+
error: errStr,
|
|
825
|
+
type: errType,
|
|
826
|
+
attempt: lastAttempt,
|
|
827
|
+
maxRetries,
|
|
828
|
+
},
|
|
447
829
|
},
|
|
448
830
|
},
|
|
449
831
|
goto: this.supervisorNodeName,
|
|
@@ -497,6 +879,32 @@ export class SwarmGraph {
|
|
|
497
879
|
this.inner.addEdge(from, to);
|
|
498
880
|
return this;
|
|
499
881
|
}
|
|
882
|
+
// ---- Topology validation ----
|
|
883
|
+
/**
|
|
884
|
+
* Check the swarm topology for common issues like orphan agents.
|
|
885
|
+
* Returns an array of issue strings (empty = valid).
|
|
886
|
+
*/
|
|
887
|
+
validateTopology() {
|
|
888
|
+
const issues = [];
|
|
889
|
+
// In supervised swarms, all agents are reachable via Command.goto
|
|
890
|
+
if (this.hasSupervisor)
|
|
891
|
+
return issues;
|
|
892
|
+
// For non-supervised swarms, check that every agent has at least one incoming edge
|
|
893
|
+
const edgeTargets = new Set();
|
|
894
|
+
for (const edge of this.inner.edges) {
|
|
895
|
+
if (edge.type === "static") {
|
|
896
|
+
edgeTargets.add(edge.to);
|
|
897
|
+
}
|
|
898
|
+
// Conditional edges: the 'from' node is reachable if it has incoming edges
|
|
899
|
+
edgeTargets.add(edge.from);
|
|
900
|
+
}
|
|
901
|
+
for (const agentId of this.agentIds) {
|
|
902
|
+
if (!edgeTargets.has(agentId)) {
|
|
903
|
+
issues.push(`Agent "${agentId}" is an orphan — no edges connect to or from it.`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return issues;
|
|
907
|
+
}
|
|
500
908
|
// ---- Pipeline shorthand ----
|
|
501
909
|
/**
|
|
502
910
|
* Wire agents as a linear pipeline: A → B → C → END
|
|
@@ -518,13 +926,90 @@ export class SwarmGraph {
|
|
|
518
926
|
...(opts.interruptBefore ? { interruptBefore: opts.interruptBefore } : {}),
|
|
519
927
|
...(opts.interruptAfter ? { interruptAfter: opts.interruptAfter } : {}),
|
|
520
928
|
});
|
|
929
|
+
const registry = this.registry;
|
|
930
|
+
const inner = this.inner;
|
|
931
|
+
const hasSupervisor = this.hasSupervisor;
|
|
932
|
+
const supervisorNodeName = this.supervisorNodeName;
|
|
933
|
+
const self = this;
|
|
521
934
|
// Attach swarm-specific extensions
|
|
522
935
|
const extensions = {
|
|
523
|
-
registry
|
|
524
|
-
agentStats: () =>
|
|
525
|
-
toMermaid: () =>
|
|
936
|
+
registry,
|
|
937
|
+
agentStats: () => registry.stats(),
|
|
938
|
+
toMermaid: () => inner.toMermaid(),
|
|
939
|
+
spawnAgent(def) {
|
|
940
|
+
// Register in the registry
|
|
941
|
+
registry.register(def);
|
|
942
|
+
// Create the agent node function (same logic as addAgent)
|
|
943
|
+
const agentNode = async (state, config) => {
|
|
944
|
+
registry.markBusy(def.id);
|
|
945
|
+
await def.hooks?.onStart?.(def.id, state);
|
|
946
|
+
const maxRetries = def.maxRetries ?? 2;
|
|
947
|
+
let lastError;
|
|
948
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
949
|
+
try {
|
|
950
|
+
const result = await def.skeleton.invoke({ ...state, context: { ...(state.context ?? {}), inbox: getInbox(state.swarmMessages ?? [], def.id) } }, { ...config, agentId: def.id });
|
|
951
|
+
registry.markIdle(def.id);
|
|
952
|
+
await def.hooks?.onComplete?.(def.id, result);
|
|
953
|
+
return {
|
|
954
|
+
...result,
|
|
955
|
+
agentResults: { ...(state.agentResults ?? {}), [def.id]: result },
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
catch (err) {
|
|
959
|
+
lastError = err;
|
|
960
|
+
registry.markError(def.id);
|
|
961
|
+
if (attempt < maxRetries)
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
await def.hooks?.onError?.(def.id, lastError);
|
|
966
|
+
// Keep agent in error status (don't reset to idle)
|
|
967
|
+
if (hasSupervisor) {
|
|
968
|
+
return new Command({
|
|
969
|
+
update: { context: { ...(state.context ?? {}), lastAgentError: { agent: def.id, error: String(lastError) } } },
|
|
970
|
+
goto: supervisorNodeName,
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
throw lastError;
|
|
974
|
+
};
|
|
975
|
+
// Add node to the compiled runner's nodes map
|
|
976
|
+
const runner = skeleton._runner;
|
|
977
|
+
if (runner?.nodes) {
|
|
978
|
+
runner.nodes.set(def.id, { name: def.id, fn: agentNode });
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
removeAgent(agentId) {
|
|
982
|
+
registry.deregister(agentId);
|
|
983
|
+
},
|
|
526
984
|
};
|
|
527
985
|
return Object.assign(skeleton, extensions);
|
|
528
986
|
}
|
|
529
987
|
}
|
|
988
|
+
// ----------------------------------------------------------------
|
|
989
|
+
// quickAgent — convenience factory for tests and simple swarms
|
|
990
|
+
// ----------------------------------------------------------------
|
|
991
|
+
/**
|
|
992
|
+
* Create a SwarmAgentDef from just an id and a handler function.
|
|
993
|
+
* Builds a single-node StateGraph internally so the agent is fully
|
|
994
|
+
* functional in any SwarmGraph topology.
|
|
995
|
+
*/
|
|
996
|
+
export function quickAgent(id, fn, opts) {
|
|
997
|
+
const g = new StateGraph({ channels: baseSwarmChannels });
|
|
998
|
+
g.addNode("work", async (state) => fn(state));
|
|
999
|
+
g.addEdge(START, "work");
|
|
1000
|
+
g.addEdge("work", END);
|
|
1001
|
+
const skeleton = g.compile();
|
|
1002
|
+
return {
|
|
1003
|
+
id,
|
|
1004
|
+
role: opts?.role ?? id,
|
|
1005
|
+
capabilities: opts?.capabilities ?? [],
|
|
1006
|
+
skeleton: skeleton,
|
|
1007
|
+
hooks: opts?.hooks,
|
|
1008
|
+
maxRetries: opts?.maxRetries,
|
|
1009
|
+
maxConcurrency: opts?.maxConcurrency,
|
|
1010
|
+
systemPrompt: opts?.systemPrompt,
|
|
1011
|
+
timeout: opts?.timeout,
|
|
1012
|
+
retryDelayMs: opts?.retryDelayMs,
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
530
1015
|
//# sourceMappingURL=graph.js.map
|