@darkhorseprojects/circuitry 0.3.1 → 0.3.3
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 +105 -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 +3 -3
package/dist/node.js
CHANGED
|
@@ -5,35 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
|
|
7
7
|
// src/yaml.ts
|
|
8
|
-
import
|
|
8
|
+
import YAML2 from "yaml";
|
|
9
9
|
|
|
10
10
|
// src/graph.ts
|
|
11
|
-
|
|
12
|
-
var
|
|
13
|
-
if (typeof value === "string") return value;
|
|
14
|
-
if (value === void 0) return "";
|
|
15
|
-
return JSON.stringify(value, null, 2);
|
|
16
|
-
};
|
|
17
|
-
var applyCircuitryRuntimeInputs = (graph, inputs = {}) => {
|
|
18
|
-
const entries = Object.entries(inputs);
|
|
19
|
-
if (entries.length === 0) return graph;
|
|
20
|
-
const next = {
|
|
21
|
-
...graph,
|
|
22
|
-
resources: graph.resources ? { ...graph.resources } : graph.resources
|
|
23
|
-
};
|
|
24
|
-
for (const [id, value] of entries) {
|
|
25
|
-
const text = runtimeInputToText(value);
|
|
26
|
-
const resource = next.resources?.[id];
|
|
27
|
-
if (resource) {
|
|
28
|
-
if (resource.type !== "text")
|
|
29
|
-
throw new Error(`Runtime input ${id} targets non-text resource: ${resource.type}`);
|
|
30
|
-
next.resources[id] = { ...resource, value: text };
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
throw new Error(`Runtime input does not match a graph text resource: ${id}`);
|
|
34
|
-
}
|
|
35
|
-
return next;
|
|
36
|
-
};
|
|
11
|
+
import YAML from "yaml";
|
|
12
|
+
var CIRCUITRY_SPEC_VERSION = "0.3.2";
|
|
37
13
|
var DEFAULT_CIRCUITRY_VALIDATION_RULES = [
|
|
38
14
|
"no-self-loops",
|
|
39
15
|
"no-unknown-edge-endpoints",
|
|
@@ -46,25 +22,30 @@ var DEFAULT_CIRCUITRY_VALIDATION_STANDARD = {
|
|
|
46
22
|
rules: DEFAULT_CIRCUITRY_VALIDATION_RULES,
|
|
47
23
|
executableKinds: ["agent", "tool", "output"]
|
|
48
24
|
};
|
|
49
|
-
var
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
);
|
|
53
|
-
|
|
25
|
+
var runtimeInputToText = (value) => {
|
|
26
|
+
if (typeof value === "string") return value;
|
|
27
|
+
if (value === void 0) return "";
|
|
28
|
+
return YAML.stringify(value).trimEnd();
|
|
29
|
+
};
|
|
30
|
+
var applyCircuitryRuntimeInputs = (graph, inputs = {}) => {
|
|
31
|
+
const next = { ...graph, resources: graph.resources ? { ...graph.resources } : graph.resources };
|
|
32
|
+
for (const [id, value] of Object.entries(inputs)) {
|
|
33
|
+
const resource = next.resources?.[id];
|
|
34
|
+
if (!resource) throw new Error(`unknown arg: ${id}`);
|
|
35
|
+
if (resource.type !== "text") throw new Error(`arg is not text: ${id}`);
|
|
36
|
+
next.resources[id] = { ...resource, value: runtimeInputToText(value) };
|
|
37
|
+
}
|
|
38
|
+
return next;
|
|
54
39
|
};
|
|
40
|
+
var ruleName = (rule) => typeof rule === "string" ? rule : rule.rule;
|
|
41
|
+
var executableKindsFromRules = (rules) => rules.find((rule) => typeof rule !== "string" && rule.rule === "require-executable-inputs")?.executableKinds;
|
|
55
42
|
var createCircuitryValidationStandard = (standard = {}, graph) => {
|
|
56
|
-
const rules = [
|
|
57
|
-
...DEFAULT_CIRCUITRY_VALIDATION_RULES,
|
|
58
|
-
...graph?.validation?.rules || [],
|
|
59
|
-
...standard.rules || []
|
|
60
|
-
];
|
|
43
|
+
const rules = [...DEFAULT_CIRCUITRY_VALIDATION_RULES, ...graph?.validation?.rules || [], ...standard.rules || []];
|
|
61
44
|
return {
|
|
62
45
|
version: standard.version || DEFAULT_CIRCUITRY_VALIDATION_STANDARD.version,
|
|
63
46
|
requireSpecVersion: standard.requireSpecVersion ?? DEFAULT_CIRCUITRY_VALIDATION_STANDARD.requireSpecVersion,
|
|
64
47
|
rules,
|
|
65
|
-
executableKinds: standard.executableKinds || executableKindsFromRules(rules) || [
|
|
66
|
-
...DEFAULT_CIRCUITRY_VALIDATION_STANDARD.executableKinds
|
|
67
|
-
]
|
|
48
|
+
executableKinds: standard.executableKinds || executableKindsFromRules(rules) || [...DEFAULT_CIRCUITRY_VALIDATION_STANDARD.executableKinds]
|
|
68
49
|
};
|
|
69
50
|
};
|
|
70
51
|
var expandResources = (resources) => {
|
|
@@ -72,12 +53,7 @@ var expandResources = (resources) => {
|
|
|
72
53
|
const edges = [];
|
|
73
54
|
for (const [id, resource] of Object.entries(resources)) {
|
|
74
55
|
if (resource.type === "text") {
|
|
75
|
-
nodes.push({
|
|
76
|
-
id,
|
|
77
|
-
kind: "input",
|
|
78
|
-
label: resource.label || id,
|
|
79
|
-
input: { type: "text", value: resource.value }
|
|
80
|
-
});
|
|
56
|
+
nodes.push({ id, kind: "input", label: resource.label || id, input: { type: "text", value: resource.value } });
|
|
81
57
|
} else if (resource.type === "agent") {
|
|
82
58
|
const agent = resource;
|
|
83
59
|
nodes.push({
|
|
@@ -87,7 +63,6 @@ var expandResources = (resources) => {
|
|
|
87
63
|
agent: {
|
|
88
64
|
identity: agent.identity || agent.label || id,
|
|
89
65
|
model: agent.model,
|
|
90
|
-
thinkingLevel: agent.thinkingLevel,
|
|
91
66
|
tools: agent.tools,
|
|
92
67
|
instructions: agent.instructions,
|
|
93
68
|
personality: agent.personality,
|
|
@@ -96,286 +71,133 @@ var expandResources = (resources) => {
|
|
|
96
71
|
skills: agent.skills,
|
|
97
72
|
expect: agent.expect
|
|
98
73
|
});
|
|
99
|
-
for (const
|
|
100
|
-
edges.push({ from: inputRef, to: id, kind: "dependency" });
|
|
101
|
-
}
|
|
74
|
+
for (const input of agent.inputs || []) edges.push({ from: input, to: id, kind: "dependency" });
|
|
102
75
|
} else if (resource.type === "tool") {
|
|
103
76
|
const tool = resource;
|
|
104
|
-
nodes.push({
|
|
105
|
-
|
|
106
|
-
kind: "tool",
|
|
107
|
-
label: tool.label || id,
|
|
108
|
-
agent: { instructions: tool.instructions }
|
|
109
|
-
});
|
|
110
|
-
for (const inputRef of tool.inputs || []) {
|
|
111
|
-
edges.push({ from: inputRef, to: id, kind: "dependency" });
|
|
112
|
-
}
|
|
77
|
+
nodes.push({ id, kind: "tool", label: tool.label || id, agent: { instructions: tool.instructions } });
|
|
78
|
+
for (const input of tool.inputs || []) edges.push({ from: input, to: id, kind: "dependency" });
|
|
113
79
|
} else {
|
|
114
80
|
const input = resource;
|
|
115
|
-
nodes.push({
|
|
116
|
-
id,
|
|
117
|
-
kind: "input",
|
|
118
|
-
label: input.label || id,
|
|
119
|
-
input: {
|
|
120
|
-
type: input.type,
|
|
121
|
-
value: input.value,
|
|
122
|
-
uri: input.uri || input.path,
|
|
123
|
-
mimeType: input.mimeType,
|
|
124
|
-
data: input.data
|
|
125
|
-
}
|
|
126
|
-
});
|
|
81
|
+
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 } });
|
|
127
82
|
}
|
|
128
83
|
}
|
|
129
84
|
return { nodes, edges };
|
|
130
85
|
};
|
|
131
86
|
var normalizeCircuitryGraph = (graph) => {
|
|
132
|
-
if (!graph.resources
|
|
133
|
-
|
|
134
|
-
return { ...graph, nodes, edges };
|
|
87
|
+
if (!graph.resources) return { ...graph };
|
|
88
|
+
return { ...graph, ...expandResources(graph.resources) };
|
|
135
89
|
};
|
|
136
|
-
var
|
|
137
|
-
|
|
138
|
-
const resolvedStandard = createCircuitryValidationStandard(
|
|
139
|
-
standard,
|
|
140
|
-
graph && typeof graph === "object" && !Array.isArray(graph) ? graph : void 0
|
|
141
|
-
);
|
|
90
|
+
var validateCircuitryGraphInternal = (graph, standard = {}, source) => {
|
|
91
|
+
const resolved = createCircuitryValidationStandard(standard, graph && typeof graph === "object" && !Array.isArray(graph) ? graph : void 0);
|
|
142
92
|
const errors = [];
|
|
143
|
-
const
|
|
144
|
-
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (options.sourceFormat) {
|
|
159
|
-
if (!graphObject.resources || Object.keys(graphObject.resources).length === 0) {
|
|
160
|
-
addError("missing_resources", "Circuitry graphs must use a resources: section", ["resources"]);
|
|
161
|
-
}
|
|
162
|
-
if (graphObject.agents && Object.keys(graphObject.agents).length > 0) {
|
|
163
|
-
addError("forbidden_agents", "Circuitry graph files must not use top-level agents:; use resources:", ["agents"]);
|
|
164
|
-
}
|
|
165
|
-
if (graphObject.inputs && Object.keys(graphObject.inputs).length > 0) {
|
|
166
|
-
addError("forbidden_inputs", "Circuitry graph files use args: for runtime arguments and resource inputs for edges", ["inputs"]);
|
|
167
|
-
}
|
|
168
|
-
if (graphObject.nodes && graphObject.nodes.length > 0) {
|
|
169
|
-
addError("forbidden_nodes", "Circuitry graph files must not use top-level nodes:; use resources:", ["nodes"]);
|
|
170
|
-
}
|
|
171
|
-
if (graphObject.edges && graphObject.edges.length > 0) {
|
|
172
|
-
addError("forbidden_edges", "Circuitry graph files must not use top-level edges:; use resource inputs:", ["edges"]);
|
|
173
|
-
}
|
|
174
|
-
if (graphObject.links && graphObject.links.length > 0) {
|
|
175
|
-
addError("forbidden_links", "Circuitry graph files use imports: for multi-file graphs", ["links"]);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
const normalized = normalizeCircuitryGraph(graphObject);
|
|
93
|
+
const issue = (code, message = code, path2) => errors.push({ code, message, ...path2 ? { path: path2 } : {} });
|
|
94
|
+
if (!graph || typeof graph !== "object" || Array.isArray(graph)) return { ok: false, errors: [{ code: "invalid_graph", message: "invalid graph" }], standard: resolved };
|
|
95
|
+
const g = graph;
|
|
96
|
+
if (resolved.requireSpecVersion && g.circuitry !== resolved.version) issue("invalid_version", "invalid version", ["circuitry"]);
|
|
97
|
+
if (source) {
|
|
98
|
+
const raw = g;
|
|
99
|
+
if (!g.resources || Object.keys(g.resources).length === 0) issue("missing_resources", "missing resources", ["resources"]);
|
|
100
|
+
if (raw.agents) issue("forbidden_agents", "forbidden agents", ["agents"]);
|
|
101
|
+
if (raw.inputs) issue("forbidden_inputs", "forbidden inputs", ["inputs"]);
|
|
102
|
+
if (g.nodes?.length) issue("forbidden_nodes", "forbidden nodes", ["nodes"]);
|
|
103
|
+
if (g.edges?.length) issue("forbidden_edges", "forbidden edges", ["edges"]);
|
|
104
|
+
if (raw.links) issue("forbidden_links", "forbidden links", ["links"]);
|
|
105
|
+
}
|
|
106
|
+
const rules = new Set(resolved.rules.map(ruleName));
|
|
107
|
+
const normalized = normalizeCircuitryGraph(g);
|
|
179
108
|
const ids = /* @__PURE__ */ new Set();
|
|
180
109
|
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
181
110
|
if (!node.id) {
|
|
182
|
-
|
|
111
|
+
issue("missing_node_id", "missing node id", ["nodes", index, "id"]);
|
|
183
112
|
continue;
|
|
184
113
|
}
|
|
185
|
-
if (ids.has(node.id))
|
|
186
|
-
addError("duplicate_node_id", `Duplicate node id: ${node.id}`, ["nodes", index, "id"]);
|
|
187
|
-
}
|
|
114
|
+
if (ids.has(node.id)) issue("duplicate_node_id", "duplicate node id", ["nodes", index, "id"]);
|
|
188
115
|
ids.add(node.id);
|
|
189
|
-
if (!node.kind)
|
|
190
|
-
addError("missing_node_kind", `Node ${node.id} is missing kind`, ["nodes", index, "kind"]);
|
|
191
|
-
}
|
|
116
|
+
if (!node.kind) issue("missing_node_kind", "missing node kind", ["nodes", index, "kind"]);
|
|
192
117
|
}
|
|
193
118
|
const adjacency = /* @__PURE__ */ new Map();
|
|
194
|
-
const
|
|
119
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
195
120
|
for (const id of ids) {
|
|
196
121
|
adjacency.set(id, []);
|
|
197
|
-
|
|
122
|
+
incoming.set(id, 0);
|
|
198
123
|
}
|
|
199
124
|
for (const [index, edge] of (normalized.edges || []).entries()) {
|
|
200
125
|
if (!edge.from || !edge.to) {
|
|
201
|
-
|
|
126
|
+
issue("missing_edge_endpoint", "missing edge endpoint", ["edges", index]);
|
|
202
127
|
continue;
|
|
203
128
|
}
|
|
204
|
-
if (
|
|
205
|
-
|
|
129
|
+
if (rules.has("no-self-loops") && edge.from === edge.to) {
|
|
130
|
+
issue("self_loop", "self loop", ["edges", index]);
|
|
206
131
|
continue;
|
|
207
132
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
addError("unknown_edge_target", `Edge references unknown target: ${edge.to}`, ["edges", index, "to"]);
|
|
133
|
+
const ok = ids.has(edge.from) && ids.has(edge.to);
|
|
134
|
+
if (rules.has("no-unknown-edge-endpoints")) {
|
|
135
|
+
if (!ids.has(edge.from)) issue("unknown_edge_source", "unknown edge source", ["edges", index, "from"]);
|
|
136
|
+
if (!ids.has(edge.to)) issue("unknown_edge_target", "unknown edge target", ["edges", index, "to"]);
|
|
213
137
|
}
|
|
214
|
-
if (
|
|
138
|
+
if (ok) {
|
|
215
139
|
adjacency.get(edge.from).push(edge.to);
|
|
216
|
-
|
|
140
|
+
incoming.set(edge.to, (incoming.get(edge.to) || 0) + 1);
|
|
217
141
|
}
|
|
218
142
|
}
|
|
219
|
-
|
|
220
|
-
|
|
143
|
+
if (rules.has("require-executable-inputs")) {
|
|
144
|
+
const executable = new Set(resolved.executableKinds);
|
|
221
145
|
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (visited.has(id)) return;
|
|
238
|
-
visiting.add(id);
|
|
239
|
-
path2.push(id);
|
|
240
|
-
for (const next of adjacency.get(id) || []) visit(next);
|
|
241
|
-
path2.pop();
|
|
242
|
-
visiting.delete(id);
|
|
243
|
-
visited.add(id);
|
|
244
|
-
};
|
|
245
|
-
if (hasRule("no-cycles")) {
|
|
246
|
-
for (const id of ids) {
|
|
247
|
-
visit(id);
|
|
248
|
-
if (cycleMessage) {
|
|
249
|
-
addError("cycle", `Graph contains cycle: ${cycleMessage}`);
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
const additionalRules = rules.filter((rule) => {
|
|
255
|
-
if (typeof rule === "string") return false;
|
|
256
|
-
return rule.rule === "require-graph-field" || rule.rule === "require-node-field" || rule.rule === "require-edge-field" || rule.rule === "require-string" || rule.rule === "reject-string";
|
|
257
|
-
});
|
|
258
|
-
const fieldPath = (field) => field.split(".").filter(Boolean);
|
|
259
|
-
const getField = (value, field) => fieldPath(field).reduce((current, key) => {
|
|
260
|
-
if (!current || typeof current !== "object") return void 0;
|
|
261
|
-
return current[key];
|
|
262
|
-
}, value);
|
|
263
|
-
const isPresent = (value) => value !== void 0 && value !== null && value !== "";
|
|
264
|
-
for (const rule of additionalRules) {
|
|
265
|
-
if (rule.rule === "require-graph-field") {
|
|
266
|
-
if (!isPresent(getField(normalized, rule.field))) {
|
|
267
|
-
addError("missing_required_graph_field", `Graph is missing required field: ${rule.field}`, fieldPath(rule.field));
|
|
268
|
-
}
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
if (rule.rule === "require-node-field") {
|
|
272
|
-
const nodeKinds = new Set(rule.nodeKinds || []);
|
|
273
|
-
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
274
|
-
if (nodeKinds.size > 0 && !nodeKinds.has(node.kind)) continue;
|
|
275
|
-
if (!isPresent(getField(node, rule.field))) {
|
|
276
|
-
addError("missing_required_node_field", `Node ${node.id || "<missing id>"} is missing required field: ${rule.field}`, ["nodes", index, ...fieldPath(rule.field)]);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (rule.rule === "require-edge-field") {
|
|
282
|
-
const edgeKinds = new Set(rule.edgeKinds || []);
|
|
283
|
-
for (const [index, edge] of (normalized.edges || []).entries()) {
|
|
284
|
-
if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind))) continue;
|
|
285
|
-
if (!isPresent(getField(edge, rule.field))) {
|
|
286
|
-
addError("missing_required_edge_field", `Edge ${edge.id || index} is missing required field: ${rule.field}`, ["edges", index, ...fieldPath(rule.field)]);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
const checkString = (target, subject, id, match, path3) => {
|
|
292
|
-
const value = getField(subject, match.field);
|
|
293
|
-
const text = typeof value === "string" ? value : "";
|
|
294
|
-
const includes = text.includes(match.value);
|
|
295
|
-
if (rule.rule === "require-string" && !includes) {
|
|
296
|
-
addError("missing_required_string", `${target} ${id} field ${match.field} must include string: ${match.value}`, path3);
|
|
297
|
-
}
|
|
298
|
-
if (rule.rule === "reject-string" && includes) {
|
|
299
|
-
addError("rejected_string", `${target} ${id} field ${match.field} must not include string: ${match.value}`, path3);
|
|
300
|
-
}
|
|
146
|
+
if (executable.has(node.kind) && (incoming.get(node.id) || 0) === 0) issue("missing_executable_input", "missing executable input", ["nodes", index]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (rules.has("no-cycles")) {
|
|
150
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
151
|
+
const visited = /* @__PURE__ */ new Set();
|
|
152
|
+
const visit = (id) => {
|
|
153
|
+
if (visiting.has(id)) return true;
|
|
154
|
+
if (visited.has(id)) return false;
|
|
155
|
+
visiting.add(id);
|
|
156
|
+
for (const next of adjacency.get(id) || []) if (visit(next)) return true;
|
|
157
|
+
visiting.delete(id);
|
|
158
|
+
visited.add(id);
|
|
159
|
+
return false;
|
|
301
160
|
};
|
|
302
|
-
for (const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
306
|
-
if (nodeKinds.size > 0 && !nodeKinds.has(node.kind)) continue;
|
|
307
|
-
checkString("node", node, node.id || "<missing id>", match, ["nodes", index, ...fieldPath(match.field)]);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
for (const match of rule.edges || []) {
|
|
311
|
-
const edgeKinds = new Set(match.edgeKinds || []);
|
|
312
|
-
for (const [index, edge] of (normalized.edges || []).entries()) {
|
|
313
|
-
if (edgeKinds.size > 0 && (!edge.kind || !edgeKinds.has(edge.kind))) continue;
|
|
314
|
-
checkString("edge", edge, edge.id || String(index), match, ["edges", index, ...fieldPath(match.field)]);
|
|
315
|
-
}
|
|
161
|
+
for (const id of ids) if (visit(id)) {
|
|
162
|
+
issue("cycle", "cycle");
|
|
163
|
+
break;
|
|
316
164
|
}
|
|
317
165
|
}
|
|
318
|
-
return { ok: errors.length === 0, errors, standard:
|
|
319
|
-
};
|
|
320
|
-
var validateCircuitryGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, { sourceFormat: true });
|
|
321
|
-
var validateCircuitryExecutionGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, { sourceFormat: false });
|
|
322
|
-
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map(
|
|
323
|
-
(error) => error.message
|
|
324
|
-
);
|
|
325
|
-
var parseCircuitryJson = (text, standard = {}, options = {}) => {
|
|
326
|
-
let graph;
|
|
327
|
-
try {
|
|
328
|
-
graph = JSON.parse(text);
|
|
329
|
-
} catch (error) {
|
|
330
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
331
|
-
throw new Error(`Could not parse Circuitry JSON. Check commas, quotes, and braces.
|
|
332
|
-
${message}`);
|
|
333
|
-
}
|
|
334
|
-
if (options.validate === false) return graph;
|
|
335
|
-
const errors = validateCircuitryGraph(graph, standard);
|
|
336
|
-
if (errors.length) {
|
|
337
|
-
throw new Error(`Invalid Circuitry graph:
|
|
338
|
-
${errors.join("\n")}`);
|
|
339
|
-
}
|
|
340
|
-
return normalizeCircuitryGraph(graph);
|
|
341
|
-
};
|
|
342
|
-
var stringifyCircuitryJson = (graph) => {
|
|
343
|
-
return `${JSON.stringify(normalizeCircuitryGraph(graph), null, 2)}
|
|
344
|
-
`;
|
|
166
|
+
return { ok: errors.length === 0, errors, standard: resolved };
|
|
345
167
|
};
|
|
168
|
+
var validateCircuitryGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, true);
|
|
169
|
+
var validateCircuitryExecutionGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, false);
|
|
170
|
+
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map((error) => error.message);
|
|
346
171
|
|
|
347
172
|
// src/yaml.ts
|
|
348
|
-
var
|
|
349
|
-
let graph;
|
|
173
|
+
var parseYamlData = (text) => {
|
|
350
174
|
try {
|
|
351
|
-
|
|
175
|
+
return YAML2.parse(text);
|
|
352
176
|
} catch (error) {
|
|
353
177
|
const message = error instanceof Error ? error.message : String(error);
|
|
354
|
-
throw new Error(`Could not parse
|
|
178
|
+
throw new Error(`Could not parse YAML.
|
|
355
179
|
${message}`);
|
|
356
180
|
}
|
|
181
|
+
};
|
|
182
|
+
var stringifyYamlData = (value) => YAML2.stringify(value);
|
|
183
|
+
var parseCircuitryYaml = (text, standard = {}, options = {}) => {
|
|
184
|
+
const graph = parseYamlData(text);
|
|
357
185
|
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
358
|
-
throw new Error("
|
|
186
|
+
throw new Error("Circuitry YAML must be a graph object.");
|
|
359
187
|
}
|
|
360
|
-
if (options.validate
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
throw new Error(`Invalid Circuitry graph:
|
|
188
|
+
if (options.validate !== false) {
|
|
189
|
+
const errors = validateCircuitryGraph(graph, standard);
|
|
190
|
+
if (errors.length) throw new Error(`Invalid Circuitry graph:
|
|
364
191
|
${errors.join("\n")}`);
|
|
365
192
|
}
|
|
366
|
-
return
|
|
367
|
-
};
|
|
368
|
-
var stringifyCircuitryYaml = (graph) => {
|
|
369
|
-
return YAML.stringify(normalizeCircuitryGraph(graph));
|
|
370
|
-
};
|
|
371
|
-
var parseCircuitryText = (text, filename = "graph.circuitry.yaml", standard = {}, options = {}) => {
|
|
372
|
-
return filename.endsWith(".json") ? parseCircuitryJson(text, standard, options) : parseCircuitryYaml(text, standard, options);
|
|
373
|
-
};
|
|
374
|
-
var stringifyCircuitryText = (graph, filename = "graph.circuitry.yaml") => {
|
|
375
|
-
return filename.endsWith(".json") ? stringifyCircuitryJson(graph) : stringifyCircuitryYaml(graph);
|
|
193
|
+
return graph;
|
|
376
194
|
};
|
|
195
|
+
var stringifyCircuitryYaml = (graph) => YAML2.stringify(graph);
|
|
196
|
+
var parseCircuitryText = (text, standard = {}, options = {}) => parseCircuitryYaml(text, standard, options);
|
|
197
|
+
var stringifyCircuitryText = (graph) => stringifyCircuitryYaml(graph);
|
|
377
198
|
|
|
378
199
|
// src/scheduler.ts
|
|
200
|
+
import YAML3 from "yaml";
|
|
379
201
|
var buildCircuitryExecutionGraph = (graph) => {
|
|
380
202
|
const normalized = normalizeCircuitryGraph(graph);
|
|
381
203
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -415,7 +237,7 @@ var formatEdgeData = (data) => {
|
|
|
415
237
|
if (!data || Object.keys(data).length === 0) {
|
|
416
238
|
return "";
|
|
417
239
|
}
|
|
418
|
-
return ` data=${
|
|
240
|
+
return ` data=${YAML3.stringify(data).trimEnd()}`;
|
|
419
241
|
};
|
|
420
242
|
var describeEdge = (edge, direction) => {
|
|
421
243
|
const otherId = direction === "incoming" ? edge.from : edge.to;
|
|
@@ -460,17 +282,17 @@ ${contextSection}`
|
|
|
460
282
|
].filter(Boolean).join("\n\n");
|
|
461
283
|
};
|
|
462
284
|
var collectImages = (contextInputs) => contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
|
|
463
|
-
var
|
|
285
|
+
var stripYamlFence = (output) => {
|
|
464
286
|
const trimmed = output.trim();
|
|
465
|
-
const fenced = trimmed.match(/^```(?:
|
|
287
|
+
const fenced = trimmed.match(/^```(?:ya?ml)?\s*([\s\S]*?)\s*```$/i);
|
|
466
288
|
return fenced ? fenced[1].trim() : trimmed;
|
|
467
289
|
};
|
|
468
290
|
var parseExpectedOutput = (nodeId, output) => {
|
|
469
291
|
try {
|
|
470
|
-
return
|
|
292
|
+
return YAML3.parse(stripYamlFence(output));
|
|
471
293
|
} catch (error) {
|
|
472
294
|
const detail = error instanceof Error ? error.message : String(error);
|
|
473
|
-
throw new Error(`Node ${nodeId} output does not match expect: output is not valid
|
|
295
|
+
throw new Error(`Node ${nodeId} output does not match expect: output is not valid YAML (${detail})`);
|
|
474
296
|
}
|
|
475
297
|
};
|
|
476
298
|
var isFieldObject = (schema) => !!schema && typeof schema === "object" && !Array.isArray(schema) && typeof schema.type === "string";
|
|
@@ -553,7 +375,7 @@ ${errors.join("\n")}`);
|
|
|
553
375
|
var executeCircuitryNode = async ({
|
|
554
376
|
graph,
|
|
555
377
|
item,
|
|
556
|
-
|
|
378
|
+
cycle,
|
|
557
379
|
inputPayload,
|
|
558
380
|
contextInputs,
|
|
559
381
|
defaultModel,
|
|
@@ -561,13 +383,13 @@ var executeCircuitryNode = async ({
|
|
|
561
383
|
onNodeStart,
|
|
562
384
|
onNodeComplete
|
|
563
385
|
}) => {
|
|
564
|
-
onNodeStart?.(item.id,
|
|
386
|
+
onNodeStart?.(item.id, cycle);
|
|
565
387
|
if (item.node.kind === "output") {
|
|
566
388
|
const output = inputPayload.map((input) => input.output).join("\n\n");
|
|
567
389
|
onNodeComplete?.(item.id, { output });
|
|
568
390
|
return {
|
|
569
391
|
nodeId: item.id,
|
|
570
|
-
|
|
392
|
+
cycle,
|
|
571
393
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
572
394
|
output
|
|
573
395
|
};
|
|
@@ -579,7 +401,6 @@ var executeCircuitryNode = async ({
|
|
|
579
401
|
nodeId: item.id,
|
|
580
402
|
model: (agent.model === "inherit" ? void 0 : agent.model) || (defaultModel === "inherit" ? void 0 : defaultModel) || "inherit",
|
|
581
403
|
tools: agent.tools || [],
|
|
582
|
-
thinkingLevel: agent.thinkingLevel || "off",
|
|
583
404
|
personality: agent.personality || "",
|
|
584
405
|
instructions: agent.instructions || "",
|
|
585
406
|
context: agent.context || "",
|
|
@@ -593,7 +414,7 @@ var executeCircuitryNode = async ({
|
|
|
593
414
|
onNodeComplete?.(item.id, { error: message });
|
|
594
415
|
return {
|
|
595
416
|
nodeId: item.id,
|
|
596
|
-
|
|
417
|
+
cycle,
|
|
597
418
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
598
419
|
output: "",
|
|
599
420
|
error: message
|
|
@@ -603,7 +424,7 @@ var executeCircuitryNode = async ({
|
|
|
603
424
|
onNodeComplete?.(item.id, { output: result.output });
|
|
604
425
|
return {
|
|
605
426
|
nodeId: item.id,
|
|
606
|
-
|
|
427
|
+
cycle,
|
|
607
428
|
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
608
429
|
output: result.output
|
|
609
430
|
};
|
|
@@ -622,7 +443,7 @@ var runCircuitryGraphExecution = async ({
|
|
|
622
443
|
const parallelism = Math.max(1, Math.floor(maxParallelRuns));
|
|
623
444
|
const defaultModel = graph.runtime?.model;
|
|
624
445
|
const runOne = async (nodeId, outputs2) => {
|
|
625
|
-
const
|
|
446
|
+
const cycle = false;
|
|
626
447
|
const item = executionGraph.nodes.get(nodeId);
|
|
627
448
|
const inputPayload = item.inputNodeIds.filter((sourceId) => outputs2.has(sourceId)).map((sourceId) => ({
|
|
628
449
|
nodeId: sourceId,
|
|
@@ -636,7 +457,7 @@ var runCircuitryGraphExecution = async ({
|
|
|
636
457
|
return executeCircuitryNode({
|
|
637
458
|
graph,
|
|
638
459
|
item,
|
|
639
|
-
|
|
460
|
+
cycle,
|
|
640
461
|
inputPayload,
|
|
641
462
|
contextInputs,
|
|
642
463
|
defaultModel,
|
|
@@ -722,7 +543,8 @@ var loadPiSDK = async () => {
|
|
|
722
543
|
return await import(modulePath);
|
|
723
544
|
}
|
|
724
545
|
}
|
|
725
|
-
|
|
546
|
+
const packageName = "@earendil-works/pi-coding-agent";
|
|
547
|
+
return await import(packageName);
|
|
726
548
|
};
|
|
727
549
|
var extractAssistantText = (message) => {
|
|
728
550
|
if (!message || message.role !== "assistant") return "";
|
|
@@ -761,8 +583,7 @@ var runNodeWithPiSDK = async ({
|
|
|
761
583
|
model,
|
|
762
584
|
prompt,
|
|
763
585
|
images = [],
|
|
764
|
-
tools = []
|
|
765
|
-
thinkingLevel = "off"
|
|
586
|
+
tools = []
|
|
766
587
|
}) => {
|
|
767
588
|
let sdk;
|
|
768
589
|
try {
|
|
@@ -786,7 +607,6 @@ var runNodeWithPiSDK = async ({
|
|
|
786
607
|
const selectedModel = requestedModel && requestedModel !== "inherit" ? findRequestedModel(modelRegistry, await modelRegistry.getAvailable(), requestedModel) : void 0;
|
|
787
608
|
const { session } = await createAgentSession({
|
|
788
609
|
...selectedModel ? { model: selectedModel } : {},
|
|
789
|
-
thinkingLevel,
|
|
790
610
|
sessionManager: SessionManager.inMemory(),
|
|
791
611
|
authStorage,
|
|
792
612
|
modelRegistry,
|
|
@@ -874,7 +694,7 @@ ${validation.errors.map((e) => e.message).join("\n")}`);
|
|
|
874
694
|
var loadCircuitryGraphFile = async (filename = defaultFilename, standard, stack = []) => {
|
|
875
695
|
const graphFile = path.resolve(process.cwd(), filename);
|
|
876
696
|
const text = await readFile(graphFile, "utf8");
|
|
877
|
-
const parsed = parseCircuitryText(text,
|
|
697
|
+
const parsed = parseCircuitryText(text, standard, { validate: false });
|
|
878
698
|
return resolveCircuitryGraph(parsed, graphFile, text, standard, stack);
|
|
879
699
|
};
|
|
880
700
|
var NodeCircuitryHost = class {
|
|
@@ -885,7 +705,7 @@ var NodeCircuitryHost = class {
|
|
|
885
705
|
);
|
|
886
706
|
}
|
|
887
707
|
runFileFor(filename) {
|
|
888
|
-
return `${filename}.run.
|
|
708
|
+
return `${filename}.run.yaml`;
|
|
889
709
|
}
|
|
890
710
|
async exists(filename) {
|
|
891
711
|
try {
|
|
@@ -902,13 +722,9 @@ var NodeCircuitryHost = class {
|
|
|
902
722
|
path: [filename]
|
|
903
723
|
};
|
|
904
724
|
}
|
|
905
|
-
|
|
725
|
+
parseSourceGraph(text, filename, standard) {
|
|
906
726
|
try {
|
|
907
|
-
return {
|
|
908
|
-
graph: parseCircuitryText(text || "", filename, standard, {
|
|
909
|
-
validate: false
|
|
910
|
-
})
|
|
911
|
-
};
|
|
727
|
+
return { graph: parseCircuitryText(text || "", standard, { validate: false }) };
|
|
912
728
|
} catch (error) {
|
|
913
729
|
return { error: this.parseIssue(error, filename) };
|
|
914
730
|
}
|
|
@@ -917,7 +733,7 @@ var NodeCircuitryHost = class {
|
|
|
917
733
|
try {
|
|
918
734
|
const graphFile = path.resolve(process.cwd(), filename);
|
|
919
735
|
const source = text || "";
|
|
920
|
-
const parsed = parseCircuitryText(source,
|
|
736
|
+
const parsed = parseCircuitryText(source, standard, { validate: false });
|
|
921
737
|
return { graph: (await resolveCircuitryGraph(parsed, graphFile, source, standard)).graph };
|
|
922
738
|
} catch (error) {
|
|
923
739
|
return { error: this.parseIssue(error, filename) };
|
|
@@ -930,14 +746,14 @@ var NodeCircuitryHost = class {
|
|
|
930
746
|
async readGraph(input = {}) {
|
|
931
747
|
const { graphFile, text, graph } = await this.readGraphFile(input.filename);
|
|
932
748
|
const lastRunFile = this.runFileFor(graphFile);
|
|
933
|
-
const lastRun = await this.exists(lastRunFile) ?
|
|
749
|
+
const lastRun = await this.exists(lastRunFile) ? parseYamlData(await readFile(lastRunFile, "utf8")) : void 0;
|
|
934
750
|
return { filename: graphFile, graph, text, lastRun };
|
|
935
751
|
}
|
|
936
752
|
async validateGraph(input) {
|
|
937
753
|
if (input.graph)
|
|
938
754
|
return validateCircuitryGraphWithStandard(input.graph, input.standard);
|
|
939
755
|
const filename = input.filename || defaultFilename;
|
|
940
|
-
const parsed =
|
|
756
|
+
const parsed = this.parseSourceGraph(input.text, filename, input.standard);
|
|
941
757
|
if (!("graph" in parsed)) {
|
|
942
758
|
return {
|
|
943
759
|
ok: false,
|
|
@@ -949,11 +765,7 @@ var NodeCircuitryHost = class {
|
|
|
949
765
|
}
|
|
950
766
|
async writeGraph(input) {
|
|
951
767
|
const graphFile = this.resolveGraphFile(input.filename);
|
|
952
|
-
const parsed = input.graph ? { graph: input.graph } :
|
|
953
|
-
input.text,
|
|
954
|
-
input.filename || graphFile,
|
|
955
|
-
input.standard
|
|
956
|
-
);
|
|
768
|
+
const parsed = input.graph ? { graph: input.graph } : this.parseSourceGraph(input.text, input.filename || graphFile, input.standard);
|
|
957
769
|
if (!("graph" in parsed))
|
|
958
770
|
throw new Error(`Invalid Circuitry graph:
|
|
959
771
|
${parsed.error?.message}`);
|
|
@@ -969,7 +781,7 @@ ${validation.errors.map((e) => e.message).join("\n")}`
|
|
|
969
781
|
);
|
|
970
782
|
await writeFile(
|
|
971
783
|
graphFile,
|
|
972
|
-
stringifyCircuitryText(graph
|
|
784
|
+
stringifyCircuitryText(graph),
|
|
973
785
|
"utf8"
|
|
974
786
|
);
|
|
975
787
|
return {
|
|
@@ -1008,7 +820,7 @@ ${validation.errors.map((e) => e.message).join("\n")}`
|
|
|
1008
820
|
if (source !== "text")
|
|
1009
821
|
await writeFile(
|
|
1010
822
|
this.runFileFor(graphFile),
|
|
1011
|
-
|
|
823
|
+
stringifyYamlData(result),
|
|
1012
824
|
"utf8"
|
|
1013
825
|
);
|
|
1014
826
|
return result;
|