@darkhorseprojects/circuitry 0.3.1 → 0.3.2
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 +2 -1
- package/dist/cli.js +102 -252
- package/dist/graph.d.ts +11 -63
- package/dist/index.js +129 -309
- package/dist/node.d.ts +3 -3
- package/dist/node.js +123 -311
- package/dist/presets.d.ts +1 -1
- package/dist/scheduler.d.ts +1 -1
- package/dist/simulation.d.ts +3 -4
- package/dist/types.d.ts +0 -1
- package/dist/yaml.d.ts +4 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,9 +7,8 @@ var createDefaultNodeData = (label) => {
|
|
|
7
7
|
kind: CIRCUITRY_NODE_KIND,
|
|
8
8
|
identity,
|
|
9
9
|
label: identity,
|
|
10
|
-
model: "
|
|
10
|
+
model: "inherit",
|
|
11
11
|
tools: [],
|
|
12
|
-
thinkingLevel: "off",
|
|
13
12
|
personality: "",
|
|
14
13
|
instructions: "Define this agent's behavior and objective.",
|
|
15
14
|
context: "",
|
|
@@ -27,32 +26,8 @@ var isNodeElement = (element) => {
|
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
// src/graph.ts
|
|
30
|
-
|
|
31
|
-
var
|
|
32
|
-
if (typeof value === "string") return value;
|
|
33
|
-
if (value === void 0) return "";
|
|
34
|
-
return JSON.stringify(value, null, 2);
|
|
35
|
-
};
|
|
36
|
-
var applyCircuitryRuntimeInputs = (graph, inputs = {}) => {
|
|
37
|
-
const entries = Object.entries(inputs);
|
|
38
|
-
if (entries.length === 0) return graph;
|
|
39
|
-
const next = {
|
|
40
|
-
...graph,
|
|
41
|
-
resources: graph.resources ? { ...graph.resources } : graph.resources
|
|
42
|
-
};
|
|
43
|
-
for (const [id, value] of entries) {
|
|
44
|
-
const text = runtimeInputToText(value);
|
|
45
|
-
const resource = next.resources?.[id];
|
|
46
|
-
if (resource) {
|
|
47
|
-
if (resource.type !== "text")
|
|
48
|
-
throw new Error(`Runtime input ${id} targets non-text resource: ${resource.type}`);
|
|
49
|
-
next.resources[id] = { ...resource, value: text };
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
throw new Error(`Runtime input does not match a graph text resource: ${id}`);
|
|
53
|
-
}
|
|
54
|
-
return next;
|
|
55
|
-
};
|
|
29
|
+
import YAML from "yaml";
|
|
30
|
+
var CIRCUITRY_SPEC_VERSION = "0.3.2";
|
|
56
31
|
var DEFAULT_CIRCUITRY_VALIDATION_RULES = [
|
|
57
32
|
"no-self-loops",
|
|
58
33
|
"no-unknown-edge-endpoints",
|
|
@@ -65,25 +40,30 @@ var DEFAULT_CIRCUITRY_VALIDATION_STANDARD = {
|
|
|
65
40
|
rules: DEFAULT_CIRCUITRY_VALIDATION_RULES,
|
|
66
41
|
executableKinds: ["agent", "tool", "output"]
|
|
67
42
|
};
|
|
68
|
-
var
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
72
|
-
return executableRule?.executableKinds;
|
|
43
|
+
var runtimeInputToText = (value) => {
|
|
44
|
+
if (typeof value === "string") return value;
|
|
45
|
+
if (value === void 0) return "";
|
|
46
|
+
return YAML.stringify(value).trimEnd();
|
|
73
47
|
};
|
|
48
|
+
var applyCircuitryRuntimeInputs = (graph, inputs = {}) => {
|
|
49
|
+
const next = { ...graph, resources: graph.resources ? { ...graph.resources } : graph.resources };
|
|
50
|
+
for (const [id, value] of Object.entries(inputs)) {
|
|
51
|
+
const resource = next.resources?.[id];
|
|
52
|
+
if (!resource) throw new Error(`unknown arg: ${id}`);
|
|
53
|
+
if (resource.type !== "text") throw new Error(`arg is not text: ${id}`);
|
|
54
|
+
next.resources[id] = { ...resource, value: runtimeInputToText(value) };
|
|
55
|
+
}
|
|
56
|
+
return next;
|
|
57
|
+
};
|
|
58
|
+
var ruleName = (rule) => typeof rule === "string" ? rule : rule.rule;
|
|
59
|
+
var executableKindsFromRules = (rules) => rules.find((rule) => typeof rule !== "string" && rule.rule === "require-executable-inputs")?.executableKinds;
|
|
74
60
|
var createCircuitryValidationStandard = (standard = {}, graph) => {
|
|
75
|
-
const rules = [
|
|
76
|
-
...DEFAULT_CIRCUITRY_VALIDATION_RULES,
|
|
77
|
-
...graph?.validation?.rules || [],
|
|
78
|
-
...standard.rules || []
|
|
79
|
-
];
|
|
61
|
+
const rules = [...DEFAULT_CIRCUITRY_VALIDATION_RULES, ...graph?.validation?.rules || [], ...standard.rules || []];
|
|
80
62
|
return {
|
|
81
63
|
version: standard.version || DEFAULT_CIRCUITRY_VALIDATION_STANDARD.version,
|
|
82
64
|
requireSpecVersion: standard.requireSpecVersion ?? DEFAULT_CIRCUITRY_VALIDATION_STANDARD.requireSpecVersion,
|
|
83
65
|
rules,
|
|
84
|
-
executableKinds: standard.executableKinds || executableKindsFromRules(rules) || [
|
|
85
|
-
...DEFAULT_CIRCUITRY_VALIDATION_STANDARD.executableKinds
|
|
86
|
-
]
|
|
66
|
+
executableKinds: standard.executableKinds || executableKindsFromRules(rules) || [...DEFAULT_CIRCUITRY_VALIDATION_STANDARD.executableKinds]
|
|
87
67
|
};
|
|
88
68
|
};
|
|
89
69
|
var expandResources = (resources) => {
|
|
@@ -91,12 +71,7 @@ var expandResources = (resources) => {
|
|
|
91
71
|
const edges = [];
|
|
92
72
|
for (const [id, resource] of Object.entries(resources)) {
|
|
93
73
|
if (resource.type === "text") {
|
|
94
|
-
nodes.push({
|
|
95
|
-
id,
|
|
96
|
-
kind: "input",
|
|
97
|
-
label: resource.label || id,
|
|
98
|
-
input: { type: "text", value: resource.value }
|
|
99
|
-
});
|
|
74
|
+
nodes.push({ id, kind: "input", label: resource.label || id, input: { type: "text", value: resource.value } });
|
|
100
75
|
} else if (resource.type === "agent") {
|
|
101
76
|
const agent = resource;
|
|
102
77
|
nodes.push({
|
|
@@ -106,7 +81,6 @@ var expandResources = (resources) => {
|
|
|
106
81
|
agent: {
|
|
107
82
|
identity: agent.identity || agent.label || id,
|
|
108
83
|
model: agent.model,
|
|
109
|
-
thinkingLevel: agent.thinkingLevel,
|
|
110
84
|
tools: agent.tools,
|
|
111
85
|
instructions: agent.instructions,
|
|
112
86
|
personality: agent.personality,
|
|
@@ -115,288 +89,132 @@ var expandResources = (resources) => {
|
|
|
115
89
|
skills: agent.skills,
|
|
116
90
|
expect: agent.expect
|
|
117
91
|
});
|
|
118
|
-
for (const
|
|
119
|
-
edges.push({ from: inputRef, to: id, kind: "dependency" });
|
|
120
|
-
}
|
|
92
|
+
for (const input of agent.inputs || []) edges.push({ from: input, to: id, kind: "dependency" });
|
|
121
93
|
} else if (resource.type === "tool") {
|
|
122
94
|
const tool = resource;
|
|
123
|
-
nodes.push({
|
|
124
|
-
|
|
125
|
-
kind: "tool",
|
|
126
|
-
label: tool.label || id,
|
|
127
|
-
agent: { instructions: tool.instructions }
|
|
128
|
-
});
|
|
129
|
-
for (const inputRef of tool.inputs || []) {
|
|
130
|
-
edges.push({ from: inputRef, to: id, kind: "dependency" });
|
|
131
|
-
}
|
|
95
|
+
nodes.push({ id, kind: "tool", label: tool.label || id, agent: { instructions: tool.instructions } });
|
|
96
|
+
for (const input of tool.inputs || []) edges.push({ from: input, to: id, kind: "dependency" });
|
|
132
97
|
} else {
|
|
133
98
|
const input = resource;
|
|
134
|
-
nodes.push({
|
|
135
|
-
id,
|
|
136
|
-
kind: "input",
|
|
137
|
-
label: input.label || id,
|
|
138
|
-
input: {
|
|
139
|
-
type: input.type,
|
|
140
|
-
value: input.value,
|
|
141
|
-
uri: input.uri || input.path,
|
|
142
|
-
mimeType: input.mimeType,
|
|
143
|
-
data: input.data
|
|
144
|
-
}
|
|
145
|
-
});
|
|
99
|
+
nodes.push({ id, kind: "input", label: input.label || id, input: { type: input.type, value: input.value, uri: input.uri || input.path, mimeType: input.mimeType, data: input.data } });
|
|
146
100
|
}
|
|
147
101
|
}
|
|
148
102
|
return { nodes, edges };
|
|
149
103
|
};
|
|
150
104
|
var normalizeCircuitryGraph = (graph) => {
|
|
151
|
-
if (!graph.resources
|
|
152
|
-
|
|
153
|
-
return { ...graph, nodes, edges };
|
|
105
|
+
if (!graph.resources) return { ...graph };
|
|
106
|
+
return { ...graph, ...expandResources(graph.resources) };
|
|
154
107
|
};
|
|
155
|
-
var
|
|
156
|
-
|
|
157
|
-
const resolvedStandard = createCircuitryValidationStandard(
|
|
158
|
-
standard,
|
|
159
|
-
graph && typeof graph === "object" && !Array.isArray(graph) ? graph : void 0
|
|
160
|
-
);
|
|
108
|
+
var validateCircuitryGraphInternal = (graph, standard = {}, source) => {
|
|
109
|
+
const resolved = createCircuitryValidationStandard(standard, graph && typeof graph === "object" && !Array.isArray(graph) ? graph : void 0);
|
|
161
110
|
const errors = [];
|
|
162
|
-
const
|
|
163
|
-
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
164
|
-
|
|
165
|
-
|
|
111
|
+
const issue = (code, message = code, path) => errors.push({ code, message, ...path ? { path } : {} });
|
|
112
|
+
if (!graph || typeof graph !== "object" || Array.isArray(graph)) return { ok: false, errors: [{ code: "invalid_graph", message: "invalid graph" }], standard: resolved };
|
|
113
|
+
const g = graph;
|
|
114
|
+
if (resolved.requireSpecVersion && g.circuitry !== resolved.version) issue("invalid_version", "invalid version", ["circuitry"]);
|
|
115
|
+
if (source) {
|
|
116
|
+
const raw = g;
|
|
117
|
+
if (!g.resources || Object.keys(g.resources).length === 0) issue("missing_resources", "missing resources", ["resources"]);
|
|
118
|
+
if (raw.agents) issue("forbidden_agents", "forbidden agents", ["agents"]);
|
|
119
|
+
if (raw.inputs) issue("forbidden_inputs", "forbidden inputs", ["inputs"]);
|
|
120
|
+
if (g.nodes?.length) issue("forbidden_nodes", "forbidden nodes", ["nodes"]);
|
|
121
|
+
if (g.edges?.length) issue("forbidden_edges", "forbidden edges", ["edges"]);
|
|
122
|
+
if (raw.links) issue("forbidden_links", "forbidden links", ["links"]);
|
|
166
123
|
}
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const hasRule = (name) => rules.some((entry) => (typeof entry === "string" ? entry : entry.rule) === name);
|
|
170
|
-
if (resolvedStandard.requireSpecVersion && !VALID_SPEC_VERSIONS.has(String(graphObject.circuitry))) {
|
|
171
|
-
addError(
|
|
172
|
-
"invalid_spec_version",
|
|
173
|
-
`Expected circuitry: "${CIRCUITRY_SPEC_VERSION}"`,
|
|
174
|
-
["circuitry"]
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
if (options.sourceFormat) {
|
|
178
|
-
if (!graphObject.resources || Object.keys(graphObject.resources).length === 0) {
|
|
179
|
-
addError("missing_resources", "Circuitry graphs must use a resources: section", ["resources"]);
|
|
180
|
-
}
|
|
181
|
-
if (graphObject.agents && Object.keys(graphObject.agents).length > 0) {
|
|
182
|
-
addError("forbidden_agents", "Circuitry graph files must not use top-level agents:; use resources:", ["agents"]);
|
|
183
|
-
}
|
|
184
|
-
if (graphObject.inputs && Object.keys(graphObject.inputs).length > 0) {
|
|
185
|
-
addError("forbidden_inputs", "Circuitry graph files use args: for runtime arguments and resource inputs for edges", ["inputs"]);
|
|
186
|
-
}
|
|
187
|
-
if (graphObject.nodes && graphObject.nodes.length > 0) {
|
|
188
|
-
addError("forbidden_nodes", "Circuitry graph files must not use top-level nodes:; use resources:", ["nodes"]);
|
|
189
|
-
}
|
|
190
|
-
if (graphObject.edges && graphObject.edges.length > 0) {
|
|
191
|
-
addError("forbidden_edges", "Circuitry graph files must not use top-level edges:; use resource inputs:", ["edges"]);
|
|
192
|
-
}
|
|
193
|
-
if (graphObject.links && graphObject.links.length > 0) {
|
|
194
|
-
addError("forbidden_links", "Circuitry graph files use imports: for multi-file graphs", ["links"]);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
const normalized = normalizeCircuitryGraph(graphObject);
|
|
124
|
+
const rules = new Set(resolved.rules.map(ruleName));
|
|
125
|
+
const normalized = normalizeCircuitryGraph(g);
|
|
198
126
|
const ids = /* @__PURE__ */ new Set();
|
|
199
127
|
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
200
128
|
if (!node.id) {
|
|
201
|
-
|
|
129
|
+
issue("missing_node_id", "missing node id", ["nodes", index, "id"]);
|
|
202
130
|
continue;
|
|
203
131
|
}
|
|
204
|
-
if (ids.has(node.id))
|
|
205
|
-
addError("duplicate_node_id", `Duplicate node id: ${node.id}`, ["nodes", index, "id"]);
|
|
206
|
-
}
|
|
132
|
+
if (ids.has(node.id)) issue("duplicate_node_id", "duplicate node id", ["nodes", index, "id"]);
|
|
207
133
|
ids.add(node.id);
|
|
208
|
-
if (!node.kind)
|
|
209
|
-
addError("missing_node_kind", `Node ${node.id} is missing kind`, ["nodes", index, "kind"]);
|
|
210
|
-
}
|
|
134
|
+
if (!node.kind) issue("missing_node_kind", "missing node kind", ["nodes", index, "kind"]);
|
|
211
135
|
}
|
|
212
136
|
const adjacency = /* @__PURE__ */ new Map();
|
|
213
|
-
const
|
|
137
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
214
138
|
for (const id of ids) {
|
|
215
139
|
adjacency.set(id, []);
|
|
216
|
-
|
|
140
|
+
incoming.set(id, 0);
|
|
217
141
|
}
|
|
218
142
|
for (const [index, edge] of (normalized.edges || []).entries()) {
|
|
219
143
|
if (!edge.from || !edge.to) {
|
|
220
|
-
|
|
144
|
+
issue("missing_edge_endpoint", "missing edge endpoint", ["edges", index]);
|
|
221
145
|
continue;
|
|
222
146
|
}
|
|
223
|
-
if (
|
|
224
|
-
|
|
147
|
+
if (rules.has("no-self-loops") && edge.from === edge.to) {
|
|
148
|
+
issue("self_loop", "self loop", ["edges", index]);
|
|
225
149
|
continue;
|
|
226
150
|
}
|
|
227
|
-
|
|
228
|
-
|
|
151
|
+
const ok = ids.has(edge.from) && ids.has(edge.to);
|
|
152
|
+
if (rules.has("no-unknown-edge-endpoints")) {
|
|
153
|
+
if (!ids.has(edge.from)) issue("unknown_edge_source", "unknown edge source", ["edges", index, "from"]);
|
|
154
|
+
if (!ids.has(edge.to)) issue("unknown_edge_target", "unknown edge target", ["edges", index, "to"]);
|
|
229
155
|
}
|
|
230
|
-
if (
|
|
231
|
-
addError("unknown_edge_target", `Edge references unknown target: ${edge.to}`, ["edges", index, "to"]);
|
|
232
|
-
}
|
|
233
|
-
if (ids.has(edge.from) && ids.has(edge.to)) {
|
|
156
|
+
if (ok) {
|
|
234
157
|
adjacency.get(edge.from).push(edge.to);
|
|
235
|
-
|
|
158
|
+
incoming.set(edge.to, (incoming.get(edge.to) || 0) + 1);
|
|
236
159
|
}
|
|
237
160
|
}
|
|
238
|
-
|
|
239
|
-
|
|
161
|
+
if (rules.has("require-executable-inputs")) {
|
|
162
|
+
const executable = new Set(resolved.executableKinds);
|
|
240
163
|
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
241
|
-
if (
|
|
242
|
-
addError("executable_without_inputs", `Executable node has no inputs: ${node.id}`, ["nodes", index]);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
247
|
-
const visited = /* @__PURE__ */ new Set();
|
|
248
|
-
const path = [];
|
|
249
|
-
let cycleMessage = "";
|
|
250
|
-
const visit = (id) => {
|
|
251
|
-
if (cycleMessage) return;
|
|
252
|
-
if (visiting.has(id)) {
|
|
253
|
-
cycleMessage = [...path.slice(path.indexOf(id)), id].join(" -> ");
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
if (visited.has(id)) return;
|
|
257
|
-
visiting.add(id);
|
|
258
|
-
path.push(id);
|
|
259
|
-
for (const next of adjacency.get(id) || []) visit(next);
|
|
260
|
-
path.pop();
|
|
261
|
-
visiting.delete(id);
|
|
262
|
-
visited.add(id);
|
|
263
|
-
};
|
|
264
|
-
if (hasRule("no-cycles")) {
|
|
265
|
-
for (const id of ids) {
|
|
266
|
-
visit(id);
|
|
267
|
-
if (cycleMessage) {
|
|
268
|
-
addError("cycle", `Graph contains cycle: ${cycleMessage}`);
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
164
|
+
if (executable.has(node.kind) && (incoming.get(node.id) || 0) === 0) issue("missing_executable_input", "missing executable input", ["nodes", index]);
|
|
271
165
|
}
|
|
272
166
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (rule.rule === "require-graph-field") {
|
|
285
|
-
if (!isPresent(getField(normalized, rule.field))) {
|
|
286
|
-
addError("missing_required_graph_field", `Graph is missing required field: ${rule.field}`, fieldPath(rule.field));
|
|
287
|
-
}
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
if (rule.rule === "require-node-field") {
|
|
291
|
-
const nodeKinds = new Set(rule.nodeKinds || []);
|
|
292
|
-
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
293
|
-
if (nodeKinds.size > 0 && !nodeKinds.has(node.kind)) continue;
|
|
294
|
-
if (!isPresent(getField(node, rule.field))) {
|
|
295
|
-
addError("missing_required_node_field", `Node ${node.id || "<missing id>"} is missing required field: ${rule.field}`, ["nodes", index, ...fieldPath(rule.field)]);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
if (rule.rule === "require-edge-field") {
|
|
301
|
-
const edgeKinds = new Set(rule.edgeKinds || []);
|
|
302
|
-
for (const [index, edge] of (normalized.edges || []).entries()) {
|
|
303
|
-
if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind))) continue;
|
|
304
|
-
if (!isPresent(getField(edge, rule.field))) {
|
|
305
|
-
addError("missing_required_edge_field", `Edge ${edge.id || index} is missing required field: ${rule.field}`, ["edges", index, ...fieldPath(rule.field)]);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
const checkString = (target, subject, id, match, path2) => {
|
|
311
|
-
const value = getField(subject, match.field);
|
|
312
|
-
const text = typeof value === "string" ? value : "";
|
|
313
|
-
const includes = text.includes(match.value);
|
|
314
|
-
if (rule.rule === "require-string" && !includes) {
|
|
315
|
-
addError("missing_required_string", `${target} ${id} field ${match.field} must include string: ${match.value}`, path2);
|
|
316
|
-
}
|
|
317
|
-
if (rule.rule === "reject-string" && includes) {
|
|
318
|
-
addError("rejected_string", `${target} ${id} field ${match.field} must not include string: ${match.value}`, path2);
|
|
319
|
-
}
|
|
167
|
+
if (rules.has("no-cycles")) {
|
|
168
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
169
|
+
const visited = /* @__PURE__ */ new Set();
|
|
170
|
+
const visit = (id) => {
|
|
171
|
+
if (visiting.has(id)) return true;
|
|
172
|
+
if (visited.has(id)) return false;
|
|
173
|
+
visiting.add(id);
|
|
174
|
+
for (const next of adjacency.get(id) || []) if (visit(next)) return true;
|
|
175
|
+
visiting.delete(id);
|
|
176
|
+
visited.add(id);
|
|
177
|
+
return false;
|
|
320
178
|
};
|
|
321
|
-
for (const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
325
|
-
if (nodeKinds.size > 0 && !nodeKinds.has(node.kind)) continue;
|
|
326
|
-
checkString("node", node, node.id || "<missing id>", match, ["nodes", index, ...fieldPath(match.field)]);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
for (const match of rule.edges || []) {
|
|
330
|
-
const edgeKinds = new Set(match.edgeKinds || []);
|
|
331
|
-
for (const [index, edge] of (normalized.edges || []).entries()) {
|
|
332
|
-
if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind))) continue;
|
|
333
|
-
checkString("edge", edge, edge.id || String(index), match, ["edges", index, ...fieldPath(match.field)]);
|
|
334
|
-
}
|
|
179
|
+
for (const id of ids) if (visit(id)) {
|
|
180
|
+
issue("cycle", "cycle");
|
|
181
|
+
break;
|
|
335
182
|
}
|
|
336
183
|
}
|
|
337
|
-
return { ok: errors.length === 0, errors, standard:
|
|
338
|
-
};
|
|
339
|
-
var validateCircuitryGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, { sourceFormat: true });
|
|
340
|
-
var validateCircuitryExecutionGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, { sourceFormat: false });
|
|
341
|
-
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map(
|
|
342
|
-
(error) => error.message
|
|
343
|
-
);
|
|
344
|
-
var parseCircuitryJson = (text, standard = {}, options = {}) => {
|
|
345
|
-
let graph;
|
|
346
|
-
try {
|
|
347
|
-
graph = JSON.parse(text);
|
|
348
|
-
} catch (error) {
|
|
349
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
350
|
-
throw new Error(`Could not parse Circuitry JSON. Check commas, quotes, and braces.
|
|
351
|
-
${message}`);
|
|
352
|
-
}
|
|
353
|
-
if (options.validate === false) return graph;
|
|
354
|
-
const errors = validateCircuitryGraph(graph, standard);
|
|
355
|
-
if (errors.length) {
|
|
356
|
-
throw new Error(`Invalid Circuitry graph:
|
|
357
|
-
${errors.join("\n")}`);
|
|
358
|
-
}
|
|
359
|
-
return normalizeCircuitryGraph(graph);
|
|
360
|
-
};
|
|
361
|
-
var stringifyCircuitryJson = (graph) => {
|
|
362
|
-
return `${JSON.stringify(normalizeCircuitryGraph(graph), null, 2)}
|
|
363
|
-
`;
|
|
364
|
-
};
|
|
365
|
-
var isUriInput = (input) => {
|
|
366
|
-
return Boolean(input.uri || ["file", "url", "uri", "image", "mcp"].includes(input.type));
|
|
184
|
+
return { ok: errors.length === 0, errors, standard: resolved };
|
|
367
185
|
};
|
|
186
|
+
var validateCircuitryGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, true);
|
|
187
|
+
var validateCircuitryExecutionGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, false);
|
|
188
|
+
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map((error) => error.message);
|
|
189
|
+
var isUriInput = (input) => Boolean(input.uri || ["file", "url", "uri", "image", "mcp"].includes(input.type));
|
|
368
190
|
|
|
369
191
|
// src/yaml.ts
|
|
370
|
-
import
|
|
371
|
-
var
|
|
372
|
-
let graph;
|
|
192
|
+
import YAML2 from "yaml";
|
|
193
|
+
var parseYamlData = (text) => {
|
|
373
194
|
try {
|
|
374
|
-
|
|
195
|
+
return YAML2.parse(text);
|
|
375
196
|
} catch (error) {
|
|
376
197
|
const message = error instanceof Error ? error.message : String(error);
|
|
377
|
-
throw new Error(`Could not parse
|
|
198
|
+
throw new Error(`Could not parse YAML.
|
|
378
199
|
${message}`);
|
|
379
200
|
}
|
|
201
|
+
};
|
|
202
|
+
var stringifyYamlData = (value) => YAML2.stringify(value);
|
|
203
|
+
var parseCircuitryYaml = (text, standard = {}, options = {}) => {
|
|
204
|
+
const graph = parseYamlData(text);
|
|
380
205
|
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
381
|
-
throw new Error("
|
|
206
|
+
throw new Error("Circuitry YAML must be a graph object.");
|
|
382
207
|
}
|
|
383
|
-
if (options.validate
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
throw new Error(`Invalid Circuitry graph:
|
|
208
|
+
if (options.validate !== false) {
|
|
209
|
+
const errors = validateCircuitryGraph(graph, standard);
|
|
210
|
+
if (errors.length) throw new Error(`Invalid Circuitry graph:
|
|
387
211
|
${errors.join("\n")}`);
|
|
388
212
|
}
|
|
389
|
-
return
|
|
390
|
-
};
|
|
391
|
-
var stringifyCircuitryYaml = (graph) => {
|
|
392
|
-
return YAML.stringify(normalizeCircuitryGraph(graph));
|
|
393
|
-
};
|
|
394
|
-
var parseCircuitryText = (text, filename = "graph.circuitry.yaml", standard = {}, options = {}) => {
|
|
395
|
-
return filename.endsWith(".json") ? parseCircuitryJson(text, standard, options) : parseCircuitryYaml(text, standard, options);
|
|
396
|
-
};
|
|
397
|
-
var stringifyCircuitryText = (graph, filename = "graph.circuitry.yaml") => {
|
|
398
|
-
return filename.endsWith(".json") ? stringifyCircuitryJson(graph) : stringifyCircuitryYaml(graph);
|
|
213
|
+
return graph;
|
|
399
214
|
};
|
|
215
|
+
var stringifyCircuitryYaml = (graph) => YAML2.stringify(graph);
|
|
216
|
+
var parseCircuitryText = (text, standard = {}, options = {}) => parseCircuitryYaml(text, standard, options);
|
|
217
|
+
var stringifyCircuitryText = (graph) => stringifyCircuitryYaml(graph);
|
|
400
218
|
|
|
401
219
|
// src/presets.ts
|
|
402
220
|
var CIRCUITRY_AGENT_PRESET_DIRS = [
|
|
@@ -405,11 +223,11 @@ var CIRCUITRY_AGENT_PRESET_DIRS = [
|
|
|
405
223
|
"~/.agents/agents",
|
|
406
224
|
"~/.circuitry/agents"
|
|
407
225
|
];
|
|
408
|
-
var parseAgentPresetMarkdown = (text,
|
|
226
|
+
var parseAgentPresetMarkdown = (text, defaultName) => {
|
|
409
227
|
const frontmatter = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/m.exec(text);
|
|
410
228
|
if (!frontmatter) {
|
|
411
229
|
return {
|
|
412
|
-
name:
|
|
230
|
+
name: defaultName,
|
|
413
231
|
instructions: text.trim()
|
|
414
232
|
};
|
|
415
233
|
}
|
|
@@ -421,7 +239,7 @@ var parseAgentPresetMarkdown = (text, fallbackName) => {
|
|
|
421
239
|
}
|
|
422
240
|
}
|
|
423
241
|
return {
|
|
424
|
-
name: metadata.name ||
|
|
242
|
+
name: metadata.name || defaultName,
|
|
425
243
|
description: metadata.description,
|
|
426
244
|
model: metadata.model,
|
|
427
245
|
personality: metadata.personality,
|
|
@@ -454,6 +272,7 @@ var applyAgentPresets = (graph, presets) => {
|
|
|
454
272
|
};
|
|
455
273
|
|
|
456
274
|
// src/scheduler.ts
|
|
275
|
+
import YAML3 from "yaml";
|
|
457
276
|
var buildCircuitryExecutionGraph = (graph) => {
|
|
458
277
|
const normalized = normalizeCircuitryGraph(graph);
|
|
459
278
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -493,7 +312,7 @@ var formatEdgeData = (data) => {
|
|
|
493
312
|
if (!data || Object.keys(data).length === 0) {
|
|
494
313
|
return "";
|
|
495
314
|
}
|
|
496
|
-
return ` data=${
|
|
315
|
+
return ` data=${YAML3.stringify(data).trimEnd()}`;
|
|
497
316
|
};
|
|
498
317
|
var describeEdge = (edge, direction) => {
|
|
499
318
|
const otherId = direction === "incoming" ? edge.from : edge.to;
|
|
@@ -538,17 +357,17 @@ ${contextSection}`
|
|
|
538
357
|
].filter(Boolean).join("\n\n");
|
|
539
358
|
};
|
|
540
359
|
var collectImages = (contextInputs) => contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
|
|
541
|
-
var
|
|
360
|
+
var stripYamlFence = (output) => {
|
|
542
361
|
const trimmed = output.trim();
|
|
543
|
-
const fenced = trimmed.match(/^```(?:
|
|
362
|
+
const fenced = trimmed.match(/^```(?:ya?ml)?\s*([\s\S]*?)\s*```$/i);
|
|
544
363
|
return fenced ? fenced[1].trim() : trimmed;
|
|
545
364
|
};
|
|
546
365
|
var parseExpectedOutput = (nodeId, output) => {
|
|
547
366
|
try {
|
|
548
|
-
return
|
|
367
|
+
return YAML3.parse(stripYamlFence(output));
|
|
549
368
|
} catch (error) {
|
|
550
369
|
const detail = error instanceof Error ? error.message : String(error);
|
|
551
|
-
throw new Error(`Node ${nodeId} output does not match expect: output is not valid
|
|
370
|
+
throw new Error(`Node ${nodeId} output does not match expect: output is not valid YAML (${detail})`);
|
|
552
371
|
}
|
|
553
372
|
};
|
|
554
373
|
var isFieldObject = (schema) => !!schema && typeof schema === "object" && !Array.isArray(schema) && typeof schema.type === "string";
|
|
@@ -631,7 +450,7 @@ ${errors.join("\n")}`);
|
|
|
631
450
|
var executeCircuitryNode = async ({
|
|
632
451
|
graph,
|
|
633
452
|
item,
|
|
634
|
-
|
|
453
|
+
cycle,
|
|
635
454
|
inputPayload,
|
|
636
455
|
contextInputs,
|
|
637
456
|
defaultModel,
|
|
@@ -639,13 +458,13 @@ var executeCircuitryNode = async ({
|
|
|
639
458
|
onNodeStart,
|
|
640
459
|
onNodeComplete
|
|
641
460
|
}) => {
|
|
642
|
-
onNodeStart?.(item.id,
|
|
461
|
+
onNodeStart?.(item.id, cycle);
|
|
643
462
|
if (item.node.kind === "output") {
|
|
644
463
|
const output = inputPayload.map((input) => input.output).join("\n\n");
|
|
645
464
|
onNodeComplete?.(item.id, { output });
|
|
646
465
|
return {
|
|
647
466
|
nodeId: item.id,
|
|
648
|
-
|
|
467
|
+
cycle,
|
|
649
468
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
650
469
|
output
|
|
651
470
|
};
|
|
@@ -657,7 +476,6 @@ var executeCircuitryNode = async ({
|
|
|
657
476
|
nodeId: item.id,
|
|
658
477
|
model: (agent.model === "inherit" ? void 0 : agent.model) || (defaultModel === "inherit" ? void 0 : defaultModel) || "inherit",
|
|
659
478
|
tools: agent.tools || [],
|
|
660
|
-
thinkingLevel: agent.thinkingLevel || "off",
|
|
661
479
|
personality: agent.personality || "",
|
|
662
480
|
instructions: agent.instructions || "",
|
|
663
481
|
context: agent.context || "",
|
|
@@ -671,7 +489,7 @@ var executeCircuitryNode = async ({
|
|
|
671
489
|
onNodeComplete?.(item.id, { error: message });
|
|
672
490
|
return {
|
|
673
491
|
nodeId: item.id,
|
|
674
|
-
|
|
492
|
+
cycle,
|
|
675
493
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
676
494
|
output: "",
|
|
677
495
|
error: message
|
|
@@ -681,7 +499,7 @@ var executeCircuitryNode = async ({
|
|
|
681
499
|
onNodeComplete?.(item.id, { output: result.output });
|
|
682
500
|
return {
|
|
683
501
|
nodeId: item.id,
|
|
684
|
-
|
|
502
|
+
cycle,
|
|
685
503
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
686
504
|
output: result.output
|
|
687
505
|
};
|
|
@@ -700,7 +518,7 @@ var runCircuitryGraphExecution = async ({
|
|
|
700
518
|
const parallelism = Math.max(1, Math.floor(maxParallelRuns));
|
|
701
519
|
const defaultModel = graph.runtime?.model;
|
|
702
520
|
const runOne = async (nodeId, outputs2) => {
|
|
703
|
-
const
|
|
521
|
+
const cycle = false;
|
|
704
522
|
const item = executionGraph.nodes.get(nodeId);
|
|
705
523
|
const inputPayload = item.inputNodeIds.filter((sourceId) => outputs2.has(sourceId)).map((sourceId) => ({
|
|
706
524
|
nodeId: sourceId,
|
|
@@ -714,7 +532,7 @@ var runCircuitryGraphExecution = async ({
|
|
|
714
532
|
return executeCircuitryNode({
|
|
715
533
|
graph,
|
|
716
534
|
item,
|
|
717
|
-
|
|
535
|
+
cycle,
|
|
718
536
|
inputPayload,
|
|
719
537
|
contextInputs,
|
|
720
538
|
defaultModel,
|
|
@@ -869,20 +687,19 @@ var collectImages2 = (contextInputs) => {
|
|
|
869
687
|
};
|
|
870
688
|
var executeSimulationNode = async ({
|
|
871
689
|
node,
|
|
872
|
-
|
|
690
|
+
cycle,
|
|
873
691
|
inputPayload,
|
|
874
692
|
contextInputs,
|
|
875
693
|
executeNode,
|
|
876
694
|
onNodeStart,
|
|
877
695
|
onNodeComplete
|
|
878
696
|
}) => {
|
|
879
|
-
onNodeStart?.(node.id,
|
|
697
|
+
onNodeStart?.(node.id, cycle);
|
|
880
698
|
try {
|
|
881
699
|
const result = await executeNode({
|
|
882
700
|
nodeId: node.id,
|
|
883
701
|
model: node.data.model === "inherit" ? "inherit" : node.data.model || "inherit",
|
|
884
702
|
tools: node.data.tools || [],
|
|
885
|
-
thinkingLevel: node.data.thinkingLevel || "off",
|
|
886
703
|
personality: node.data.personality,
|
|
887
704
|
instructions: node.data.instructions,
|
|
888
705
|
context: node.data.context,
|
|
@@ -893,7 +710,7 @@ var executeSimulationNode = async ({
|
|
|
893
710
|
});
|
|
894
711
|
const item = {
|
|
895
712
|
nodeId: node.id,
|
|
896
|
-
|
|
713
|
+
cycle,
|
|
897
714
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
898
715
|
output: result.output
|
|
899
716
|
};
|
|
@@ -903,7 +720,7 @@ var executeSimulationNode = async ({
|
|
|
903
720
|
const message = error instanceof Error ? error.message : String(error);
|
|
904
721
|
const item = {
|
|
905
722
|
nodeId: node.id,
|
|
906
|
-
|
|
723
|
+
cycle,
|
|
907
724
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
908
725
|
output: "",
|
|
909
726
|
error: message
|
|
@@ -943,7 +760,7 @@ var runGraphExecution = async ({
|
|
|
943
760
|
runs.push(
|
|
944
761
|
await executeSimulationNode({
|
|
945
762
|
node,
|
|
946
|
-
|
|
763
|
+
cycle: false,
|
|
947
764
|
inputPayload,
|
|
948
765
|
contextInputs,
|
|
949
766
|
executeNode,
|
|
@@ -965,7 +782,7 @@ var runGraphExecution = async ({
|
|
|
965
782
|
if (ready.length === 0) {
|
|
966
783
|
throw new Error(`Circuitry execution stalled; unresolved dependencies: ${pending.join(", ")}`);
|
|
967
784
|
}
|
|
968
|
-
const
|
|
785
|
+
const cycle = false;
|
|
969
786
|
const currentBatch = ready;
|
|
970
787
|
for (let index = 0; index < currentBatch.length; index += parallelism) {
|
|
971
788
|
const chunk = currentBatch.slice(index, index + parallelism);
|
|
@@ -984,7 +801,7 @@ var runGraphExecution = async ({
|
|
|
984
801
|
}) || [];
|
|
985
802
|
const result = await executeSimulationNode({
|
|
986
803
|
node,
|
|
987
|
-
|
|
804
|
+
cycle,
|
|
988
805
|
inputPayload,
|
|
989
806
|
contextInputs,
|
|
990
807
|
executeNode,
|
|
@@ -1021,8 +838,9 @@ var runDependencySimulation = async ({
|
|
|
1021
838
|
};
|
|
1022
839
|
|
|
1023
840
|
// src/bundle.ts
|
|
1024
|
-
|
|
1025
|
-
var
|
|
841
|
+
import YAML4 from "yaml";
|
|
842
|
+
var BUNDLE_MIME = "application/vnd.circuitry.bundle+yaml";
|
|
843
|
+
var deepClone = (value) => structuredClone(value);
|
|
1026
844
|
var createBundleFromSelection = (elements, selectedIds) => {
|
|
1027
845
|
const selected = elements.filter((el) => selectedIds[el.id]);
|
|
1028
846
|
if (!selected.length) {
|
|
@@ -1046,8 +864,9 @@ var createBundleFromSelection = (elements, selectedIds) => {
|
|
|
1046
864
|
elements: [...dedup.values()]
|
|
1047
865
|
};
|
|
1048
866
|
};
|
|
867
|
+
var stringifyBundle = (bundle) => YAML4.stringify(bundle);
|
|
1049
868
|
var parseBundleText = (text) => {
|
|
1050
|
-
const parsed =
|
|
869
|
+
const parsed = YAML4.parse(text);
|
|
1051
870
|
if (parsed.version !== 1 || !Array.isArray(parsed.elements)) {
|
|
1052
871
|
throw new Error("Invalid Circuitry bundle format");
|
|
1053
872
|
}
|
|
@@ -1159,9 +978,9 @@ var createCircuitryRunGraphTool = (host) => ({
|
|
|
1159
978
|
});
|
|
1160
979
|
var createCircuitryValidateGraphTool = (host) => ({
|
|
1161
980
|
name: "circuitry_validate_graph",
|
|
1162
|
-
description: "Validate Circuitry YAML
|
|
981
|
+
description: "Validate Circuitry YAML graph text. Returns structured validation issues.",
|
|
1163
982
|
parameters: z.object({
|
|
1164
|
-
text: z.string().describe("YAML
|
|
983
|
+
text: z.string().describe("YAML graph text to validate."),
|
|
1165
984
|
filename: z.string().optional().describe("Optional filename context."),
|
|
1166
985
|
standard: standardSchema
|
|
1167
986
|
}),
|
|
@@ -1202,16 +1021,17 @@ export {
|
|
|
1202
1021
|
normalizeCircuitryGraph,
|
|
1203
1022
|
parseAgentPresetMarkdown,
|
|
1204
1023
|
parseBundleText,
|
|
1205
|
-
parseCircuitryJson,
|
|
1206
1024
|
parseCircuitryText,
|
|
1207
1025
|
parseCircuitryYaml,
|
|
1026
|
+
parseYamlData,
|
|
1208
1027
|
runCircuitryGraphExecution,
|
|
1209
1028
|
runDependencySimulation,
|
|
1210
1029
|
runGraphExecution,
|
|
1211
1030
|
standardSchema,
|
|
1212
|
-
|
|
1031
|
+
stringifyBundle,
|
|
1213
1032
|
stringifyCircuitryText,
|
|
1214
1033
|
stringifyCircuitryYaml,
|
|
1034
|
+
stringifyYamlData,
|
|
1215
1035
|
validateCircuitryExecutionGraphWithStandard,
|
|
1216
1036
|
validateCircuitryGraph,
|
|
1217
1037
|
validateCircuitryGraphWithStandard
|