@tangle-network/agent-runtime 0.167.0 → 0.169.0
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/graph.d.ts +115 -27
- package/dist/graph.js +188 -2
- package/dist/graph.js.map +1 -1
- package/dist/testing.js +8 -8
- package/package.json +1 -1
package/dist/graph.d.ts
CHANGED
|
@@ -2,28 +2,6 @@ import { $l as MakeWorkerAgent, Gt as AgentGraph, If as DeliverableSpec, Qt as R
|
|
|
2
2
|
import { H as SpawnJournal, K as Spend, V as SpawnEvent, i as Budget, j as ResultBlobStore, t as Agent, z as Scope } from "./types-DjVO-p7R.js";
|
|
3
3
|
import { o as ExecutorConfig } from "./runtime-B0HZbuEv.js";
|
|
4
4
|
import { AgentProfile } from "@tangle-network/agent-interface";
|
|
5
|
-
//#region src/runtime/graph/condition.d.ts
|
|
6
|
-
/** The leaf comparison operators; `exists`/`truthy` are unary, the rest compare against `value`. */
|
|
7
|
-
declare const CONDITION_OPS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "in", "contains", "exists", "truthy"];
|
|
8
|
-
type ConditionOp = (typeof CONDITION_OPS)[number];
|
|
9
|
-
interface ConditionLeaf {
|
|
10
|
-
/** Dotted path with `[N]` indexing into the guard context, e.g. `out.findings[0].severity`. */
|
|
11
|
-
readonly path: string;
|
|
12
|
-
readonly op: ConditionOp;
|
|
13
|
-
readonly value?: unknown;
|
|
14
|
-
}
|
|
15
|
-
type Condition = ConditionLeaf | {
|
|
16
|
-
readonly all: ReadonlyArray<Condition>;
|
|
17
|
-
} | {
|
|
18
|
-
readonly any: ReadonlyArray<Condition>;
|
|
19
|
-
} | {
|
|
20
|
-
readonly not: Condition;
|
|
21
|
-
};
|
|
22
|
-
/** Validate shape, bounds, and per-leaf path/operator rules; returns the input for chaining. */
|
|
23
|
-
declare function validateCondition(raw: unknown, context: string): Condition;
|
|
24
|
-
/** Walk a validated condition over a context to a boolean. Never throws on data shape. */
|
|
25
|
-
declare function evaluateCondition(condition: Condition, context: unknown): boolean;
|
|
26
|
-
//#endregion
|
|
27
5
|
//#region src/runtime/graph/registry.d.ts
|
|
28
6
|
/**
|
|
29
7
|
* `Registry<T>` — the ONE name→thing shape for the graph engine.
|
|
@@ -163,6 +141,17 @@ interface NodeKind<Config = unknown, Effects extends ReadonlyArray<EffectName> =
|
|
|
163
141
|
readonly host?: GraphHost;
|
|
164
142
|
}) => Agent<unknown, unknown>;
|
|
165
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* A kind of ANY config shape — what a registry holds and what every engine signature accepts.
|
|
146
|
+
*
|
|
147
|
+
* `NodeKind<Config>` puts `Config` in a parameter position (`run({ config })`), so it is
|
|
148
|
+
* contravariant: an array of differently-configured kinds is not assignable to
|
|
149
|
+
* `ReadonlyArray<NodeKind<unknown>>`, and every caller composing a heterogeneous kind set would
|
|
150
|
+
* need a cast. That cost belongs here, once, not at each consumer: a registry is heterogeneous by
|
|
151
|
+
* definition, and each kind validates its own config at its own boundary through
|
|
152
|
+
* `validateConfig`, which is where the type is actually enforced.
|
|
153
|
+
*/
|
|
154
|
+
type AnyNodeKind = NodeKind<any, ReadonlyArray<EffectName>>;
|
|
166
155
|
/** Per-node flags a graph author sets; they are node properties, not kinds (agent-runtime#970). */
|
|
167
156
|
interface NodeFlags {
|
|
168
157
|
/** An oracle — a judge, grader, auditor, trace analyst — may be bound only by an `analyzes`
|
|
@@ -176,7 +165,7 @@ interface NodeFlags {
|
|
|
176
165
|
}
|
|
177
166
|
/** Validate a kind declaration at registration — so a malformed kind is refused by name once,
|
|
178
167
|
* not at the first node that uses it. */
|
|
179
|
-
declare function validateNodeKind(kind:
|
|
168
|
+
declare function validateNodeKind(kind: AnyNodeKind, context?: string): AnyNodeKind;
|
|
180
169
|
/** The handle a graph writes to name this kind. */
|
|
181
170
|
declare function kindHandle(kind: Pick<NodeKind, 'id' | 'version'>): RegistryHandle;
|
|
182
171
|
/**
|
|
@@ -186,6 +175,97 @@ declare function kindHandle(kind: Pick<NodeKind, 'id' | 'version'>): RegistryHan
|
|
|
186
175
|
*/
|
|
187
176
|
declare function narrowEffects<Effects extends ReadonlyArray<EffectName>>(declared: Effects, provided: Readonly<Record<string, unknown>>, context: string): EffectContext<Effects>;
|
|
188
177
|
//#endregion
|
|
178
|
+
//#region src/runtime/graph/codemode.d.ts
|
|
179
|
+
/** One operation the authored program may call, and the line the model is shown about it. */
|
|
180
|
+
interface CodeOperation {
|
|
181
|
+
/** Identifier the program calls, e.g. `search`. Must be a valid JS identifier. */
|
|
182
|
+
readonly name: string;
|
|
183
|
+
/** The signature and meaning, as the model sees it: `search(query: string): Promise<Hit[]>`. */
|
|
184
|
+
readonly signature: string;
|
|
185
|
+
readonly description: string;
|
|
186
|
+
/** What the call actually does. A spend it reports is added to the node's settlement. */
|
|
187
|
+
readonly call: (...args: ReadonlyArray<unknown>) => Promise<CodeOperationResult> | CodeOperationResult;
|
|
188
|
+
}
|
|
189
|
+
/** An operation's answer. `spend` is optional; an operation that costs nothing omits it. */
|
|
190
|
+
interface CodeOperationResult {
|
|
191
|
+
readonly value: unknown;
|
|
192
|
+
readonly spend?: Spend;
|
|
193
|
+
}
|
|
194
|
+
/** What a host's `codeRunner` effect must do: run authored source with `api` in scope, and answer
|
|
195
|
+
* what it returned. The host decides WHERE that happens — in process, or jailed. */
|
|
196
|
+
interface CodeRunner {
|
|
197
|
+
run(args: {
|
|
198
|
+
readonly code: string;
|
|
199
|
+
readonly api: Readonly<Record<string, (...args: ReadonlyArray<unknown>) => Promise<unknown>>>;
|
|
200
|
+
readonly signal: AbortSignal;
|
|
201
|
+
}): Promise<unknown>;
|
|
202
|
+
}
|
|
203
|
+
/** What a host's `model` effect must do: answer one prompt with text. */
|
|
204
|
+
interface CodeAuthor {
|
|
205
|
+
complete(args: {
|
|
206
|
+
readonly prompt: string;
|
|
207
|
+
readonly profile: AgentProfile;
|
|
208
|
+
readonly signal: AbortSignal;
|
|
209
|
+
}): Promise<{
|
|
210
|
+
readonly text: string;
|
|
211
|
+
readonly spend?: Spend;
|
|
212
|
+
}>;
|
|
213
|
+
}
|
|
214
|
+
interface CodeModeConfig {
|
|
215
|
+
readonly operations: ReadonlyArray<CodeOperation>;
|
|
216
|
+
/** What the program must accomplish, in the author's words. Appended to the generated API doc. */
|
|
217
|
+
readonly task: string;
|
|
218
|
+
/** Import specifiers the authored code may name. Empty (the default) bans every import. */
|
|
219
|
+
readonly allowedImports?: ReadonlyArray<string>;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Refuse the obvious escapes in authored source. A LINT, not a sandbox: it reads text and cannot
|
|
223
|
+
* constrain what running code does. Generalized from `strategy-author.ts`'s contract check, which
|
|
224
|
+
* has guarded agent-authored optimization strategies since 0.60.
|
|
225
|
+
*/
|
|
226
|
+
declare function assertAuthoredCode(code: string, options?: {
|
|
227
|
+
readonly allowedImports?: ReadonlyArray<string>;
|
|
228
|
+
readonly context?: string;
|
|
229
|
+
}): void;
|
|
230
|
+
/** The API doc the model is shown — generated from the grant, so the two cannot disagree. */
|
|
231
|
+
declare function renderCodeApi(config: CodeModeConfig): string;
|
|
232
|
+
/** Pull the first fenced block out of a model reply; the whole reply if it carries no fence. */
|
|
233
|
+
declare function extractCodeBlock(reply: string): string;
|
|
234
|
+
/**
|
|
235
|
+
* A node that asks a model for a program and runs it. Declares the two effects it cannot supply
|
|
236
|
+
* itself — `model` (who writes the code) and `codeRunner` (where it runs) — so the host owns both.
|
|
237
|
+
*/
|
|
238
|
+
declare function codemodeKind(): NodeKind<CodeModeConfig, readonly ['model', 'codeRunner']>;
|
|
239
|
+
/**
|
|
240
|
+
* An in-process `codeRunner`: builds the authored body as a function with the granted API in
|
|
241
|
+
* scope. **A development convenience, not a security boundary** — `assertAuthoredCode` is a lint,
|
|
242
|
+
* and code running in this process can reach whatever the process can. Supply a jailed runner for
|
|
243
|
+
* code you did not write.
|
|
244
|
+
*/
|
|
245
|
+
declare function inlineCodeRunner(): CodeRunner;
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/runtime/graph/condition.d.ts
|
|
248
|
+
/** The leaf comparison operators; `exists`/`truthy` are unary, the rest compare against `value`. */
|
|
249
|
+
declare const CONDITION_OPS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "in", "contains", "exists", "truthy"];
|
|
250
|
+
type ConditionOp = (typeof CONDITION_OPS)[number];
|
|
251
|
+
interface ConditionLeaf {
|
|
252
|
+
/** Dotted path with `[N]` indexing into the guard context, e.g. `out.findings[0].severity`. */
|
|
253
|
+
readonly path: string;
|
|
254
|
+
readonly op: ConditionOp;
|
|
255
|
+
readonly value?: unknown;
|
|
256
|
+
}
|
|
257
|
+
type Condition = ConditionLeaf | {
|
|
258
|
+
readonly all: ReadonlyArray<Condition>;
|
|
259
|
+
} | {
|
|
260
|
+
readonly any: ReadonlyArray<Condition>;
|
|
261
|
+
} | {
|
|
262
|
+
readonly not: Condition;
|
|
263
|
+
};
|
|
264
|
+
/** Validate shape, bounds, and per-leaf path/operator rules; returns the input for chaining. */
|
|
265
|
+
declare function validateCondition(raw: unknown, context: string): Condition;
|
|
266
|
+
/** Walk a validated condition over a context to a boolean. Never throws on data shape. */
|
|
267
|
+
declare function evaluateCondition(condition: Condition, context: unknown): boolean;
|
|
268
|
+
//#endregion
|
|
189
269
|
//#region src/runtime/graph/projection.d.ts
|
|
190
270
|
type Projection = {
|
|
191
271
|
readonly path: string;
|
|
@@ -284,15 +364,15 @@ interface EngineGraphSpec {
|
|
|
284
364
|
//#region src/runtime/graph/engine.d.ts
|
|
285
365
|
interface GraphEngineOptions {
|
|
286
366
|
/** Kinds to register beside the core set. A host adds its own here; nothing is global. */
|
|
287
|
-
readonly kinds?: ReadonlyArray<
|
|
367
|
+
readonly kinds?: ReadonlyArray<AnyNodeKind>;
|
|
288
368
|
/** The host's effect table, by name. A kind receives only the effects it declared. */
|
|
289
369
|
readonly effects?: Readonly<Record<EffectName, unknown>>;
|
|
290
370
|
/** The core set. Injected so a test can substitute, and so the engine never imports a
|
|
291
371
|
* backend-specific factory at module load. */
|
|
292
|
-
readonly coreKinds: ReadonlyArray<
|
|
372
|
+
readonly coreKinds: ReadonlyArray<AnyNodeKind>;
|
|
293
373
|
}
|
|
294
374
|
interface GraphEngine {
|
|
295
|
-
readonly kinds: Registry<
|
|
375
|
+
readonly kinds: Registry<AnyNodeKind>;
|
|
296
376
|
readonly effects: Readonly<Record<EffectName, unknown>>;
|
|
297
377
|
/** Every effect name any registered kind declares — what a host must provide for this engine's
|
|
298
378
|
* whole kind set to be runnable. Listed, never discovered mid-run. */
|
|
@@ -363,6 +443,10 @@ type GraphRunReason = 'all-children-down' | 'budget-exhausted' | 'aborted' | 'dr
|
|
|
363
443
|
interface GraphNodeSettle {
|
|
364
444
|
readonly node: string;
|
|
365
445
|
readonly visit: number;
|
|
446
|
+
/** Run-wide settle ordinal. `GraphRunResult.settles` is sorted by it, so the array reads in the
|
|
447
|
+
* order the run actually settled — not grouped by node, which is what a reader assumes and
|
|
448
|
+
* what the first consumer of this API tripped on. */
|
|
449
|
+
readonly seq: number;
|
|
366
450
|
readonly status: 'done' | 'down';
|
|
367
451
|
/** The node's completion check verdict; `undefined` when the node declares no check. */
|
|
368
452
|
readonly valid?: boolean;
|
|
@@ -451,6 +535,10 @@ interface FoldNode {
|
|
|
451
535
|
settles: GraphNodeSettle[];
|
|
452
536
|
}
|
|
453
537
|
interface GraphFoldState {
|
|
538
|
+
/** Next settle ordinal; the fold is the only writer, so replay reproduces the same order. */
|
|
539
|
+
settleSeq: {
|
|
540
|
+
value: number;
|
|
541
|
+
};
|
|
454
542
|
readonly nodes: Map<string, FoldNode>;
|
|
455
543
|
readonly edges: Map<string, FoldEdge>;
|
|
456
544
|
/** Every node instance the journal knows, keyed by `<node>#<visit>`. */
|
|
@@ -710,5 +798,5 @@ declare function runEngineGraph(engine: GraphEngine, spec: EngineGraphSpec | Com
|
|
|
710
798
|
*/
|
|
711
799
|
declare function createGraphRun(engine: GraphEngine, spec: EngineGraphSpec | CompiledGraph, task: string, options: GraphRunOptions): GraphRunHandle;
|
|
712
800
|
//#endregion
|
|
713
|
-
export { type AgentKindConfig, type BudgetMode, CONDITION_OPS, type CompiledEdge, type CompiledGraph, type CompiledNode, type Condition, type ConditionLeaf, type ConditionOp, DEFAULT_MAX_NODE_VISITS, ENGINE_WOKEN_SEQ_BASE, type EdgeLedger, type EffectContext, type EffectName, type EngineGraphEdge, type EngineGraphNode, type EngineGraphSpec, type FinalizerChoice, type FoldEdge, type FoldEdgeState, type FoldInstance, type FoldInstanceStatus, type FoldNode, type FoldSuspension, type GatingEdge, type GraphEdgeKind, type GraphEdgeTraversal, type GraphEngine, type GraphEngineOptions, type GraphFoldState, type GraphHost, type GraphNodeSettle, type GraphRunContext, type GraphRunHandle, type GraphRunOptions, type GraphRunReason, type GraphRunResult, JOIN_RULES, type JoinDecision, type JoinRule, type JsonSchema, MAX_MAX_NODE_VISITS, type NodeFlags, type NodeKind, type OnCrash, type PortSpec, type Projection, RUN_GRAPH_ROOT_KIND, type Registered, type Registry, type RegistryHandle, type ScriptBody, type ScriptKindConfig, type SubgraphKindConfig, type SupervisorKindConfig, type SuspensionRequest, admitPayload, agentKind, applyGraphFoldEvent, applyProjection, assembleGraphResult, compileGraph, createEdgeLedger, createGraphEngine, createGraphRun, createRegistry, decideJoin, emptyFoldState, evaluateCondition, foldGraphJournal, formatRegistryHandle, graphFromRunGraph, isEngineFired, isSuspensionRequest, kindHandle, materializeSettles, mintSuspensionToken, narrowEffects, openGraphRun, parseRegistryHandle, runEngineGraph, schemaAccepts, scriptKind, subgraphKind, supervisorKind, suspended, suspensionNodeId, tokenFromSuspensionNodeId, validateCondition, validateNodeKind, validateProjection };
|
|
801
|
+
export { type AgentKindConfig, type AnyNodeKind, type BudgetMode, CONDITION_OPS, type CodeAuthor, type CodeModeConfig, type CodeOperation, type CodeOperationResult, type CodeRunner, type CompiledEdge, type CompiledGraph, type CompiledNode, type Condition, type ConditionLeaf, type ConditionOp, DEFAULT_MAX_NODE_VISITS, ENGINE_WOKEN_SEQ_BASE, type EdgeLedger, type EffectContext, type EffectName, type EngineGraphEdge, type EngineGraphNode, type EngineGraphSpec, type FinalizerChoice, type FoldEdge, type FoldEdgeState, type FoldInstance, type FoldInstanceStatus, type FoldNode, type FoldSuspension, type GatingEdge, type GraphEdgeKind, type GraphEdgeTraversal, type GraphEngine, type GraphEngineOptions, type GraphFoldState, type GraphHost, type GraphNodeSettle, type GraphRunContext, type GraphRunHandle, type GraphRunOptions, type GraphRunReason, type GraphRunResult, JOIN_RULES, type JoinDecision, type JoinRule, type JsonSchema, MAX_MAX_NODE_VISITS, type NodeFlags, type NodeKind, type OnCrash, type PortSpec, type Projection, RUN_GRAPH_ROOT_KIND, type Registered, type Registry, type RegistryHandle, type ScriptBody, type ScriptKindConfig, type SubgraphKindConfig, type SupervisorKindConfig, type SuspensionRequest, admitPayload, agentKind, applyGraphFoldEvent, applyProjection, assembleGraphResult, assertAuthoredCode, codemodeKind, compileGraph, createEdgeLedger, createGraphEngine, createGraphRun, createRegistry, decideJoin, emptyFoldState, evaluateCondition, extractCodeBlock, foldGraphJournal, formatRegistryHandle, graphFromRunGraph, inlineCodeRunner, isEngineFired, isSuspensionRequest, kindHandle, materializeSettles, mintSuspensionToken, narrowEffects, openGraphRun, parseRegistryHandle, renderCodeApi, runEngineGraph, schemaAccepts, scriptKind, subgraphKind, supervisorKind, suspended, suspensionNodeId, tokenFromSuspensionNodeId, validateCondition, validateNodeKind, validateProjection };
|
|
714
802
|
//# sourceMappingURL=graph.d.ts.map
|
package/dist/graph.js
CHANGED
|
@@ -4,6 +4,188 @@ import { t as addSpend } from "./util-D6ZEuBMi.js";
|
|
|
4
4
|
import { N as createBudgetPool, U as createExecutorRegistry, a as uncertainSpawnBudgets, i as sumMeasuredSpendFromEvents, l as runFinalizer, o as bestDelivered, p as createScope, r as maxSeqOf, s as collectDelivered } from "./supervisor-B-mmmiOU.js";
|
|
5
5
|
import { i as workerFromBackend, s as supervisorAgent } from "./supervise-rEE46ruH.js";
|
|
6
6
|
import { t as GraphEdgeCapError } from "./graph-ZDMygFIo.js";
|
|
7
|
+
//#region src/runtime/graph/codemode.ts
|
|
8
|
+
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;
|
|
9
|
+
/**
|
|
10
|
+
* Refuse the obvious escapes in authored source. A LINT, not a sandbox: it reads text and cannot
|
|
11
|
+
* constrain what running code does. Generalized from `strategy-author.ts`'s contract check, which
|
|
12
|
+
* has guarded agent-authored optimization strategies since 0.60.
|
|
13
|
+
*/
|
|
14
|
+
function assertAuthoredCode(code, options = {}) {
|
|
15
|
+
const context = options.context ?? "authored code";
|
|
16
|
+
const allowed = options.allowedImports ?? [];
|
|
17
|
+
for (const line of code.split("\n")) {
|
|
18
|
+
if (!/^\s*import\s/.test(line)) continue;
|
|
19
|
+
if (!allowed.some((specifier) => line.includes(`'${specifier}'`) || line.includes(`"${specifier}"`))) throw new ValidationError(`${context} rejected: foreign import — ${line.trim().slice(0, 120)}${allowed.length === 0 ? " (no import is allowed here)" : ` (allowed: ${allowed.join(", ")})`}`);
|
|
20
|
+
}
|
|
21
|
+
for (const [pattern, what] of [
|
|
22
|
+
[/\brequire\s*\(/, "require()"],
|
|
23
|
+
[/\bimport\s*\(/, "dynamic import()"],
|
|
24
|
+
[/\beval\s*\(/, "eval()"],
|
|
25
|
+
[/new\s+Function\s*\(/, "new Function()"],
|
|
26
|
+
[/\bprocess\s*[.[]/, "process access"],
|
|
27
|
+
[/\bglobalThis\s*[.[]/, "globalThis access"],
|
|
28
|
+
[/\bfetch\s*\(/, "network access"],
|
|
29
|
+
[/child_process|node:fs|node:net|node:http|worker_threads/, "node builtin access"]
|
|
30
|
+
]) if (pattern.test(code)) throw new ValidationError(`${context} rejected: ${what}`);
|
|
31
|
+
}
|
|
32
|
+
/** The API doc the model is shown — generated from the grant, so the two cannot disagree. */
|
|
33
|
+
function renderCodeApi(config) {
|
|
34
|
+
return [
|
|
35
|
+
"Write ONE async function body. It may call only these operations:",
|
|
36
|
+
"",
|
|
37
|
+
...config.operations.map((operation) => ` ${operation.signature}\n // ${operation.description}`),
|
|
38
|
+
"",
|
|
39
|
+
"Return the result as the body's return value. No imports, no require, no process, no fetch.",
|
|
40
|
+
"Reply with a single fenced code block and nothing else.",
|
|
41
|
+
"",
|
|
42
|
+
`TASK: ${config.task}`
|
|
43
|
+
].join("\n");
|
|
44
|
+
}
|
|
45
|
+
/** Pull the first fenced block out of a model reply; the whole reply if it carries no fence. */
|
|
46
|
+
function extractCodeBlock(reply) {
|
|
47
|
+
const code = (/```(?:[a-zA-Z]*)\n([\s\S]*?)```/u.exec(reply)?.[1] ?? reply).trim();
|
|
48
|
+
if (code.length === 0) throw new ValidationError("codemode: the model returned no code");
|
|
49
|
+
return code;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A node that asks a model for a program and runs it. Declares the two effects it cannot supply
|
|
53
|
+
* itself — `model` (who writes the code) and `codeRunner` (where it runs) — so the host owns both.
|
|
54
|
+
*/
|
|
55
|
+
function codemodeKind() {
|
|
56
|
+
return {
|
|
57
|
+
id: "codemode",
|
|
58
|
+
version: 1,
|
|
59
|
+
description: "Ask a model for a program over a granted API, then run it as one node.",
|
|
60
|
+
validateConfig: (raw, context) => {
|
|
61
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new ValidationError(`${context}: codemode config must be an object`);
|
|
62
|
+
const config = raw;
|
|
63
|
+
if (typeof config.task !== "string" || config.task.trim().length === 0) throw new ValidationError(`${context}: codemode config.task must be a non-empty string`);
|
|
64
|
+
if (!Array.isArray(config.operations) || config.operations.length === 0) throw new ValidationError(`${context}: codemode config.operations must grant at least one operation`);
|
|
65
|
+
const seen = /* @__PURE__ */ new Set();
|
|
66
|
+
for (const operation of config.operations) {
|
|
67
|
+
if (!IDENTIFIER.test(operation?.name ?? "")) throw new ValidationError(`${context}: operation name ${JSON.stringify(operation?.name)} is not a JS identifier`);
|
|
68
|
+
if (seen.has(operation.name)) throw new ValidationError(`${context}: duplicate operation ${operation.name}`);
|
|
69
|
+
seen.add(operation.name);
|
|
70
|
+
if (typeof operation.call !== "function") throw new ValidationError(`${context}: operation ${operation.name} has no call`);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
operations: config.operations,
|
|
74
|
+
task: config.task,
|
|
75
|
+
...config.allowedImports === void 0 ? {} : { allowedImports: config.allowedImports }
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
configSchema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
task: { type: "string" },
|
|
82
|
+
operations: { type: "array" }
|
|
83
|
+
},
|
|
84
|
+
required: ["task", "operations"]
|
|
85
|
+
},
|
|
86
|
+
inputs: [{
|
|
87
|
+
name: "context",
|
|
88
|
+
schema: {}
|
|
89
|
+
}],
|
|
90
|
+
outputs: [],
|
|
91
|
+
effects: ["model", "codeRunner"],
|
|
92
|
+
onCrash: "restart",
|
|
93
|
+
budget: "metered",
|
|
94
|
+
run: ({ config, profile, inputs, effects }) => {
|
|
95
|
+
const author = effects.model;
|
|
96
|
+
const runner = effects.codeRunner;
|
|
97
|
+
let artifact;
|
|
98
|
+
return {
|
|
99
|
+
name: profile.name ?? "codemode",
|
|
100
|
+
act: () => Promise.reject(new ValidationError("codemode: act() is not the execution path")),
|
|
101
|
+
executorSpec: {
|
|
102
|
+
profile,
|
|
103
|
+
harness: null,
|
|
104
|
+
executor: {
|
|
105
|
+
runtime: "inline",
|
|
106
|
+
async execute(task, signal) {
|
|
107
|
+
const spends = [];
|
|
108
|
+
const prompt = [
|
|
109
|
+
renderCodeApi(config),
|
|
110
|
+
inputs.context === void 0 ? "" : `\nCONTEXT: ${JSON.stringify(inputs.context)}`,
|
|
111
|
+
typeof task === "string" && task.length > 0 ? `\n${task}` : ""
|
|
112
|
+
].filter((part) => part.length > 0).join("\n");
|
|
113
|
+
const authored = await author.complete({
|
|
114
|
+
prompt,
|
|
115
|
+
profile,
|
|
116
|
+
signal
|
|
117
|
+
});
|
|
118
|
+
if (authored.spend) spends.push(authored.spend);
|
|
119
|
+
const code = extractCodeBlock(authored.text);
|
|
120
|
+
assertAuthoredCode(code, {
|
|
121
|
+
...config.allowedImports === void 0 ? {} : { allowedImports: config.allowedImports },
|
|
122
|
+
context: `codemode ${JSON.stringify(profile.name ?? "node")}`
|
|
123
|
+
});
|
|
124
|
+
const api = {};
|
|
125
|
+
for (const operation of config.operations) api[operation.name] = async (...args) => {
|
|
126
|
+
const result = await operation.call(...args);
|
|
127
|
+
if (result?.spend) spends.push(result.spend);
|
|
128
|
+
return result?.value;
|
|
129
|
+
};
|
|
130
|
+
const out = await runner.run({
|
|
131
|
+
code,
|
|
132
|
+
api: Object.freeze(api),
|
|
133
|
+
signal
|
|
134
|
+
});
|
|
135
|
+
artifact = {
|
|
136
|
+
outRef: contentAddress({ codemode: out }),
|
|
137
|
+
out,
|
|
138
|
+
spent: totalSpend(spends)
|
|
139
|
+
};
|
|
140
|
+
return artifact;
|
|
141
|
+
},
|
|
142
|
+
teardown: () => Promise.resolve({ destroyed: true }),
|
|
143
|
+
resultArtifact: () => {
|
|
144
|
+
if (!artifact) throw new ValidationError("codemode: resultArtifact() read before execute()");
|
|
145
|
+
return artifact;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** The node's spend is the model call plus every operation that reported one. An operation that
|
|
154
|
+
* reports nothing is not assumed free — it contributes an UNKNOWN mark, like the kernel's own. */
|
|
155
|
+
function totalSpend(parts) {
|
|
156
|
+
return parts.reduce((total, part) => ({
|
|
157
|
+
iterations: total.iterations + (part.iterations ?? 0),
|
|
158
|
+
tokens: {
|
|
159
|
+
input: total.tokens.input + (part.tokens?.input ?? 0),
|
|
160
|
+
output: total.tokens.output + (part.tokens?.output ?? 0),
|
|
161
|
+
...part.tokens?.tokensKnown === false ? { tokensKnown: false } : {}
|
|
162
|
+
},
|
|
163
|
+
usd: total.usd + (part.usd ?? 0),
|
|
164
|
+
...part.usdKnown === false ? { usdKnown: false } : {},
|
|
165
|
+
ms: total.ms + (part.ms ?? 0)
|
|
166
|
+
}), {
|
|
167
|
+
iterations: 1,
|
|
168
|
+
tokens: {
|
|
169
|
+
input: 0,
|
|
170
|
+
output: 0
|
|
171
|
+
},
|
|
172
|
+
usd: 0,
|
|
173
|
+
ms: 0
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* An in-process `codeRunner`: builds the authored body as a function with the granted API in
|
|
178
|
+
* scope. **A development convenience, not a security boundary** — `assertAuthoredCode` is a lint,
|
|
179
|
+
* and code running in this process can reach whatever the process can. Supply a jailed runner for
|
|
180
|
+
* code you did not write.
|
|
181
|
+
*/
|
|
182
|
+
function inlineCodeRunner() {
|
|
183
|
+
return { async run({ code, api }) {
|
|
184
|
+
const names = Object.keys(api);
|
|
185
|
+
return new Function(...names, `"use strict"; return (async () => { ${code} })()`)(...names.map((name) => api[name]));
|
|
186
|
+
} };
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
7
189
|
//#region src/runtime/graph/condition.ts
|
|
8
190
|
/**
|
|
9
191
|
* The ONE predicate tree: every guard in a graph — an edge `guard`, a filter projection's
|
|
@@ -632,6 +814,7 @@ function emptyFoldState(compiled) {
|
|
|
632
814
|
capped: false
|
|
633
815
|
});
|
|
634
816
|
return {
|
|
817
|
+
settleSeq: { value: 0 },
|
|
635
818
|
nodes,
|
|
636
819
|
edges,
|
|
637
820
|
instances: /* @__PURE__ */ new Map(),
|
|
@@ -681,6 +864,7 @@ function applyGraphFoldEvent(state, ev, compiled) {
|
|
|
681
864
|
node: instance.node,
|
|
682
865
|
visit: instance.visit,
|
|
683
866
|
status: ev.status,
|
|
867
|
+
seq: state.settleSeq.value++,
|
|
684
868
|
...ev.outRef !== void 0 ? { outRef: ev.outRef } : {},
|
|
685
869
|
...ev.reason !== void 0 ? { reason: ev.reason } : {}
|
|
686
870
|
};
|
|
@@ -776,6 +960,7 @@ function applyGraphFoldEvent(state, ev, compiled) {
|
|
|
776
960
|
node: suspension.node,
|
|
777
961
|
visit: instance.visit,
|
|
778
962
|
status: "down",
|
|
963
|
+
seq: state.settleSeq.value++,
|
|
779
964
|
reason: "suspension expired"
|
|
780
965
|
};
|
|
781
966
|
instance.settle = settle;
|
|
@@ -790,6 +975,7 @@ function applyGraphFoldEvent(state, ev, compiled) {
|
|
|
790
975
|
node: suspension.node,
|
|
791
976
|
visit: instance.visit,
|
|
792
977
|
status: "done",
|
|
978
|
+
seq: state.settleSeq.value++,
|
|
793
979
|
...ev.outRef !== void 0 ? { outRef: ev.outRef } : {}
|
|
794
980
|
};
|
|
795
981
|
instance.settle = settle;
|
|
@@ -1282,7 +1468,7 @@ async function materializeSettles(compiled, state, blobs, outCache) {
|
|
|
1282
1468
|
...valid !== void 0 ? { valid } : {}
|
|
1283
1469
|
};
|
|
1284
1470
|
};
|
|
1285
|
-
return Promise.all([...compiled.nodes.keys()].flatMap((id) => state.nodes.get(id)?.settles ?? []).map(rehydrate));
|
|
1471
|
+
return Promise.all([...compiled.nodes.keys()].flatMap((id) => state.nodes.get(id)?.settles ?? []).sort((a, b) => a.seq - b.seq).map(rehydrate));
|
|
1286
1472
|
}
|
|
1287
1473
|
/** Turn a finished run into its result: rehydrate, reduce the terminals, classify a no-winner. */
|
|
1288
1474
|
async function assembleGraphResult(args) {
|
|
@@ -2068,6 +2254,6 @@ async function runGraphLoop(engine, compiled, task, options, wakes, onWakeSignal
|
|
|
2068
2254
|
});
|
|
2069
2255
|
}
|
|
2070
2256
|
//#endregion
|
|
2071
|
-
export { CONDITION_OPS, DEFAULT_MAX_NODE_VISITS, ENGINE_WOKEN_SEQ_BASE, JOIN_RULES, MAX_MAX_NODE_VISITS, RUN_GRAPH_ROOT_KIND, admitPayload, agentKind, applyGraphFoldEvent, applyProjection, assembleGraphResult, compileGraph, createEdgeLedger, createGraphEngine, createGraphRun, createRegistry, decideJoin, emptyFoldState, evaluateCondition, foldGraphJournal, formatRegistryHandle, graphFromRunGraph, isEngineFired, isSuspensionRequest, kindHandle, materializeSettles, mintSuspensionToken, narrowEffects, openGraphRun, parseRegistryHandle, runEngineGraph, schemaAccepts, scriptKind, subgraphKind, supervisorKind, suspended, suspensionNodeId, tokenFromSuspensionNodeId, validateCondition, validateNodeKind, validateProjection };
|
|
2257
|
+
export { CONDITION_OPS, DEFAULT_MAX_NODE_VISITS, ENGINE_WOKEN_SEQ_BASE, JOIN_RULES, MAX_MAX_NODE_VISITS, RUN_GRAPH_ROOT_KIND, admitPayload, agentKind, applyGraphFoldEvent, applyProjection, assembleGraphResult, assertAuthoredCode, codemodeKind, compileGraph, createEdgeLedger, createGraphEngine, createGraphRun, createRegistry, decideJoin, emptyFoldState, evaluateCondition, extractCodeBlock, foldGraphJournal, formatRegistryHandle, graphFromRunGraph, inlineCodeRunner, isEngineFired, isSuspensionRequest, kindHandle, materializeSettles, mintSuspensionToken, narrowEffects, openGraphRun, parseRegistryHandle, renderCodeApi, runEngineGraph, schemaAccepts, scriptKind, subgraphKind, supervisorKind, suspended, suspensionNodeId, tokenFromSuspensionNodeId, validateCondition, validateNodeKind, validateProjection };
|
|
2072
2258
|
|
|
2073
2259
|
//# sourceMappingURL=graph.js.map
|