@darkhorseprojects/circuitry 0.2.12 → 0.2.13
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/bundle.d.ts +31 -0
- package/dist/graph.d.ts +221 -0
- package/dist/host.d.ts +40 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1332 -0
- package/dist/node.d.ts +48 -0
- package/dist/presets.d.ts +4 -0
- package/dist/runtime.d.ts +16 -0
- package/dist/scheduler.d.ts +30 -0
- package/dist/simulation.d.ts +90 -0
- package/dist/tools.d.ts +547 -0
- package/dist/types.d.ts +42 -0
- package/dist/yaml.d.ts +9 -0
- package/package.json +1 -1
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
|
+
};
|