@tangle-network/agent-runtime 0.168.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 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.
@@ -197,6 +175,97 @@ declare function kindHandle(kind: Pick<NodeKind, 'id' | 'version'>): RegistryHan
197
175
  */
198
176
  declare function narrowEffects<Effects extends ReadonlyArray<EffectName>>(declared: Effects, provided: Readonly<Record<string, unknown>>, context: string): EffectContext<Effects>;
199
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
200
269
  //#region src/runtime/graph/projection.d.ts
201
270
  type Projection = {
202
271
  readonly path: string;
@@ -729,5 +798,5 @@ declare function runEngineGraph(engine: GraphEngine, spec: EngineGraphSpec | Com
729
798
  */
730
799
  declare function createGraphRun(engine: GraphEngine, spec: EngineGraphSpec | CompiledGraph, task: string, options: GraphRunOptions): GraphRunHandle;
731
800
  //#endregion
732
- export { type AgentKindConfig, type AnyNodeKind, 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 };
733
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
@@ -2072,6 +2254,6 @@ async function runGraphLoop(engine, compiled, task, options, wakes, onWakeSignal
2072
2254
  });
2073
2255
  }
2074
2256
  //#endregion
2075
- 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 };
2076
2258
 
2077
2259
  //# sourceMappingURL=graph.js.map