@darkhorseprojects/circuitry 0.2.6 → 0.2.12

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 DELETED
@@ -1,1063 +0,0 @@
1
- // src/types.ts
2
- var CIRCUITRY_NODE_CUSTOM_TYPE = "circuitry-node-person";
3
- var CIRCUITRY_NODE_KIND = "circuitry-node";
4
- var createDefaultNodeData = (label) => {
5
- const identity = label || "Agent";
6
- return {
7
- kind: CIRCUITRY_NODE_KIND,
8
- identity,
9
- label: identity,
10
- model: "github-copilot/claude-haiku-4.5",
11
- tools: [],
12
- thinkingLevel: "off",
13
- personality: "",
14
- instructions: "Define this agent's behavior and objective.",
15
- context: "",
16
- output: "",
17
- status: "idle",
18
- lastError: null,
19
- lastRunAt: null
20
- };
21
- };
22
- var hasCircuitryNodeData = (element) => {
23
- return !!element.customData?.circuitry && element.customData.circuitry.kind === CIRCUITRY_NODE_KIND;
24
- };
25
- var isNodeElement = (element) => {
26
- return hasCircuitryNodeData(element);
27
- };
28
-
29
- // src/graph.ts
30
- var CIRCUITRY_SPEC_VERSION = "0.2";
31
- var CIRCUITRY_SPEC_VERSION_LEGACY = "0.1";
32
- var DEFAULT_CIRCUITRY_VALIDATION_RULES = [
33
- "no-self-loops",
34
- "no-unknown-edge-endpoints",
35
- "require-executable-inputs",
36
- "no-cycles"
37
- ];
38
- var DEFAULT_CIRCUITRY_VALIDATION_STANDARD = {
39
- version: CIRCUITRY_SPEC_VERSION,
40
- requireSpecVersion: true,
41
- rules: DEFAULT_CIRCUITRY_VALIDATION_RULES,
42
- executableKinds: ["agent", "tool", "output"]
43
- };
44
- var executableKindsFromRules = (rules = []) => {
45
- const executableRule = rules.find(
46
- (entry) => typeof entry !== "string" && entry.rule === "require-executable-inputs"
47
- );
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 = /* @__PURE__ */ new Set([CIRCUITRY_SPEC_VERSION]);
173
- var validateCircuitryGraphWithStandard = (graph, standard = {}) => {
174
- const resolvedStandard = createCircuitryValidationStandard(
175
- standard,
176
- graph && typeof graph === "object" && !Array.isArray(graph) ? graph : void 0
177
- );
178
- const errors = [];
179
- const addError = (code, message, path2) => errors.push({ code, message, ...path2 ? { path: path2 } : {} });
180
- if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
181
- addError("invalid_graph", "Circuitry graph must be an object");
182
- return { ok: false, errors, standard: resolvedStandard };
183
- }
184
- const graphObject = graph;
185
- const rules = resolvedStandard.rules;
186
- const hasRule = (name) => rules.some((entry) => (typeof entry === "string" ? entry : entry.rule) === name);
187
- if (resolvedStandard.requireSpecVersion && !VALID_SPEC_VERSIONS.has(String(graphObject.circuitry))) {
188
- addError(
189
- "invalid_spec_version",
190
- `Expected circuitry: "${CIRCUITRY_SPEC_VERSION}"`,
191
- ["circuitry"]
192
- );
193
- }
194
- if (graphObject.circuitry === CIRCUITRY_SPEC_VERSION && graphObject.resources && Object.keys(graphObject.resources).length > 0) {
195
- if (graphObject.agents && Object.keys(graphObject.agents).length > 0) {
196
- addError("v02_mixed_format", "v0.2 graphs with resources: must not use legacy agents: section", ["agents"]);
197
- }
198
- if (graphObject.inputs && Object.keys(graphObject.inputs).length > 0) {
199
- addError("v02_mixed_format", "v0.2 graphs with resources: must not use legacy inputs: section", ["inputs"]);
200
- }
201
- if (graphObject.edges && graphObject.edges.length > 0) {
202
- addError("v02_mixed_format", "v0.2 graphs with resources: must not use legacy edges: section", ["edges"]);
203
- }
204
- }
205
- const normalized = normalizeCircuitryGraph(graphObject);
206
- const ids = /* @__PURE__ */ new Set();
207
- for (const [index, node] of (normalized.nodes || []).entries()) {
208
- if (!node.id) {
209
- addError("missing_node_id", "Node is missing id", ["nodes", index, "id"]);
210
- continue;
211
- }
212
- if (ids.has(node.id)) {
213
- addError("duplicate_node_id", `Duplicate node id: ${node.id}`, ["nodes", index, "id"]);
214
- }
215
- ids.add(node.id);
216
- if (!node.kind) {
217
- addError("missing_node_kind", `Node ${node.id} is missing kind`, ["nodes", index, "kind"]);
218
- }
219
- }
220
- const adjacency = /* @__PURE__ */ new Map();
221
- const incomingCount = /* @__PURE__ */ new Map();
222
- for (const id of ids) {
223
- adjacency.set(id, []);
224
- incomingCount.set(id, 0);
225
- }
226
- for (const [index, edge] of (normalized.edges || []).entries()) {
227
- if (!edge.from || !edge.to) {
228
- addError("missing_edge_endpoint", "Edge is missing from/to", ["edges", index]);
229
- continue;
230
- }
231
- if (hasRule("no-self-loops") && edge.from === edge.to) {
232
- addError("self_loop", `Edge creates self loop: ${edge.from} -> ${edge.to}`, ["edges", index]);
233
- continue;
234
- }
235
- if (hasRule("no-unknown-edge-endpoints") && !ids.has(edge.from)) {
236
- addError("unknown_edge_source", `Edge references unknown source: ${edge.from}`, ["edges", index, "from"]);
237
- }
238
- if (hasRule("no-unknown-edge-endpoints") && !ids.has(edge.to)) {
239
- addError("unknown_edge_target", `Edge references unknown target: ${edge.to}`, ["edges", index, "to"]);
240
- }
241
- if (ids.has(edge.from) && ids.has(edge.to)) {
242
- adjacency.get(edge.from).push(edge.to);
243
- incomingCount.set(edge.to, (incomingCount.get(edge.to) || 0) + 1);
244
- }
245
- }
246
- const executableKinds = new Set(resolvedStandard.executableKinds);
247
- if (hasRule("require-executable-inputs")) {
248
- for (const [index, node] of (normalized.nodes || []).entries()) {
249
- if (executableKinds.has(node.kind) && (incomingCount.get(node.id) || 0) === 0) {
250
- addError("executable_without_inputs", `Executable node has no inputs: ${node.id}`, ["nodes", index]);
251
- }
252
- }
253
- }
254
- const visiting = /* @__PURE__ */ new Set();
255
- const visited = /* @__PURE__ */ new Set();
256
- const path = [];
257
- let cycleMessage = "";
258
- const visit = (id) => {
259
- if (cycleMessage) return;
260
- if (visiting.has(id)) {
261
- cycleMessage = [...path.slice(path.indexOf(id)), id].join(" -> ");
262
- return;
263
- }
264
- if (visited.has(id)) return;
265
- visiting.add(id);
266
- path.push(id);
267
- for (const next of adjacency.get(id) || []) visit(next);
268
- path.pop();
269
- visiting.delete(id);
270
- visited.add(id);
271
- };
272
- if (hasRule("no-cycles")) {
273
- for (const id of ids) {
274
- visit(id);
275
- if (cycleMessage) {
276
- addError("cycle", `Graph contains cycle: ${cycleMessage}`);
277
- break;
278
- }
279
- }
280
- }
281
- const additionalRules = rules.filter((rule) => {
282
- if (typeof rule === "string") return false;
283
- return rule.rule === "require-graph-field" || rule.rule === "require-node-field" || rule.rule === "require-edge-field" || rule.rule === "require-string" || rule.rule === "reject-string";
284
- });
285
- const fieldPath = (field) => field.split(".").filter(Boolean);
286
- const getField = (value, field) => fieldPath(field).reduce((current, key) => {
287
- if (!current || typeof current !== "object") return void 0;
288
- return current[key];
289
- }, value);
290
- const isPresent = (value) => value !== void 0 && value !== null && value !== "";
291
- for (const rule of additionalRules) {
292
- if (rule.rule === "require-graph-field") {
293
- if (!isPresent(getField(normalized, rule.field))) {
294
- addError("missing_required_graph_field", `Graph is missing required field: ${rule.field}`, fieldPath(rule.field));
295
- }
296
- continue;
297
- }
298
- if (rule.rule === "require-node-field") {
299
- const nodeKinds = new Set(rule.nodeKinds || []);
300
- for (const [index, node] of (normalized.nodes || []).entries()) {
301
- if (nodeKinds.size > 0 && !nodeKinds.has(node.kind)) continue;
302
- if (!isPresent(getField(node, rule.field))) {
303
- addError("missing_required_node_field", `Node ${node.id || "<missing id>"} is missing required field: ${rule.field}`, ["nodes", index, ...fieldPath(rule.field)]);
304
- }
305
- }
306
- continue;
307
- }
308
- if (rule.rule === "require-edge-field") {
309
- const edgeKinds = new Set(rule.edgeKinds || []);
310
- for (const [index, edge] of (normalized.edges || []).entries()) {
311
- if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind))) 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 || []) checkString("graph", normalized, "<root>", match, fieldPath(match.field));
330
- for (const match of rule.nodes || []) {
331
- const nodeKinds = new Set(match.nodeKinds || []);
332
- for (const [index, node] of (normalized.nodes || []).entries()) {
333
- if (nodeKinds.size > 0 && !nodeKinds.has(node.kind)) continue;
334
- checkString("node", node, node.id || "<missing id>", match, ["nodes", index, ...fieldPath(match.field)]);
335
- }
336
- }
337
- for (const match of rule.edges || []) {
338
- const edgeKinds = new Set(match.edgeKinds || []);
339
- for (const [index, edge] of (normalized.edges || []).entries()) {
340
- if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind))) continue;
341
- checkString("edge", edge, edge.id || String(index), match, ["edges", index, ...fieldPath(match.field)]);
342
- }
343
- }
344
- }
345
- return { ok: errors.length === 0, errors, standard: resolvedStandard };
346
- };
347
- var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map(
348
- (error) => error.message
349
- );
350
- var parseCircuitryJson = (text, standard = {}, options = {}) => {
351
- let graph;
352
- try {
353
- graph = JSON.parse(text);
354
- } catch (error) {
355
- const message = error instanceof Error ? error.message : String(error);
356
- throw new Error(`Could not parse Circuitry JSON. Check commas, quotes, and braces.
357
- ${message}`);
358
- }
359
- if (options.validate !== false) {
360
- const errors = validateCircuitryGraph(graph, standard);
361
- if (errors.length) {
362
- throw new Error(`Invalid Circuitry graph:
363
- ${errors.join("\n")}`);
364
- }
365
- }
366
- return normalizeCircuitryGraph(graph);
367
- };
368
- var stringifyCircuitryJson = (graph) => {
369
- return `${JSON.stringify(normalizeCircuitryGraph(graph), null, 2)}
370
- `;
371
- };
372
- var isUriInput = (input) => {
373
- return Boolean(input.uri || ["file", "url", "uri", "image", "mcp"].includes(input.type));
374
- };
375
-
376
- // src/yaml.ts
377
- import YAML from "yaml";
378
- var parseCircuitryYaml = (text, standard = {}, options = {}) => {
379
- let graph;
380
- try {
381
- graph = YAML.parse(text);
382
- } catch (error) {
383
- const message = error instanceof Error ? error.message : String(error);
384
- throw new Error(`Could not parse Circuitry YAML. Check indentation and quotes.
385
- ${message}`);
386
- }
387
- if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
388
- throw new Error("Could not parse Circuitry YAML. Expected a graph object.");
389
- }
390
- if (options.validate !== false) {
391
- const errors = validateCircuitryGraph(graph, standard);
392
- if (errors.length) {
393
- throw new Error(`Invalid Circuitry graph:
394
- ${errors.join("\n")}`);
395
- }
396
- }
397
- return normalizeCircuitryGraph(graph);
398
- };
399
- var stringifyCircuitryYaml = (graph) => {
400
- return YAML.stringify(normalizeCircuitryGraph(graph));
401
- };
402
- var parseCircuitryText = (text, filename = "graph.circuitry.yaml", standard = {}, options = {}) => {
403
- return filename.endsWith(".json") ? parseCircuitryJson(text, standard, options) : parseCircuitryYaml(text, standard, options);
404
- };
405
- var stringifyCircuitryText = (graph, filename = "graph.circuitry.yaml") => {
406
- return filename.endsWith(".json") ? stringifyCircuitryJson(graph) : stringifyCircuitryYaml(graph);
407
- };
408
-
409
- // src/presets.ts
410
- var CIRCUITRY_AGENT_PRESET_DIRS = [
411
- ".agents/agents",
412
- ".circuitry/agents",
413
- "~/.agents/agents",
414
- "~/.circuitry/agents"
415
- ];
416
- var parseAgentPresetMarkdown = (text, fallbackName) => {
417
- const frontmatter = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/m.exec(text);
418
- if (!frontmatter) {
419
- return {
420
- name: fallbackName,
421
- instructions: text.trim()
422
- };
423
- }
424
- const metadata = {};
425
- for (const line of frontmatter[1].split("\n")) {
426
- const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line.trim());
427
- if (match) {
428
- metadata[match[1]] = match[2].replace(/^['\"]|['\"]$/g, "");
429
- }
430
- }
431
- return {
432
- name: metadata.name || fallbackName,
433
- description: metadata.description,
434
- model: metadata.model,
435
- personality: metadata.personality,
436
- instructions: frontmatter[2].trim(),
437
- metadata
438
- };
439
- };
440
- var applyAgentPresets = (graph, presets) => {
441
- const nodes = (graph.nodes || []).map((node) => {
442
- if (node.kind !== "agent" || !node.agent?.uses) {
443
- return node;
444
- }
445
- const preset = presets[node.agent.uses];
446
- if (!preset) {
447
- return node;
448
- }
449
- return {
450
- ...node,
451
- label: node.label || preset.name,
452
- agent: {
453
- model: preset.model,
454
- personality: preset.personality,
455
- instructions: preset.instructions,
456
- identity: preset.name,
457
- ...node.agent
458
- }
459
- };
460
- });
461
- return { ...graph, nodes };
462
- };
463
-
464
- // src/scheduler.ts
465
- var buildCircuitryExecutionGraph = (graph) => {
466
- const normalized = normalizeCircuitryGraph(graph);
467
- const nodes = /* @__PURE__ */ new Map();
468
- const inputSets = /* @__PURE__ */ new Map();
469
- const outputSets = /* @__PURE__ */ new Map();
470
- for (const node of normalized.nodes || []) {
471
- if (node.kind !== "agent" && node.kind !== "tool" && node.kind !== "output" && node.kind !== "input") {
472
- continue;
473
- }
474
- nodes.set(node.id, {
475
- id: node.id,
476
- node,
477
- inputNodeIds: [],
478
- outputNodeIds: [],
479
- // Input nodes are already "done" with their value as output
480
- ...node.kind === "input" ? { output: node.input?.value || "", completed: true } : {}
481
- });
482
- inputSets.set(node.id, /* @__PURE__ */ new Set());
483
- outputSets.set(node.id, /* @__PURE__ */ 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(
515
- (input, index) => `Upstream ${index + 1} from ${input.nodeId}:
516
- ${input.output}`
517
- ).join("\n\n") : "No upstream node outputs.";
518
- const contextSection = contextInputs.length ? contextInputs.map(
519
- (input, index) => input.kind === "text" ? `Context ${index + 1} [text] from ${input.sourceId}:
520
- ${input.text || ""}` : `Context ${index + 1} [${input.kind}] from ${input.sourceId}${input.text ? `:
521
- ${input.text}` : ""}`
522
- ).join("\n\n") : "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("\n") : "- none",
529
- "Outgoing edges:",
530
- outgoingEdges.length ? outgoingEdges.map((edge) => describeEdge(edge, "outgoing")).join("\n") : "- none"
531
- ].join("\n");
532
- return [
533
- `Node label: ${node.label}`,
534
- agent.identity ? `Identity: ${agent.identity}` : "",
535
- agent.context ? `Context:
536
- ${agent.context}` : "",
537
- agent.personality ? `Personality:
538
- ${agent.personality}` : "",
539
- agent.instructions ? `Instructions:
540
- ${agent.instructions}` : "",
541
- wiringSection,
542
- `Upstream Node Outputs:
543
- ${upstreamSection}`,
544
- `Connected Context Inputs:
545
- ${contextSection}`
546
- ].filter(Boolean).join("\n\n");
547
- };
548
- var collectImages = (contextInputs) => contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
549
- var executeCircuitryNode = async ({
550
- graph,
551
- item,
552
- fallbackCycle,
553
- inputPayload,
554
- contextInputs,
555
- defaultModel,
556
- executeNode,
557
- onNodeStart,
558
- onNodeComplete
559
- }) => {
560
- onNodeStart?.(item.id, fallbackCycle);
561
- if (item.node.kind === "output") {
562
- const output = inputPayload.map((input) => input.output).join("\n\n");
563
- onNodeComplete?.(item.id, { output });
564
- return {
565
- nodeId: item.id,
566
- fallbackCycle,
567
- inputNodeIds: inputPayload.map((input) => input.nodeId),
568
- output
569
- };
570
- }
571
- const agent = item.node.agent || {};
572
- try {
573
- const result = await executeNode({
574
- nodeId: item.id,
575
- model: agent.model || defaultModel || "",
576
- tools: agent.tools || [],
577
- thinkingLevel: agent.thinkingLevel || "off",
578
- personality: agent.personality || "",
579
- instructions: agent.instructions || "",
580
- context: agent.context || "",
581
- inputs: inputPayload,
582
- contextInputs,
583
- images: collectImages(contextInputs),
584
- prompt: composeCircuitryPrompt(graph, item.node, inputPayload, contextInputs)
585
- });
586
- onNodeComplete?.(item.id, { output: result.output });
587
- return {
588
- nodeId: item.id,
589
- fallbackCycle,
590
- inputNodeIds: inputPayload.map((input) => input.nodeId),
591
- output: result.output
592
- };
593
- } catch (error) {
594
- const message = error instanceof Error ? error.message : String(error);
595
- onNodeComplete?.(item.id, { error: message });
596
- return {
597
- nodeId: item.id,
598
- fallbackCycle,
599
- inputNodeIds: inputPayload.map((input) => input.nodeId),
600
- output: "",
601
- error: message
602
- };
603
- }
604
- };
605
- var runCircuitryGraphExecution = async ({
606
- graph,
607
- selectedNodeId,
608
- executeNode,
609
- resolveContextInputs,
610
- onNodeStart,
611
- onNodeComplete,
612
- maxParallelRuns = 4
613
- }) => {
614
- const executionGraph = buildCircuitryExecutionGraph(graph);
615
- const runs = [];
616
- const parallelism = Math.max(1, Math.floor(maxParallelRuns));
617
- const defaultModel = graph.runtime?.model;
618
- const runOne = async (nodeId, outputs2) => {
619
- const fallbackCycle = false;
620
- const item = executionGraph.nodes.get(nodeId);
621
- const inputPayload = item.inputNodeIds.filter((sourceId) => outputs2.has(sourceId)).map((sourceId) => ({
622
- nodeId: sourceId,
623
- output: outputs2.get(sourceId)
624
- }));
625
- const contextInputs = await resolveContextInputs?.({
626
- nodeId,
627
- inputNodeIds: new Set(item.inputNodeIds),
628
- graph
629
- }) || [];
630
- return executeCircuitryNode({
631
- graph,
632
- item,
633
- fallbackCycle,
634
- inputPayload,
635
- contextInputs,
636
- defaultModel,
637
- executeNode,
638
- onNodeStart,
639
- onNodeComplete
640
- });
641
- };
642
- if (selectedNodeId) {
643
- if (!executionGraph.nodes.has(selectedNodeId)) {
644
- return runs;
645
- }
646
- runs.push(await runOne(selectedNodeId, /* @__PURE__ */ new Map()));
647
- return runs;
648
- }
649
- const completed = /* @__PURE__ */ new Set();
650
- const queued = /* @__PURE__ */ new Set();
651
- const outputs = /* @__PURE__ */ new Map();
652
- const nodeIds = [...executionGraph.nodes.keys()];
653
- const remainingDependencies = /* @__PURE__ */ new Map();
654
- const ready = [];
655
- for (const nodeId of nodeIds) {
656
- const node = executionGraph.nodes.get(nodeId);
657
- if (node.completed) {
658
- completed.add(nodeId);
659
- if (node.output) {
660
- outputs.set(nodeId, node.output);
661
- }
662
- continue;
663
- }
664
- const count = node.inputNodeIds.filter(
665
- (sourceId) => executionGraph.nodes.has(sourceId)
666
- ).length;
667
- remainingDependencies.set(nodeId, count);
668
- if (count === 0) {
669
- ready.push(nodeId);
670
- queued.add(nodeId);
671
- }
672
- }
673
- while (completed.size < nodeIds.length) {
674
- if (ready.length === 0) {
675
- const blocked = nodeIds.filter((id) => !completed.has(id));
676
- throw new Error(`Circuitry execution stalled; unresolved dependencies: ${blocked.join(", ")}`);
677
- }
678
- const chunk = ready.splice(0, parallelism);
679
- const chunkResults = await Promise.all(
680
- chunk.map(async (nodeId) => ({
681
- nodeId,
682
- result: await runOne(nodeId, outputs)
683
- }))
684
- );
685
- for (const { nodeId, result } of chunkResults) {
686
- outputs.set(nodeId, result.output);
687
- completed.add(nodeId);
688
- runs.push(result);
689
- for (const outputNodeId of executionGraph.nodes.get(nodeId).outputNodeIds) {
690
- if (completed.has(outputNodeId) || queued.has(outputNodeId)) {
691
- continue;
692
- }
693
- const nextCount = Math.max(
694
- 0,
695
- (remainingDependencies.get(outputNodeId) || 0) - 1
696
- );
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
-
708
- // src/simulation.ts
709
- var DEFAULT_MAX_PARALLEL_RUNS = 4;
710
- var pushUnique = (array, value) => {
711
- if (!array.includes(value)) {
712
- array.push(value);
713
- }
714
- };
715
- var buildSimulationGraph = (elements) => {
716
- const nodes = /* @__PURE__ */ new Map();
717
- for (const element of elements) {
718
- if (!hasCircuitryNodeData(element)) {
719
- continue;
720
- }
721
- nodes.set(element.id, {
722
- id: element.id,
723
- element,
724
- data: element.customData.circuitry,
725
- inputNodeIds: [],
726
- outputNodeIds: [],
727
- incomingSourceIds: []
728
- });
729
- }
730
- for (const element of elements) {
731
- if (element.type !== "arrow") {
732
- continue;
733
- }
734
- const arrow = element;
735
- const sourceId = arrow.startBinding?.elementId;
736
- const targetId = arrow.endBinding?.elementId;
737
- if (!sourceId || !targetId || !nodes.has(targetId)) {
738
- continue;
739
- }
740
- const target = nodes.get(targetId);
741
- pushUnique(target.incomingSourceIds, sourceId);
742
- if (!nodes.has(sourceId)) {
743
- continue;
744
- }
745
- const source = nodes.get(sourceId);
746
- pushUnique(source.outputNodeIds, targetId);
747
- pushUnique(target.inputNodeIds, sourceId);
748
- }
749
- return { nodes };
750
- };
751
- var formatContextInput = (input, index) => {
752
- if (input.kind === "text") {
753
- return `Context ${index + 1} [text] from ${input.sourceId}:
754
- ${input.text || ""}`;
755
- }
756
- if (input.kind === "image") {
757
- return `Context ${index + 1} [image] from ${input.sourceId}`;
758
- }
759
- const label = input.elementType ? `${input.elementType}` : "element";
760
- return `Context ${index + 1} [${label}] from ${input.sourceId}${input.text ? `:
761
- ${input.text}` : ""}`;
762
- };
763
- var composePrompt = (node, inputs, contextInputs) => {
764
- const upstreamSection = inputs.length === 0 ? "No upstream node outputs." : inputs.map(
765
- (input, index) => `Upstream ${index + 1} from ${input.nodeId}:
766
- ${input.output}`
767
- ).join("\n\n");
768
- const contextSection = contextInputs.length === 0 ? "No connected external context." : contextInputs.map(formatContextInput).join("\n\n");
769
- return [
770
- `Node label: ${node.data.label}`,
771
- node.data.context ? `Context:
772
- ${node.data.context}` : "",
773
- node.data.personality ? `Personality:
774
- ${node.data.personality}` : "",
775
- node.data.instructions ? `Instructions:
776
- ${node.data.instructions}` : "",
777
- `Upstream Node Outputs:
778
- ${upstreamSection}`,
779
- `Connected Context Inputs:
780
- ${contextSection}`
781
- ].filter(Boolean).join("\n\n");
782
- };
783
- var collectImages2 = (contextInputs) => {
784
- return contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
785
- };
786
- var executeSimulationNode = async ({
787
- node,
788
- fallbackCycle,
789
- inputPayload,
790
- contextInputs,
791
- executeNode,
792
- onNodeStart,
793
- onNodeComplete
794
- }) => {
795
- onNodeStart?.(node.id, fallbackCycle);
796
- try {
797
- const result = await executeNode({
798
- nodeId: node.id,
799
- model: node.data.model,
800
- tools: node.data.tools || [],
801
- thinkingLevel: node.data.thinkingLevel || "off",
802
- personality: node.data.personality,
803
- instructions: node.data.instructions,
804
- context: node.data.context,
805
- inputs: inputPayload,
806
- contextInputs,
807
- images: collectImages2(contextInputs),
808
- prompt: composePrompt(node, inputPayload, contextInputs)
809
- });
810
- const item = {
811
- nodeId: node.id,
812
- fallbackCycle,
813
- inputNodeIds: inputPayload.map((input) => input.nodeId),
814
- output: result.output
815
- };
816
- onNodeComplete?.(node.id, { output: result.output });
817
- return item;
818
- } catch (error) {
819
- const message = error instanceof Error ? error.message : String(error);
820
- const item = {
821
- nodeId: node.id,
822
- fallbackCycle,
823
- inputNodeIds: inputPayload.map((input) => input.nodeId),
824
- output: "",
825
- error: message
826
- };
827
- onNodeComplete?.(node.id, { error: message });
828
- return item;
829
- }
830
- };
831
- var runGraphExecution = async ({
832
- elements,
833
- mode,
834
- selectedNodeId,
835
- executeNode,
836
- resolveContextInputs,
837
- onNodeStart,
838
- onNodeComplete,
839
- maxParallelRuns = DEFAULT_MAX_PARALLEL_RUNS
840
- }) => {
841
- const graph = buildSimulationGraph(elements);
842
- const runs = [];
843
- const parallelism = Math.max(1, Math.floor(maxParallelRuns));
844
- if (mode === "single") {
845
- if (!selectedNodeId || !graph.nodes.has(selectedNodeId)) {
846
- return runs;
847
- }
848
- const node = graph.nodes.get(selectedNodeId);
849
- const inputPayload = node.inputNodeIds.map((sourceId) => graph.nodes.get(sourceId)).filter((sourceNode) => !!sourceNode?.data.output).map((sourceNode) => ({
850
- nodeId: sourceNode.id,
851
- output: sourceNode.data.output
852
- }));
853
- const contextInputs = await resolveContextInputs?.({
854
- nodeId: node.id,
855
- inputNodeIds: new Set(node.inputNodeIds),
856
- incomingSourceIds: node.incomingSourceIds,
857
- elements
858
- }) || [];
859
- runs.push(
860
- await executeSimulationNode({
861
- node,
862
- fallbackCycle: false,
863
- inputPayload,
864
- contextInputs,
865
- executeNode,
866
- onNodeStart,
867
- onNodeComplete
868
- })
869
- );
870
- return runs;
871
- }
872
- const completed = /* @__PURE__ */ new Set();
873
- const outputs = /* @__PURE__ */ new Map();
874
- const nodeIds = [...graph.nodes.keys()];
875
- while (completed.size < nodeIds.length) {
876
- const pending = nodeIds.filter((id) => !completed.has(id));
877
- const ready = pending.filter((id) => {
878
- const node = graph.nodes.get(id);
879
- return node.inputNodeIds.every((sourceId) => completed.has(sourceId));
880
- });
881
- if (ready.length === 0) {
882
- throw new Error(`Circuitry execution stalled; unresolved dependencies: ${pending.join(", ")}`);
883
- }
884
- const fallbackCycle = false;
885
- const currentBatch = ready;
886
- for (let index = 0; index < currentBatch.length; index += parallelism) {
887
- const chunk = currentBatch.slice(index, index + parallelism);
888
- const chunkResults = await Promise.all(
889
- 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
- );
913
- for (const { nodeId, result } of chunkResults) {
914
- outputs.set(nodeId, result.output);
915
- completed.add(nodeId);
916
- runs.push(result);
917
- }
918
- }
919
- }
920
- return runs;
921
- };
922
- var runDependencySimulation = async ({
923
- elements,
924
- executeNode,
925
- onNodeStart,
926
- onNodeComplete,
927
- maxParallelRuns
928
- }) => {
929
- return runGraphExecution({
930
- elements,
931
- mode: "dependency",
932
- executeNode,
933
- onNodeStart,
934
- onNodeComplete,
935
- maxParallelRuns
936
- });
937
- };
938
-
939
- // src/bundle.ts
940
- var BUNDLE_MIME = "application/vnd.circuitry.bundle+json";
941
- var deepClone = (value) => JSON.parse(JSON.stringify(value));
942
- var createBundleFromSelection = (elements, selectedIds) => {
943
- const selected = elements.filter((el) => selectedIds[el.id]);
944
- if (!selected.length) {
945
- return null;
946
- }
947
- const selectedSet = new Set(selected.map((el) => el.id));
948
- const relationships = elements.filter((el) => {
949
- if (el.type !== "arrow") {
950
- return false;
951
- }
952
- const arrow = el;
953
- const source = arrow.startBinding?.elementId;
954
- const target = arrow.endBinding?.elementId;
955
- return !!source && !!target && selectedSet.has(source) && selectedSet.has(target);
956
- });
957
- const dedup = /* @__PURE__ */ new Map();
958
- [...selected, ...relationships].forEach((el) => dedup.set(el.id, deepClone(el)));
959
- return {
960
- version: 1,
961
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
962
- elements: [...dedup.values()]
963
- };
964
- };
965
- var parseBundleText = (text) => {
966
- const parsed = JSON.parse(text);
967
- if (parsed.version !== 1 || !Array.isArray(parsed.elements)) {
968
- throw new Error("Invalid Circuitry bundle format");
969
- }
970
- return parsed;
971
- };
972
- var randomId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID().replaceAll("-", "") : Math.random().toString(36).slice(2, 12);
973
- var importBundleElements = (bundle, opts) => {
974
- const offsetX = opts?.offsetX ?? 24;
975
- const offsetY = opts?.offsetY ?? 24;
976
- const idMap = /* @__PURE__ */ new Map();
977
- for (const element of bundle.elements) {
978
- idMap.set(element.id, randomId());
979
- }
980
- return bundle.elements.map((element) => {
981
- const cloned = deepClone(element);
982
- const oldId = cloned.id;
983
- cloned.id = idMap.get(oldId);
984
- if (typeof cloned.x === "number") {
985
- cloned.x += offsetX;
986
- }
987
- if (typeof cloned.y === "number") {
988
- cloned.y += offsetY;
989
- }
990
- if (cloned.frameId && idMap.has(cloned.frameId)) {
991
- cloned.frameId = idMap.get(cloned.frameId);
992
- }
993
- if (cloned.boundElements?.length) {
994
- cloned.boundElements = cloned.boundElements.map(
995
- (item) => idMap.has(item.id) ? { ...item, id: idMap.get(item.id) } : item
996
- );
997
- }
998
- if (cloned.containerId && idMap.has(cloned.containerId)) {
999
- cloned.containerId = idMap.get(cloned.containerId);
1000
- }
1001
- if (cloned.type === "arrow") {
1002
- const arrow = cloned;
1003
- if (arrow.startBinding && idMap.has(arrow.startBinding.elementId)) {
1004
- arrow.startBinding = {
1005
- ...arrow.startBinding,
1006
- elementId: idMap.get(arrow.startBinding.elementId)
1007
- };
1008
- }
1009
- if (arrow.endBinding && idMap.has(arrow.endBinding.elementId)) {
1010
- arrow.endBinding = {
1011
- ...arrow.endBinding,
1012
- elementId: idMap.get(arrow.endBinding.elementId)
1013
- };
1014
- }
1015
- }
1016
- return cloned;
1017
- });
1018
- };
1019
-
1020
- // src/runtime.ts
1021
- var createUnsupportedRuntimeAdapter = (id) => ({
1022
- id,
1023
- label: String(id),
1024
- async runNode() {
1025
- throw new Error(`Circuitry runtime adapter is not configured: ${String(id)}`);
1026
- }
1027
- });
1028
- export {
1029
- BUNDLE_MIME,
1030
- CIRCUITRY_AGENT_PRESET_DIRS,
1031
- CIRCUITRY_NODE_CUSTOM_TYPE,
1032
- CIRCUITRY_NODE_KIND,
1033
- CIRCUITRY_SPEC_VERSION,
1034
- CIRCUITRY_SPEC_VERSION_LEGACY,
1035
- DEFAULT_CIRCUITRY_VALIDATION_RULES,
1036
- DEFAULT_CIRCUITRY_VALIDATION_STANDARD,
1037
- DEFAULT_MAX_PARALLEL_RUNS,
1038
- applyAgentPresets,
1039
- buildCircuitryExecutionGraph,
1040
- buildSimulationGraph,
1041
- createBundleFromSelection,
1042
- createCircuitryValidationStandard,
1043
- createDefaultNodeData,
1044
- createUnsupportedRuntimeAdapter,
1045
- hasCircuitryNodeData,
1046
- importBundleElements,
1047
- isNodeElement,
1048
- isUriInput,
1049
- normalizeCircuitryGraph,
1050
- parseAgentPresetMarkdown,
1051
- parseBundleText,
1052
- parseCircuitryJson,
1053
- parseCircuitryText,
1054
- parseCircuitryYaml,
1055
- runCircuitryGraphExecution,
1056
- runDependencySimulation,
1057
- runGraphExecution,
1058
- stringifyCircuitryJson,
1059
- stringifyCircuitryText,
1060
- stringifyCircuitryYaml,
1061
- validateCircuitryGraph,
1062
- validateCircuitryGraphWithStandard
1063
- };