@darkhorseprojects/circuitry 0.2.12 → 0.2.14

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/index.js ADDED
@@ -0,0 +1,1332 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // src/types.ts
5
+ var CIRCUITRY_NODE_CUSTOM_TYPE = "circuitry-node-person";
6
+ var CIRCUITRY_NODE_KIND = "circuitry-node";
7
+ var createDefaultNodeData = (label) => {
8
+ const identity = label || "Agent";
9
+ return {
10
+ kind: CIRCUITRY_NODE_KIND,
11
+ identity,
12
+ label: identity,
13
+ model: "github-copilot/claude-haiku-4.5",
14
+ tools: [],
15
+ thinkingLevel: "off",
16
+ personality: "",
17
+ instructions: "Define this agent's behavior and objective.",
18
+ context: "",
19
+ output: "",
20
+ status: "idle",
21
+ lastError: null,
22
+ lastRunAt: null
23
+ };
24
+ };
25
+ var hasCircuitryNodeData = (element) => {
26
+ return !!element.customData?.circuitry && element.customData.circuitry.kind === CIRCUITRY_NODE_KIND;
27
+ };
28
+ var isNodeElement = (element) => {
29
+ return hasCircuitryNodeData(element);
30
+ };
31
+ // src/graph.ts
32
+ var CIRCUITRY_SPEC_VERSION = "0.2";
33
+ var CIRCUITRY_SPEC_VERSION_LEGACY = "0.1";
34
+ var DEFAULT_CIRCUITRY_VALIDATION_RULES = [
35
+ "no-self-loops",
36
+ "no-unknown-edge-endpoints",
37
+ "require-executable-inputs",
38
+ "no-cycles"
39
+ ];
40
+ var DEFAULT_CIRCUITRY_VALIDATION_STANDARD = {
41
+ version: CIRCUITRY_SPEC_VERSION,
42
+ requireSpecVersion: true,
43
+ rules: DEFAULT_CIRCUITRY_VALIDATION_RULES,
44
+ executableKinds: ["agent", "tool", "output"]
45
+ };
46
+ var executableKindsFromRules = (rules = []) => {
47
+ const executableRule = rules.find((entry) => typeof entry !== "string" && entry.rule === "require-executable-inputs");
48
+ return executableRule?.executableKinds;
49
+ };
50
+ var createCircuitryValidationStandard = (standard = {}, graph) => {
51
+ const rules = [
52
+ ...DEFAULT_CIRCUITRY_VALIDATION_RULES,
53
+ ...graph?.validation?.rules || [],
54
+ ...standard.rules || []
55
+ ];
56
+ return {
57
+ version: standard.version || DEFAULT_CIRCUITRY_VALIDATION_STANDARD.version,
58
+ requireSpecVersion: standard.requireSpecVersion ?? DEFAULT_CIRCUITRY_VALIDATION_STANDARD.requireSpecVersion,
59
+ rules,
60
+ executableKinds: standard.executableKinds || executableKindsFromRules(rules) || [
61
+ ...DEFAULT_CIRCUITRY_VALIDATION_STANDARD.executableKinds
62
+ ]
63
+ };
64
+ };
65
+ var expandResources = (resources) => {
66
+ const nodes = [];
67
+ const edges = [];
68
+ for (const [id, resource] of Object.entries(resources)) {
69
+ if (resource.type === "text") {
70
+ nodes.push({
71
+ id,
72
+ kind: "input",
73
+ label: resource.label || id,
74
+ input: { type: "text", value: resource.value }
75
+ });
76
+ } else if (resource.type === "agent") {
77
+ nodes.push({
78
+ id,
79
+ kind: "agent",
80
+ label: resource.label || resource.identity || id,
81
+ agent: {
82
+ identity: resource.identity || resource.label || id,
83
+ model: resource.model,
84
+ thinkingLevel: resource.thinkingLevel,
85
+ tools: resource.tools,
86
+ instructions: resource.instructions,
87
+ personality: resource.personality,
88
+ context: resource.context
89
+ },
90
+ skills: resource.skills,
91
+ expect: resource.expect
92
+ });
93
+ for (const inputRef of resource.inputs || []) {
94
+ edges.push({ from: inputRef, to: id, kind: "dependency" });
95
+ }
96
+ } else if (resource.type === "tool") {
97
+ nodes.push({
98
+ id,
99
+ kind: "tool",
100
+ label: resource.label || id,
101
+ agent: { instructions: resource.instructions }
102
+ });
103
+ for (const inputRef of resource.inputs || []) {
104
+ edges.push({ from: inputRef, to: id, kind: "dependency" });
105
+ }
106
+ }
107
+ }
108
+ return { nodes, edges };
109
+ };
110
+ var normalizeCircuitryGraph = (graph) => {
111
+ if (graph.resources && Object.keys(graph.resources).length > 0) {
112
+ const { nodes: nodes2, edges } = expandResources(graph.resources);
113
+ return { ...graph, nodes: nodes2, edges };
114
+ }
115
+ if (graph.nodes && graph.nodes.length > 0) {
116
+ const nodeIds = new Set(graph.nodes.map((n) => n.id));
117
+ const agents = graph.agents || {};
118
+ const inputs = graph.inputs || {};
119
+ const nodes2 = [...graph.nodes];
120
+ for (const [id, agent] of Object.entries(agents)) {
121
+ if (!nodeIds.has(id)) {
122
+ nodes2.push({
123
+ ...agent,
124
+ id,
125
+ kind: "agent",
126
+ label: agent.label || agent.agent?.identity || id,
127
+ agent: {
128
+ identity: agent.agent?.identity || agent.label || id,
129
+ ...agent.agent
130
+ }
131
+ });
132
+ }
133
+ }
134
+ for (const [id, input] of Object.entries(inputs)) {
135
+ if (!nodeIds.has(id)) {
136
+ nodes2.push({
137
+ id,
138
+ kind: "input",
139
+ label: input.label || id,
140
+ input
141
+ });
142
+ }
143
+ }
144
+ return { ...graph, nodes: nodes2 };
145
+ }
146
+ const nodes = [];
147
+ for (const [id, agent] of Object.entries(graph.agents || {})) {
148
+ nodes.push({
149
+ ...agent,
150
+ id,
151
+ kind: "agent",
152
+ label: agent.label || agent.agent?.identity || id,
153
+ agent: {
154
+ identity: agent.agent?.identity || agent.label || id,
155
+ ...agent.agent
156
+ }
157
+ });
158
+ }
159
+ for (const [id, input] of Object.entries(graph.inputs || {})) {
160
+ nodes.push({
161
+ id,
162
+ kind: "input",
163
+ label: input.label || id,
164
+ input
165
+ });
166
+ }
167
+ return {
168
+ ...graph,
169
+ nodes
170
+ };
171
+ };
172
+ var VALID_SPEC_VERSIONS = new Set([CIRCUITRY_SPEC_VERSION]);
173
+ var validateCircuitryGraphWithStandard = (graph, standard = {}) => {
174
+ const resolvedStandard = createCircuitryValidationStandard(standard, graph && typeof graph === "object" && !Array.isArray(graph) ? graph : undefined);
175
+ const errors = [];
176
+ const addError = (code, message, path2) => errors.push({ code, message, ...path2 ? { path: path2 } : {} });
177
+ if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
178
+ addError("invalid_graph", "Circuitry graph must be an object");
179
+ return { ok: false, errors, standard: resolvedStandard };
180
+ }
181
+ const graphObject = graph;
182
+ const rules = resolvedStandard.rules;
183
+ const hasRule = (name) => rules.some((entry) => (typeof entry === "string" ? entry : entry.rule) === name);
184
+ if (resolvedStandard.requireSpecVersion && !VALID_SPEC_VERSIONS.has(String(graphObject.circuitry))) {
185
+ addError("invalid_spec_version", `Expected circuitry: "${CIRCUITRY_SPEC_VERSION}"`, ["circuitry"]);
186
+ }
187
+ if (graphObject.circuitry === CIRCUITRY_SPEC_VERSION && graphObject.resources && Object.keys(graphObject.resources).length > 0) {
188
+ if (graphObject.agents && Object.keys(graphObject.agents).length > 0) {
189
+ addError("v02_mixed_format", "v0.2 graphs with resources: must not use legacy agents: section", ["agents"]);
190
+ }
191
+ if (graphObject.inputs && Object.keys(graphObject.inputs).length > 0) {
192
+ addError("v02_mixed_format", "v0.2 graphs with resources: must not use legacy inputs: section", ["inputs"]);
193
+ }
194
+ if (graphObject.edges && graphObject.edges.length > 0) {
195
+ addError("v02_mixed_format", "v0.2 graphs with resources: must not use legacy edges: section", ["edges"]);
196
+ }
197
+ }
198
+ const normalized = normalizeCircuitryGraph(graphObject);
199
+ const ids = new Set;
200
+ for (const [index, node] of (normalized.nodes || []).entries()) {
201
+ if (!node.id) {
202
+ addError("missing_node_id", "Node is missing id", ["nodes", index, "id"]);
203
+ continue;
204
+ }
205
+ if (ids.has(node.id)) {
206
+ addError("duplicate_node_id", `Duplicate node id: ${node.id}`, ["nodes", index, "id"]);
207
+ }
208
+ ids.add(node.id);
209
+ if (!node.kind) {
210
+ addError("missing_node_kind", `Node ${node.id} is missing kind`, ["nodes", index, "kind"]);
211
+ }
212
+ }
213
+ const adjacency = new Map;
214
+ const incomingCount = new Map;
215
+ for (const id of ids) {
216
+ adjacency.set(id, []);
217
+ incomingCount.set(id, 0);
218
+ }
219
+ for (const [index, edge] of (normalized.edges || []).entries()) {
220
+ if (!edge.from || !edge.to) {
221
+ addError("missing_edge_endpoint", "Edge is missing from/to", ["edges", index]);
222
+ continue;
223
+ }
224
+ if (hasRule("no-self-loops") && edge.from === edge.to) {
225
+ addError("self_loop", `Edge creates self loop: ${edge.from} -> ${edge.to}`, ["edges", index]);
226
+ continue;
227
+ }
228
+ if (hasRule("no-unknown-edge-endpoints") && !ids.has(edge.from)) {
229
+ addError("unknown_edge_source", `Edge references unknown source: ${edge.from}`, ["edges", index, "from"]);
230
+ }
231
+ if (hasRule("no-unknown-edge-endpoints") && !ids.has(edge.to)) {
232
+ addError("unknown_edge_target", `Edge references unknown target: ${edge.to}`, ["edges", index, "to"]);
233
+ }
234
+ if (ids.has(edge.from) && ids.has(edge.to)) {
235
+ adjacency.get(edge.from).push(edge.to);
236
+ incomingCount.set(edge.to, (incomingCount.get(edge.to) || 0) + 1);
237
+ }
238
+ }
239
+ const executableKinds = new Set(resolvedStandard.executableKinds);
240
+ if (hasRule("require-executable-inputs")) {
241
+ for (const [index, node] of (normalized.nodes || []).entries()) {
242
+ if (executableKinds.has(node.kind) && (incomingCount.get(node.id) || 0) === 0) {
243
+ addError("executable_without_inputs", `Executable node has no inputs: ${node.id}`, ["nodes", index]);
244
+ }
245
+ }
246
+ }
247
+ const visiting = new Set;
248
+ const visited = new Set;
249
+ const path = [];
250
+ let cycleMessage = "";
251
+ const visit = (id) => {
252
+ if (cycleMessage)
253
+ return;
254
+ if (visiting.has(id)) {
255
+ cycleMessage = [...path.slice(path.indexOf(id)), id].join(" -> ");
256
+ return;
257
+ }
258
+ if (visited.has(id))
259
+ return;
260
+ visiting.add(id);
261
+ path.push(id);
262
+ for (const next of adjacency.get(id) || [])
263
+ visit(next);
264
+ path.pop();
265
+ visiting.delete(id);
266
+ visited.add(id);
267
+ };
268
+ if (hasRule("no-cycles")) {
269
+ for (const id of ids) {
270
+ visit(id);
271
+ if (cycleMessage) {
272
+ addError("cycle", `Graph contains cycle: ${cycleMessage}`);
273
+ break;
274
+ }
275
+ }
276
+ }
277
+ const additionalRules = rules.filter((rule) => {
278
+ if (typeof rule === "string")
279
+ return false;
280
+ return rule.rule === "require-graph-field" || rule.rule === "require-node-field" || rule.rule === "require-edge-field" || rule.rule === "require-string" || rule.rule === "reject-string";
281
+ });
282
+ const fieldPath = (field) => field.split(".").filter(Boolean);
283
+ const getField = (value, field) => fieldPath(field).reduce((current, key) => {
284
+ if (!current || typeof current !== "object")
285
+ return;
286
+ return current[key];
287
+ }, value);
288
+ const isPresent = (value) => value !== undefined && value !== null && value !== "";
289
+ for (const rule of additionalRules) {
290
+ if (rule.rule === "require-graph-field") {
291
+ if (!isPresent(getField(normalized, rule.field))) {
292
+ addError("missing_required_graph_field", `Graph is missing required field: ${rule.field}`, fieldPath(rule.field));
293
+ }
294
+ continue;
295
+ }
296
+ if (rule.rule === "require-node-field") {
297
+ const nodeKinds = new Set(rule.nodeKinds || []);
298
+ for (const [index, node] of (normalized.nodes || []).entries()) {
299
+ if (nodeKinds.size > 0 && !nodeKinds.has(node.kind))
300
+ continue;
301
+ if (!isPresent(getField(node, rule.field))) {
302
+ addError("missing_required_node_field", `Node ${node.id || "<missing id>"} is missing required field: ${rule.field}`, ["nodes", index, ...fieldPath(rule.field)]);
303
+ }
304
+ }
305
+ continue;
306
+ }
307
+ if (rule.rule === "require-edge-field") {
308
+ const edgeKinds = new Set(rule.edgeKinds || []);
309
+ for (const [index, edge] of (normalized.edges || []).entries()) {
310
+ if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind)))
311
+ continue;
312
+ if (!isPresent(getField(edge, rule.field))) {
313
+ addError("missing_required_edge_field", `Edge ${edge.id || index} is missing required field: ${rule.field}`, ["edges", index, ...fieldPath(rule.field)]);
314
+ }
315
+ }
316
+ continue;
317
+ }
318
+ const checkString = (target, subject, id, match, path2) => {
319
+ const value = getField(subject, match.field);
320
+ const text = typeof value === "string" ? value : "";
321
+ const includes = text.includes(match.value);
322
+ if (rule.rule === "require-string" && !includes) {
323
+ addError("missing_required_string", `${target} ${id} field ${match.field} must include string: ${match.value}`, path2);
324
+ }
325
+ if (rule.rule === "reject-string" && includes) {
326
+ addError("rejected_string", `${target} ${id} field ${match.field} must not include string: ${match.value}`, path2);
327
+ }
328
+ };
329
+ for (const match of rule.graph || [])
330
+ checkString("graph", normalized, "<root>", match, fieldPath(match.field));
331
+ for (const match of rule.nodes || []) {
332
+ const nodeKinds = new Set(match.nodeKinds || []);
333
+ for (const [index, node] of (normalized.nodes || []).entries()) {
334
+ if (nodeKinds.size > 0 && !nodeKinds.has(node.kind))
335
+ continue;
336
+ checkString("node", node, node.id || "<missing id>", match, ["nodes", index, ...fieldPath(match.field)]);
337
+ }
338
+ }
339
+ for (const match of rule.edges || []) {
340
+ const edgeKinds = new Set(match.edgeKinds || []);
341
+ for (const [index, edge] of (normalized.edges || []).entries()) {
342
+ if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind)))
343
+ continue;
344
+ checkString("edge", edge, edge.id || String(index), match, ["edges", index, ...fieldPath(match.field)]);
345
+ }
346
+ }
347
+ }
348
+ return { ok: errors.length === 0, errors, standard: resolvedStandard };
349
+ };
350
+ var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map((error) => error.message);
351
+ var parseCircuitryJson = (text, standard = {}, options = {}) => {
352
+ let graph;
353
+ try {
354
+ graph = JSON.parse(text);
355
+ } catch (error) {
356
+ const message = error instanceof Error ? error.message : String(error);
357
+ throw new Error(`Could not parse Circuitry JSON. Check commas, quotes, and braces.
358
+ ${message}`);
359
+ }
360
+ if (options.validate !== false) {
361
+ const errors = validateCircuitryGraph(graph, standard);
362
+ if (errors.length) {
363
+ throw new Error(`Invalid Circuitry graph:
364
+ ${errors.join(`
365
+ `)}`);
366
+ }
367
+ }
368
+ return normalizeCircuitryGraph(graph);
369
+ };
370
+ var stringifyCircuitryJson = (graph) => {
371
+ return `${JSON.stringify(normalizeCircuitryGraph(graph), null, 2)}
372
+ `;
373
+ };
374
+ var isUriInput = (input) => {
375
+ return Boolean(input.uri || ["file", "url", "uri", "image", "mcp"].includes(input.type));
376
+ };
377
+ // src/yaml.ts
378
+ import YAML from "yaml";
379
+ var parseCircuitryYaml = (text, standard = {}, options = {}) => {
380
+ let graph;
381
+ try {
382
+ graph = YAML.parse(text);
383
+ } catch (error) {
384
+ const message = error instanceof Error ? error.message : String(error);
385
+ throw new Error(`Could not parse Circuitry YAML. Check indentation and quotes.
386
+ ${message}`);
387
+ }
388
+ if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
389
+ throw new Error("Could not parse Circuitry YAML. Expected a graph object.");
390
+ }
391
+ if (options.validate !== false) {
392
+ const errors = validateCircuitryGraph(graph, standard);
393
+ if (errors.length) {
394
+ throw new Error(`Invalid Circuitry graph:
395
+ ${errors.join(`
396
+ `)}`);
397
+ }
398
+ }
399
+ return normalizeCircuitryGraph(graph);
400
+ };
401
+ var stringifyCircuitryYaml = (graph) => {
402
+ return YAML.stringify(normalizeCircuitryGraph(graph));
403
+ };
404
+ var parseCircuitryText = (text, filename = "graph.circuitry.yaml", standard = {}, options = {}) => {
405
+ return filename.endsWith(".json") ? parseCircuitryJson(text, standard, options) : parseCircuitryYaml(text, standard, options);
406
+ };
407
+ var stringifyCircuitryText = (graph, filename = "graph.circuitry.yaml") => {
408
+ return filename.endsWith(".json") ? stringifyCircuitryJson(graph) : stringifyCircuitryYaml(graph);
409
+ };
410
+ // src/presets.ts
411
+ var CIRCUITRY_AGENT_PRESET_DIRS = [
412
+ ".agents/agents",
413
+ ".circuitry/agents",
414
+ "~/.agents/agents",
415
+ "~/.circuitry/agents"
416
+ ];
417
+ var parseAgentPresetMarkdown = (text, fallbackName) => {
418
+ const frontmatter = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/m.exec(text);
419
+ if (!frontmatter) {
420
+ return {
421
+ name: fallbackName,
422
+ instructions: text.trim()
423
+ };
424
+ }
425
+ const metadata = {};
426
+ for (const line of frontmatter[1].split(`
427
+ `)) {
428
+ const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line.trim());
429
+ if (match) {
430
+ metadata[match[1]] = match[2].replace(/^['\"]|['\"]$/g, "");
431
+ }
432
+ }
433
+ return {
434
+ name: metadata.name || fallbackName,
435
+ description: metadata.description,
436
+ model: metadata.model,
437
+ personality: metadata.personality,
438
+ instructions: frontmatter[2].trim(),
439
+ metadata
440
+ };
441
+ };
442
+ var applyAgentPresets = (graph, presets) => {
443
+ const nodes = (graph.nodes || []).map((node) => {
444
+ if (node.kind !== "agent" || !node.agent?.uses) {
445
+ return node;
446
+ }
447
+ const preset = presets[node.agent.uses];
448
+ if (!preset) {
449
+ return node;
450
+ }
451
+ return {
452
+ ...node,
453
+ label: node.label || preset.name,
454
+ agent: {
455
+ model: preset.model,
456
+ personality: preset.personality,
457
+ instructions: preset.instructions,
458
+ identity: preset.name,
459
+ ...node.agent
460
+ }
461
+ };
462
+ });
463
+ return { ...graph, nodes };
464
+ };
465
+ // src/scheduler.ts
466
+ var buildCircuitryExecutionGraph = (graph) => {
467
+ const normalized = normalizeCircuitryGraph(graph);
468
+ const nodes = new Map;
469
+ const inputSets = new Map;
470
+ const outputSets = new Map;
471
+ for (const node of normalized.nodes || []) {
472
+ if (node.kind !== "agent" && node.kind !== "tool" && node.kind !== "output" && node.kind !== "input") {
473
+ continue;
474
+ }
475
+ nodes.set(node.id, {
476
+ id: node.id,
477
+ node,
478
+ inputNodeIds: [],
479
+ outputNodeIds: [],
480
+ ...node.kind === "input" ? { output: node.input?.value || "", completed: true } : {}
481
+ });
482
+ inputSets.set(node.id, new Set);
483
+ outputSets.set(node.id, new Set);
484
+ }
485
+ for (const edge of normalized.edges || []) {
486
+ if (!nodes.has(edge.to)) {
487
+ continue;
488
+ }
489
+ inputSets.get(edge.to).add(edge.from);
490
+ if (nodes.has(edge.from)) {
491
+ outputSets.get(edge.from).add(edge.to);
492
+ }
493
+ }
494
+ for (const [id, node] of nodes) {
495
+ node.inputNodeIds = [...inputSets.get(id)];
496
+ node.outputNodeIds = [...outputSets.get(id)];
497
+ }
498
+ return { nodes };
499
+ };
500
+ var formatEdgeData = (data) => {
501
+ if (!data || Object.keys(data).length === 0) {
502
+ return "";
503
+ }
504
+ return ` data=${JSON.stringify(data)}`;
505
+ };
506
+ var describeEdge = (edge, direction) => {
507
+ const otherId = direction === "incoming" ? edge.from : edge.to;
508
+ const kind = edge.kind ? ` [${edge.kind}]` : "";
509
+ const label = edge.label ? `: ${edge.label}` : "";
510
+ return `- ${direction === "incoming" ? "from" : "to"} ${otherId}${kind}${label}${formatEdgeData(edge.data)}`;
511
+ };
512
+ var composeCircuitryPrompt = (graph, node, inputs, contextInputs) => {
513
+ const agent = node.agent || {};
514
+ const upstreamSection = inputs.length ? inputs.map((input, index) => `Upstream ${index + 1} from ${input.nodeId}:
515
+ ${input.output}`).join(`
516
+
517
+ `) : "No upstream node outputs.";
518
+ const contextSection = contextInputs.length ? contextInputs.map((input, index) => input.kind === "text" ? `Context ${index + 1} [text] from ${input.sourceId}:
519
+ ${input.text || ""}` : `Context ${index + 1} [${input.kind}] from ${input.sourceId}${input.text ? `:
520
+ ${input.text}` : ""}`).join(`
521
+
522
+ `) : "No connected external context.";
523
+ const incomingEdges = (graph.edges || []).filter((edge) => edge.to === node.id);
524
+ const outgoingEdges = (graph.edges || []).filter((edge) => edge.from === node.id);
525
+ const wiringSection = [
526
+ "Canvas Wiring:",
527
+ "Incoming edges:",
528
+ incomingEdges.length ? incomingEdges.map((edge) => describeEdge(edge, "incoming")).join(`
529
+ `) : "- none",
530
+ "Outgoing edges:",
531
+ outgoingEdges.length ? outgoingEdges.map((edge) => describeEdge(edge, "outgoing")).join(`
532
+ `) : "- none"
533
+ ].join(`
534
+ `);
535
+ return [
536
+ `Node label: ${node.label}`,
537
+ agent.identity ? `Identity: ${agent.identity}` : "",
538
+ agent.context ? `Context:
539
+ ${agent.context}` : "",
540
+ agent.personality ? `Personality:
541
+ ${agent.personality}` : "",
542
+ agent.instructions ? `Instructions:
543
+ ${agent.instructions}` : "",
544
+ wiringSection,
545
+ `Upstream Node Outputs:
546
+ ${upstreamSection}`,
547
+ `Connected Context Inputs:
548
+ ${contextSection}`
549
+ ].filter(Boolean).join(`
550
+
551
+ `);
552
+ };
553
+ var collectImages = (contextInputs) => contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
554
+ var executeCircuitryNode = async ({
555
+ graph,
556
+ item,
557
+ fallbackCycle,
558
+ inputPayload,
559
+ contextInputs,
560
+ defaultModel,
561
+ executeNode,
562
+ onNodeStart,
563
+ onNodeComplete
564
+ }) => {
565
+ onNodeStart?.(item.id, fallbackCycle);
566
+ if (item.node.kind === "output") {
567
+ const output = inputPayload.map((input) => input.output).join(`
568
+
569
+ `);
570
+ onNodeComplete?.(item.id, { output });
571
+ return {
572
+ nodeId: item.id,
573
+ fallbackCycle,
574
+ inputNodeIds: inputPayload.map((input) => input.nodeId),
575
+ output
576
+ };
577
+ }
578
+ const agent = item.node.agent || {};
579
+ try {
580
+ const result = await executeNode({
581
+ nodeId: item.id,
582
+ model: (agent.model === "inherit" ? undefined : agent.model) || (defaultModel === "inherit" ? undefined : defaultModel) || "inherit",
583
+ tools: agent.tools || [],
584
+ thinkingLevel: agent.thinkingLevel || "off",
585
+ personality: agent.personality || "",
586
+ instructions: agent.instructions || "",
587
+ context: agent.context || "",
588
+ inputs: inputPayload,
589
+ contextInputs,
590
+ images: collectImages(contextInputs),
591
+ prompt: composeCircuitryPrompt(graph, item.node, inputPayload, contextInputs)
592
+ });
593
+ onNodeComplete?.(item.id, { output: result.output });
594
+ return {
595
+ nodeId: item.id,
596
+ fallbackCycle,
597
+ inputNodeIds: inputPayload.map((input) => input.nodeId),
598
+ output: result.output
599
+ };
600
+ } catch (error) {
601
+ const message = error instanceof Error ? error.message : String(error);
602
+ onNodeComplete?.(item.id, { error: message });
603
+ return {
604
+ nodeId: item.id,
605
+ fallbackCycle,
606
+ inputNodeIds: inputPayload.map((input) => input.nodeId),
607
+ output: "",
608
+ error: message
609
+ };
610
+ }
611
+ };
612
+ var runCircuitryGraphExecution = async ({
613
+ graph,
614
+ selectedNodeId,
615
+ executeNode,
616
+ resolveContextInputs,
617
+ onNodeStart,
618
+ onNodeComplete,
619
+ maxParallelRuns = 4
620
+ }) => {
621
+ const executionGraph = buildCircuitryExecutionGraph(graph);
622
+ const runs = [];
623
+ const parallelism = Math.max(1, Math.floor(maxParallelRuns));
624
+ const defaultModel = graph.runtime?.model;
625
+ const runOne = async (nodeId, outputs2) => {
626
+ const fallbackCycle = false;
627
+ const item = executionGraph.nodes.get(nodeId);
628
+ const inputPayload = item.inputNodeIds.filter((sourceId) => outputs2.has(sourceId)).map((sourceId) => ({
629
+ nodeId: sourceId,
630
+ output: outputs2.get(sourceId)
631
+ }));
632
+ const contextInputs = await resolveContextInputs?.({
633
+ nodeId,
634
+ inputNodeIds: new Set(item.inputNodeIds),
635
+ graph
636
+ }) || [];
637
+ return executeCircuitryNode({
638
+ graph,
639
+ item,
640
+ fallbackCycle,
641
+ inputPayload,
642
+ contextInputs,
643
+ defaultModel,
644
+ executeNode,
645
+ onNodeStart,
646
+ onNodeComplete
647
+ });
648
+ };
649
+ if (selectedNodeId) {
650
+ if (!executionGraph.nodes.has(selectedNodeId)) {
651
+ return runs;
652
+ }
653
+ runs.push(await runOne(selectedNodeId, new Map));
654
+ return runs;
655
+ }
656
+ const completed = new Set;
657
+ const queued = new Set;
658
+ const outputs = new Map;
659
+ const nodeIds = [...executionGraph.nodes.keys()];
660
+ const remainingDependencies = new Map;
661
+ const ready = [];
662
+ for (const nodeId of nodeIds) {
663
+ const node = executionGraph.nodes.get(nodeId);
664
+ if (node.completed) {
665
+ completed.add(nodeId);
666
+ if (node.output) {
667
+ outputs.set(nodeId, node.output);
668
+ }
669
+ continue;
670
+ }
671
+ const count = node.inputNodeIds.filter((sourceId) => executionGraph.nodes.has(sourceId)).length;
672
+ remainingDependencies.set(nodeId, count);
673
+ if (count === 0) {
674
+ ready.push(nodeId);
675
+ queued.add(nodeId);
676
+ }
677
+ }
678
+ while (completed.size < nodeIds.length) {
679
+ if (ready.length === 0) {
680
+ const blocked = nodeIds.filter((id) => !completed.has(id));
681
+ throw new Error(`Circuitry execution stalled; unresolved dependencies: ${blocked.join(", ")}`);
682
+ }
683
+ const chunk = ready.splice(0, parallelism);
684
+ const chunkResults = await Promise.all(chunk.map(async (nodeId) => ({
685
+ nodeId,
686
+ result: await runOne(nodeId, outputs)
687
+ })));
688
+ for (const { nodeId, result } of chunkResults) {
689
+ outputs.set(nodeId, result.output);
690
+ completed.add(nodeId);
691
+ runs.push(result);
692
+ for (const outputNodeId of executionGraph.nodes.get(nodeId).outputNodeIds) {
693
+ if (completed.has(outputNodeId) || queued.has(outputNodeId)) {
694
+ continue;
695
+ }
696
+ const nextCount = Math.max(0, (remainingDependencies.get(outputNodeId) || 0) - 1);
697
+ remainingDependencies.set(outputNodeId, nextCount);
698
+ if (nextCount === 0) {
699
+ ready.push(outputNodeId);
700
+ queued.add(outputNodeId);
701
+ }
702
+ }
703
+ }
704
+ }
705
+ return runs;
706
+ };
707
+ // src/simulation.ts
708
+ var DEFAULT_MAX_PARALLEL_RUNS = 4;
709
+ var pushUnique = (array, value) => {
710
+ if (!array.includes(value)) {
711
+ array.push(value);
712
+ }
713
+ };
714
+ var buildSimulationGraph = (elements) => {
715
+ const nodes = new Map;
716
+ for (const element of elements) {
717
+ if (!hasCircuitryNodeData(element)) {
718
+ continue;
719
+ }
720
+ nodes.set(element.id, {
721
+ id: element.id,
722
+ element,
723
+ data: element.customData.circuitry,
724
+ inputNodeIds: [],
725
+ outputNodeIds: [],
726
+ incomingSourceIds: []
727
+ });
728
+ }
729
+ for (const element of elements) {
730
+ if (element.type !== "arrow") {
731
+ continue;
732
+ }
733
+ const arrow = element;
734
+ const sourceId = arrow.startBinding?.elementId;
735
+ const targetId = arrow.endBinding?.elementId;
736
+ if (!sourceId || !targetId || !nodes.has(targetId)) {
737
+ continue;
738
+ }
739
+ const target = nodes.get(targetId);
740
+ pushUnique(target.incomingSourceIds, sourceId);
741
+ if (!nodes.has(sourceId)) {
742
+ continue;
743
+ }
744
+ const source = nodes.get(sourceId);
745
+ pushUnique(source.outputNodeIds, targetId);
746
+ pushUnique(target.inputNodeIds, sourceId);
747
+ }
748
+ return { nodes };
749
+ };
750
+ var formatContextInput = (input, index) => {
751
+ if (input.kind === "text") {
752
+ return `Context ${index + 1} [text] from ${input.sourceId}:
753
+ ${input.text || ""}`;
754
+ }
755
+ if (input.kind === "image") {
756
+ return `Context ${index + 1} [image] from ${input.sourceId}`;
757
+ }
758
+ const label = input.elementType ? `${input.elementType}` : "element";
759
+ return `Context ${index + 1} [${label}] from ${input.sourceId}${input.text ? `:
760
+ ${input.text}` : ""}`;
761
+ };
762
+ var composePrompt = (node, inputs, contextInputs) => {
763
+ const upstreamSection = inputs.length === 0 ? "No upstream node outputs." : inputs.map((input, index) => `Upstream ${index + 1} from ${input.nodeId}:
764
+ ${input.output}`).join(`
765
+
766
+ `);
767
+ const contextSection = contextInputs.length === 0 ? "No connected external context." : contextInputs.map(formatContextInput).join(`
768
+
769
+ `);
770
+ return [
771
+ `Node label: ${node.data.label}`,
772
+ node.data.context ? `Context:
773
+ ${node.data.context}` : "",
774
+ node.data.personality ? `Personality:
775
+ ${node.data.personality}` : "",
776
+ node.data.instructions ? `Instructions:
777
+ ${node.data.instructions}` : "",
778
+ `Upstream Node Outputs:
779
+ ${upstreamSection}`,
780
+ `Connected Context Inputs:
781
+ ${contextSection}`
782
+ ].filter(Boolean).join(`
783
+
784
+ `);
785
+ };
786
+ var collectImages2 = (contextInputs) => {
787
+ return contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
788
+ };
789
+ var executeSimulationNode = async ({
790
+ node,
791
+ fallbackCycle,
792
+ inputPayload,
793
+ contextInputs,
794
+ executeNode,
795
+ onNodeStart,
796
+ onNodeComplete
797
+ }) => {
798
+ onNodeStart?.(node.id, fallbackCycle);
799
+ try {
800
+ const result = await executeNode({
801
+ nodeId: node.id,
802
+ model: node.data.model === "inherit" ? "inherit" : node.data.model || "inherit",
803
+ tools: node.data.tools || [],
804
+ thinkingLevel: node.data.thinkingLevel || "off",
805
+ personality: node.data.personality,
806
+ instructions: node.data.instructions,
807
+ context: node.data.context,
808
+ inputs: inputPayload,
809
+ contextInputs,
810
+ images: collectImages2(contextInputs),
811
+ prompt: composePrompt(node, inputPayload, contextInputs)
812
+ });
813
+ const item = {
814
+ nodeId: node.id,
815
+ fallbackCycle,
816
+ inputNodeIds: inputPayload.map((input) => input.nodeId),
817
+ output: result.output
818
+ };
819
+ onNodeComplete?.(node.id, { output: result.output });
820
+ return item;
821
+ } catch (error) {
822
+ const message = error instanceof Error ? error.message : String(error);
823
+ const item = {
824
+ nodeId: node.id,
825
+ fallbackCycle,
826
+ inputNodeIds: inputPayload.map((input) => input.nodeId),
827
+ output: "",
828
+ error: message
829
+ };
830
+ onNodeComplete?.(node.id, { error: message });
831
+ return item;
832
+ }
833
+ };
834
+ var runGraphExecution = async ({
835
+ elements,
836
+ mode,
837
+ selectedNodeId,
838
+ executeNode,
839
+ resolveContextInputs,
840
+ onNodeStart,
841
+ onNodeComplete,
842
+ maxParallelRuns = DEFAULT_MAX_PARALLEL_RUNS
843
+ }) => {
844
+ const graph = buildSimulationGraph(elements);
845
+ const runs = [];
846
+ const parallelism = Math.max(1, Math.floor(maxParallelRuns));
847
+ if (mode === "single") {
848
+ if (!selectedNodeId || !graph.nodes.has(selectedNodeId)) {
849
+ return runs;
850
+ }
851
+ const node = graph.nodes.get(selectedNodeId);
852
+ const inputPayload = node.inputNodeIds.map((sourceId) => graph.nodes.get(sourceId)).filter((sourceNode) => !!sourceNode?.data.output).map((sourceNode) => ({
853
+ nodeId: sourceNode.id,
854
+ output: sourceNode.data.output
855
+ }));
856
+ const contextInputs = await resolveContextInputs?.({
857
+ nodeId: node.id,
858
+ inputNodeIds: new Set(node.inputNodeIds),
859
+ incomingSourceIds: node.incomingSourceIds,
860
+ elements
861
+ }) || [];
862
+ runs.push(await executeSimulationNode({
863
+ node,
864
+ fallbackCycle: false,
865
+ inputPayload,
866
+ contextInputs,
867
+ executeNode,
868
+ onNodeStart,
869
+ onNodeComplete
870
+ }));
871
+ return runs;
872
+ }
873
+ const completed = new Set;
874
+ const outputs = new Map;
875
+ const nodeIds = [...graph.nodes.keys()];
876
+ while (completed.size < nodeIds.length) {
877
+ const pending = nodeIds.filter((id) => !completed.has(id));
878
+ const ready = pending.filter((id) => {
879
+ const node = graph.nodes.get(id);
880
+ return node.inputNodeIds.every((sourceId) => completed.has(sourceId));
881
+ });
882
+ if (ready.length === 0) {
883
+ throw new Error(`Circuitry execution stalled; unresolved dependencies: ${pending.join(", ")}`);
884
+ }
885
+ const fallbackCycle = false;
886
+ const currentBatch = ready;
887
+ for (let index = 0;index < currentBatch.length; index += parallelism) {
888
+ const chunk = currentBatch.slice(index, index + parallelism);
889
+ const chunkResults = await Promise.all(chunk.map(async (nodeId) => {
890
+ const node = graph.nodes.get(nodeId);
891
+ const inputPayload = node.inputNodeIds.filter((sourceId) => outputs.has(sourceId)).map((sourceId) => ({
892
+ nodeId: sourceId,
893
+ output: outputs.get(sourceId)
894
+ }));
895
+ const contextInputs = await resolveContextInputs?.({
896
+ nodeId,
897
+ inputNodeIds: new Set(node.inputNodeIds),
898
+ incomingSourceIds: node.incomingSourceIds,
899
+ elements
900
+ }) || [];
901
+ const result = await executeSimulationNode({
902
+ node,
903
+ fallbackCycle,
904
+ inputPayload,
905
+ contextInputs,
906
+ executeNode,
907
+ onNodeStart,
908
+ onNodeComplete
909
+ });
910
+ return { nodeId, result };
911
+ }));
912
+ for (const { nodeId, result } of chunkResults) {
913
+ outputs.set(nodeId, result.output);
914
+ completed.add(nodeId);
915
+ runs.push(result);
916
+ }
917
+ }
918
+ }
919
+ return runs;
920
+ };
921
+ var runDependencySimulation = async ({
922
+ elements,
923
+ executeNode,
924
+ onNodeStart,
925
+ onNodeComplete,
926
+ maxParallelRuns
927
+ }) => {
928
+ return runGraphExecution({
929
+ elements,
930
+ mode: "dependency",
931
+ executeNode,
932
+ onNodeStart,
933
+ onNodeComplete,
934
+ maxParallelRuns
935
+ });
936
+ };
937
+ // src/bundle.ts
938
+ var BUNDLE_MIME = "application/vnd.circuitry.bundle+json";
939
+ var deepClone = (value) => JSON.parse(JSON.stringify(value));
940
+ var createBundleFromSelection = (elements, selectedIds) => {
941
+ const selected = elements.filter((el) => selectedIds[el.id]);
942
+ if (!selected.length) {
943
+ return null;
944
+ }
945
+ const selectedSet = new Set(selected.map((el) => el.id));
946
+ const relationships = elements.filter((el) => {
947
+ if (el.type !== "arrow") {
948
+ return false;
949
+ }
950
+ const arrow = el;
951
+ const source = arrow.startBinding?.elementId;
952
+ const target = arrow.endBinding?.elementId;
953
+ return !!source && !!target && selectedSet.has(source) && selectedSet.has(target);
954
+ });
955
+ const dedup = new Map;
956
+ [...selected, ...relationships].forEach((el) => dedup.set(el.id, deepClone(el)));
957
+ return {
958
+ version: 1,
959
+ createdAt: new Date().toISOString(),
960
+ elements: [...dedup.values()]
961
+ };
962
+ };
963
+ var parseBundleText = (text) => {
964
+ const parsed = JSON.parse(text);
965
+ if (parsed.version !== 1 || !Array.isArray(parsed.elements)) {
966
+ throw new Error("Invalid Circuitry bundle format");
967
+ }
968
+ return parsed;
969
+ };
970
+ var randomId = () => typeof crypto !== "undefined" && ("randomUUID" in crypto) ? crypto.randomUUID().replaceAll("-", "") : Math.random().toString(36).slice(2, 12);
971
+ var importBundleElements = (bundle, opts) => {
972
+ const offsetX = opts?.offsetX ?? 24;
973
+ const offsetY = opts?.offsetY ?? 24;
974
+ const idMap = new Map;
975
+ for (const element of bundle.elements) {
976
+ idMap.set(element.id, randomId());
977
+ }
978
+ return bundle.elements.map((element) => {
979
+ const cloned = deepClone(element);
980
+ const oldId = cloned.id;
981
+ cloned.id = idMap.get(oldId);
982
+ if (typeof cloned.x === "number") {
983
+ cloned.x += offsetX;
984
+ }
985
+ if (typeof cloned.y === "number") {
986
+ cloned.y += offsetY;
987
+ }
988
+ if (cloned.frameId && idMap.has(cloned.frameId)) {
989
+ cloned.frameId = idMap.get(cloned.frameId);
990
+ }
991
+ if (cloned.boundElements?.length) {
992
+ cloned.boundElements = cloned.boundElements.map((item) => idMap.has(item.id) ? { ...item, id: idMap.get(item.id) } : item);
993
+ }
994
+ if (cloned.containerId && idMap.has(cloned.containerId)) {
995
+ cloned.containerId = idMap.get(cloned.containerId);
996
+ }
997
+ if (cloned.type === "arrow") {
998
+ const arrow = cloned;
999
+ if (arrow.startBinding && idMap.has(arrow.startBinding.elementId)) {
1000
+ arrow.startBinding = {
1001
+ ...arrow.startBinding,
1002
+ elementId: idMap.get(arrow.startBinding.elementId)
1003
+ };
1004
+ }
1005
+ if (arrow.endBinding && idMap.has(arrow.endBinding.elementId)) {
1006
+ arrow.endBinding = {
1007
+ ...arrow.endBinding,
1008
+ elementId: idMap.get(arrow.endBinding.elementId)
1009
+ };
1010
+ }
1011
+ }
1012
+ return cloned;
1013
+ });
1014
+ };
1015
+ // src/runtime.ts
1016
+ var createUnsupportedRuntimeAdapter = (id) => ({
1017
+ id,
1018
+ label: String(id),
1019
+ async runNode() {
1020
+ throw new Error(`Circuitry runtime adapter is not configured: ${String(id)}`);
1021
+ }
1022
+ });
1023
+ // src/tools.ts
1024
+ import { z } from "zod";
1025
+ var standardSchema = z.object({
1026
+ version: z.string().optional(),
1027
+ requireSpecVersion: z.boolean().optional(),
1028
+ executableKinds: z.array(z.string()).optional(),
1029
+ rules: z.array(z.union([z.string(), z.object({ rule: z.string() }).passthrough()])).optional()
1030
+ }).passthrough().optional();
1031
+ var createCircuitryReadGraphTool = (host) => ({
1032
+ name: "circuitry_read_graph",
1033
+ description: "Read the current Circuitry graph. In the App this reads the canvas and includes latest node run outputs.",
1034
+ parameters: z.object({
1035
+ filename: z.string().optional().describe("Optional filename when in standalone mode.")
1036
+ }),
1037
+ execute: (input) => host.readGraph(input)
1038
+ });
1039
+ var createCircuitryWriteGraphTool = (host) => ({
1040
+ name: "circuitry_write_graph",
1041
+ description: "Replace a Circuitry graph. In the App this applies the graph to the canvas; standalone writes a file.",
1042
+ parameters: z.object({
1043
+ text: z.string().optional().describe("YAML graph text."),
1044
+ filename: z.string().optional().describe("Optional filename."),
1045
+ graph: z.any().optional().describe("Parsed graph object."),
1046
+ standard: standardSchema
1047
+ }),
1048
+ execute: (input) => host.writeGraph(input)
1049
+ });
1050
+ var createCircuitryRunGraphTool = (host) => ({
1051
+ name: "circuitry_run_graph",
1052
+ description: "Run the current Circuitry graph. After running, call circuitry_read_graph to inspect updated per-node outputs and errors.",
1053
+ parameters: z.object({
1054
+ source: z.enum(["current", "text"]).optional().describe("Whether to run the 'current' canvas/file or provided 'text'."),
1055
+ text: z.string().optional().describe("YAML graph text if source is 'text'."),
1056
+ filename: z.string().optional().describe("Optional filename."),
1057
+ selectedNodeId: z.string().optional().describe("Optional specific node to run."),
1058
+ standard: standardSchema,
1059
+ hostModel: z.string().optional().describe("Optional model to use when a node is set to 'inherit'.")
1060
+ }),
1061
+ execute: (input) => host.runGraph(input)
1062
+ });
1063
+ var createCircuitryValidateGraphTool = (host) => ({
1064
+ name: "circuitry_validate_graph",
1065
+ description: "Validate Circuitry YAML or JSON graph text. Returns structured validation issues.",
1066
+ parameters: z.object({
1067
+ text: z.string().describe("YAML or JSON graph text to validate."),
1068
+ filename: z.string().optional().describe("Optional filename context."),
1069
+ standard: standardSchema
1070
+ }),
1071
+ execute: (input) => host.validateGraph(input)
1072
+ });
1073
+ var createCircuitryTools = (host) => [
1074
+ createCircuitryReadGraphTool(host),
1075
+ createCircuitryWriteGraphTool(host),
1076
+ createCircuitryRunGraphTool(host),
1077
+ createCircuitryValidateGraphTool(host)
1078
+ ];
1079
+ // src/node.ts
1080
+ import { access, readFile, writeFile } from "node:fs/promises";
1081
+ import fs from "node:fs";
1082
+ import path from "node:path";
1083
+ import os from "node:os";
1084
+ var loadPiSDK = async () => {
1085
+ const possiblePaths = [
1086
+ path.join(process.cwd(), "node_modules/@earendil-works/pi-coding-agent/dist/index.js"),
1087
+ path.join(os.homedir(), ".npm-global/lib/node_modules/@earendil-works/pi-coding-agent/dist/index.js")
1088
+ ];
1089
+ for (const modulePath of possiblePaths) {
1090
+ if (fs.existsSync(modulePath)) {
1091
+ return await import(modulePath);
1092
+ }
1093
+ }
1094
+ return await import("@earendil-works/pi-coding-agent");
1095
+ };
1096
+ var extractAssistantText = (message) => {
1097
+ if (!message || message.role !== "assistant")
1098
+ return "";
1099
+ if (typeof message.content === "string")
1100
+ return message.content;
1101
+ if (!Array.isArray(message.content))
1102
+ return "";
1103
+ return message.content.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
1104
+ };
1105
+ var withTimeout = async (promise, timeoutMs) => {
1106
+ let timeout;
1107
+ try {
1108
+ return await Promise.race([
1109
+ promise,
1110
+ new Promise((_, reject) => {
1111
+ timeout = setTimeout(() => reject(new Error(`Pi SDK request timed out after ${timeoutMs}ms`)), timeoutMs);
1112
+ })
1113
+ ]);
1114
+ } finally {
1115
+ clearTimeout(timeout);
1116
+ }
1117
+ };
1118
+ var runNodeWithPiSDK = async ({
1119
+ model,
1120
+ prompt,
1121
+ images = [],
1122
+ tools = [],
1123
+ thinkingLevel = "off"
1124
+ }) => {
1125
+ let sdk;
1126
+ try {
1127
+ sdk = await loadPiSDK();
1128
+ } catch (error) {
1129
+ const message = error instanceof Error ? error.message : String(error);
1130
+ throw new Error([
1131
+ "Circuitry could not load the Pi SDK package for execution.",
1132
+ "Ensure @earendil-works/pi-coding-agent is installed.",
1133
+ message
1134
+ ].join(`
1135
+ `));
1136
+ }
1137
+ const { AuthStorage, ModelRegistry, SessionManager, createAgentSession } = sdk;
1138
+ const authStorage = AuthStorage.create();
1139
+ const modelRegistry = ModelRegistry.create(authStorage);
1140
+ const [provider, ...modelParts] = String(model || "").split("/");
1141
+ const modelId = modelParts.join("/");
1142
+ const available = await modelRegistry.getAvailable();
1143
+ const selectedModel = (provider && modelId ? modelRegistry.find(provider, modelId) : undefined) || available.find((entry) => {
1144
+ if (!modelId)
1145
+ return false;
1146
+ return `${entry.provider}/${entry.id}` === `${provider}/${modelId}` || entry.id === modelId;
1147
+ }) || available[0];
1148
+ if (!selectedModel) {
1149
+ throw new Error("No Pi SDK models are available. Configure ~/.pi/agent/auth.json");
1150
+ }
1151
+ const { session } = await createAgentSession({
1152
+ model: selectedModel,
1153
+ thinkingLevel,
1154
+ sessionManager: SessionManager.inMemory(),
1155
+ authStorage,
1156
+ modelRegistry
1157
+ });
1158
+ session.setActiveToolsByName(Array.isArray(tools) ? tools.map((tool) => String(tool)) : []);
1159
+ let output = "";
1160
+ const unsubscribe = session.subscribe((event) => {
1161
+ if ((event.type === "message_update" || event.type === "message_end") && event.message?.role === "assistant") {
1162
+ output = extractAssistantText(event.message);
1163
+ }
1164
+ if (event.type === "agent_end" && Array.isArray(event.messages)) {
1165
+ const assistantMessage = [...event.messages].reverse().find((message) => message.role === "assistant");
1166
+ output = extractAssistantText(assistantMessage);
1167
+ }
1168
+ });
1169
+ try {
1170
+ const promptImages = images.filter((image) => image?.mediaType && image?.data).map((image) => ({
1171
+ type: "image",
1172
+ source: { type: "base64", mediaType: image.mediaType, data: image.data }
1173
+ }));
1174
+ await withTimeout(session.prompt(prompt, { images: promptImages }), 180000);
1175
+ return { output: output.trim() };
1176
+ } finally {
1177
+ unsubscribe();
1178
+ session.dispose();
1179
+ }
1180
+ };
1181
+ var defaultFilename = "graph.circuitry.yaml";
1182
+
1183
+ class NodeCircuitryHost {
1184
+ resolveGraphFile(filename) {
1185
+ return path.resolve(process.cwd(), filename || process.env.CIRCUITRY_GRAPH || defaultFilename);
1186
+ }
1187
+ runFileFor(filename) {
1188
+ return `${filename}.run.json`;
1189
+ }
1190
+ async exists(filename) {
1191
+ try {
1192
+ await access(filename);
1193
+ return true;
1194
+ } catch {
1195
+ return false;
1196
+ }
1197
+ }
1198
+ parseIssue(error, filename) {
1199
+ return {
1200
+ code: "parse_error",
1201
+ message: error instanceof Error ? error.message : String(error),
1202
+ path: [filename]
1203
+ };
1204
+ }
1205
+ parseGraphForValidation(text, filename, standard) {
1206
+ try {
1207
+ return {
1208
+ graph: parseCircuitryText(text || "", filename, standard, {
1209
+ validate: false
1210
+ })
1211
+ };
1212
+ } catch (error) {
1213
+ return { error: this.parseIssue(error, filename) };
1214
+ }
1215
+ }
1216
+ async readGraphFile(filename) {
1217
+ const graphFile = this.resolveGraphFile(filename);
1218
+ const text = await readFile(graphFile, "utf8");
1219
+ const graph = parseCircuitryText(text, graphFile);
1220
+ return { graphFile, text, graph };
1221
+ }
1222
+ async readGraph(input = {}) {
1223
+ const { graphFile, text, graph } = await this.readGraphFile(input.filename);
1224
+ const lastRunFile = this.runFileFor(graphFile);
1225
+ const lastRun = await this.exists(lastRunFile) ? JSON.parse(await readFile(lastRunFile, "utf8")) : undefined;
1226
+ return { filename: graphFile, graph, text, lastRun };
1227
+ }
1228
+ async validateGraph(input) {
1229
+ if (input.graph)
1230
+ return validateCircuitryGraphWithStandard(input.graph, input.standard);
1231
+ const filename = input.filename || defaultFilename;
1232
+ const parsed = this.parseGraphForValidation(input.text, filename, input.standard);
1233
+ if (parsed.error) {
1234
+ return {
1235
+ ok: false,
1236
+ errors: [parsed.error],
1237
+ standard: validateCircuitryGraphWithStandard({}, input.standard).standard
1238
+ };
1239
+ }
1240
+ return validateCircuitryGraphWithStandard(parsed.graph, input.standard);
1241
+ }
1242
+ async writeGraph(input) {
1243
+ const graphFile = this.resolveGraphFile(input.filename);
1244
+ const parsed = input.graph ? { graph: input.graph } : this.parseGraphForValidation(input.text, input.filename || graphFile, input.standard);
1245
+ if (!parsed.graph)
1246
+ throw new Error(`Invalid Circuitry graph:
1247
+ ${parsed.error?.message}`);
1248
+ const validation = await this.validateGraph({
1249
+ graph: parsed.graph,
1250
+ standard: input.standard
1251
+ });
1252
+ if (validation.errors.length)
1253
+ throw new Error(`Invalid Circuitry graph:
1254
+ ${validation.errors.map((e) => e.message).join(`
1255
+ `)}`);
1256
+ await writeFile(graphFile, stringifyCircuitryText(parsed.graph, graphFile), "utf8");
1257
+ return {
1258
+ filename: graphFile,
1259
+ graph: parsed.graph,
1260
+ mode: "replace"
1261
+ };
1262
+ }
1263
+ async runGraph(input = {}) {
1264
+ const source = input.source || (input.text ? "text" : "current");
1265
+ const graphFile = this.resolveGraphFile(input.filename);
1266
+ const parsed = source === "text" ? this.parseGraphForValidation(input.text, input.filename || graphFile, input.standard) : { graph: (await this.readGraphFile(input.filename)).graph };
1267
+ if (!parsed.graph)
1268
+ throw new Error(`Invalid Circuitry graph:
1269
+ ${parsed.error?.message}`);
1270
+ const validation = await this.validateGraph({
1271
+ graph: parsed.graph,
1272
+ standard: input.standard
1273
+ });
1274
+ if (validation.errors.length)
1275
+ throw new Error(`Invalid Circuitry graph:
1276
+ ${validation.errors.map((e) => e.message).join(`
1277
+ `)}`);
1278
+ const runItems = await runCircuitryGraphExecution({
1279
+ graph: parsed.graph,
1280
+ selectedNodeId: input.selectedNodeId,
1281
+ executeNode: (req) => runNodeWithPiSDK(req)
1282
+ });
1283
+ const result = { completedAt: new Date().toISOString(), runItems };
1284
+ if (source !== "text")
1285
+ await writeFile(this.runFileFor(graphFile), JSON.stringify(result, null, 2), "utf8");
1286
+ return result;
1287
+ }
1288
+ }
1289
+ export {
1290
+ validateCircuitryGraphWithStandard,
1291
+ validateCircuitryGraph,
1292
+ stringifyCircuitryYaml,
1293
+ stringifyCircuitryText,
1294
+ stringifyCircuitryJson,
1295
+ standardSchema,
1296
+ runNodeWithPiSDK,
1297
+ runGraphExecution,
1298
+ runDependencySimulation,
1299
+ runCircuitryGraphExecution,
1300
+ parseCircuitryYaml,
1301
+ parseCircuitryText,
1302
+ parseCircuitryJson,
1303
+ parseBundleText,
1304
+ parseAgentPresetMarkdown,
1305
+ normalizeCircuitryGraph,
1306
+ isUriInput,
1307
+ isNodeElement,
1308
+ importBundleElements,
1309
+ hasCircuitryNodeData,
1310
+ createUnsupportedRuntimeAdapter,
1311
+ createDefaultNodeData,
1312
+ createCircuitryWriteGraphTool,
1313
+ createCircuitryValidationStandard,
1314
+ createCircuitryValidateGraphTool,
1315
+ createCircuitryTools,
1316
+ createCircuitryRunGraphTool,
1317
+ createCircuitryReadGraphTool,
1318
+ createBundleFromSelection,
1319
+ buildSimulationGraph,
1320
+ buildCircuitryExecutionGraph,
1321
+ applyAgentPresets,
1322
+ NodeCircuitryHost,
1323
+ DEFAULT_MAX_PARALLEL_RUNS,
1324
+ DEFAULT_CIRCUITRY_VALIDATION_STANDARD,
1325
+ DEFAULT_CIRCUITRY_VALIDATION_RULES,
1326
+ CIRCUITRY_SPEC_VERSION_LEGACY,
1327
+ CIRCUITRY_SPEC_VERSION,
1328
+ CIRCUITRY_NODE_KIND,
1329
+ CIRCUITRY_NODE_CUSTOM_TYPE,
1330
+ CIRCUITRY_AGENT_PRESET_DIRS,
1331
+ BUNDLE_MIME
1332
+ };