@tangle-network/agent-runtime 0.161.0 → 0.162.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/{activation-DaUNBcZw.js → activation-DxGP0h_p.js} +2 -2
- package/dist/{activation-DaUNBcZw.js.map → activation-DxGP0h_p.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{authoring-DDRliHit.js → authoring-kO7vE_c9.js} +2 -2
- package/dist/{authoring-DDRliHit.js.map → authoring-kO7vE_c9.js.map} +1 -1
- package/dist/{conversation-DLYKdh6W.js → conversation-7XGKoDM6.js} +3 -3
- package/dist/{conversation-DLYKdh6W.js.map → conversation-7XGKoDM6.js.map} +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.js +1 -1
- package/dist/durable.d.ts +2 -2
- package/dist/durable.js +1 -1
- package/dist/{environment-provider-BobKk46F.d.ts → environment-provider-Bk8rg4gs.d.ts} +2 -2
- package/dist/environment-provider.d.ts +1 -1
- package/dist/{executable-spec-7ovS7PBo.js → executable-spec-CkGvdPds.js} +17 -5
- package/dist/executable-spec-CkGvdPds.js.map +1 -0
- package/dist/{graph-DWUz1caU.js → graph-COikUkrj.js} +2 -2
- package/dist/{graph-DWUz1caU.js.map → graph-COikUkrj.js.map} +1 -1
- package/dist/graph.d.ts +241 -0
- package/dist/graph.js +380 -0
- package/dist/graph.js.map +1 -0
- package/dist/{improvement-cycle-oUBE1Pzg.js → improvement-cycle-dGtgX1vr.js} +4 -4
- package/dist/{improvement-cycle-oUBE1Pzg.js.map → improvement-cycle-dGtgX1vr.js.map} +1 -1
- package/dist/{index-CT7Cmz66.d.ts → index-4mRXbu_V.d.ts} +4 -4
- package/dist/{index-5B7TlTSi.d.ts → index-8b0o4dMF.d.ts} +5 -5
- package/dist/{index-CLt9zpuD.d.ts → index-CTQ97IMO.d.ts} +2 -2
- package/dist/index.d.ts +7 -7
- package/dist/index.js +9 -9
- package/dist/intelligence.d.ts +4 -4
- package/dist/intelligence.js +3 -3
- package/dist/kernel.d.ts +5 -5
- package/dist/kernel.js +7 -7
- package/dist/{knowledge-BBfx7pSo.js → knowledge-CZT8ahp6.js} +3 -3
- package/dist/{knowledge-BBfx7pSo.js.map → knowledge-CZT8ahp6.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-CjRjwtal.js → loop-runner-bin-CIjmKf7v.js} +3 -3
- package/dist/{loop-runner-bin-CjRjwtal.js.map → loop-runner-bin-CIjmKf7v.js.map} +1 -1
- package/dist/{loop-runner-bin-DRX6EDIy.d.ts → loop-runner-bin-DHU5kmJW.d.ts} +3 -3
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +3 -3
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-CLb9RqXL.js → openai-tools-DG779Z6S.js} +2 -2
- package/dist/{openai-tools-CLb9RqXL.js.map → openai-tools-DG779Z6S.js.map} +1 -1
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/{redact-0AK8JmHW.d.ts → redact-BUIB5IWj.d.ts} +3 -3
- package/dist/{runtime-BzY66cd6.d.ts → runtime-B0FIFwpH.d.ts} +3 -3
- package/dist/{runtime-CyVJSWzN.js → runtime-BT00zfIS.js} +8 -8
- package/dist/{runtime-CyVJSWzN.js.map → runtime-BT00zfIS.js.map} +1 -1
- package/dist/{stream-agent-turn-jaaFaSXM.d.ts → stream-agent-turn-BEhmmYmL.d.ts} +2 -2
- package/dist/{stream-agent-turn-FRZAuXAL.js → stream-agent-turn-CKIpCbSq.js} +2 -2
- package/dist/{stream-agent-turn-FRZAuXAL.js.map → stream-agent-turn-CKIpCbSq.js.map} +1 -1
- package/dist/{structural-rollout-JEhoJUjC.js → structural-rollout-DAenyNls.js} +4 -4
- package/dist/{structural-rollout-JEhoJUjC.js.map → structural-rollout-DAenyNls.js.map} +1 -1
- package/dist/{supervise-DE-GFNKg.js → supervise-Di6xvnOL.js} +2 -2
- package/dist/{supervise-DE-GFNKg.js.map → supervise-Di6xvnOL.js.map} +1 -1
- package/dist/{supervisor-ZlQy5Fwl.js → supervisor-DmWq4vkU.js} +15 -2
- package/dist/{supervisor-ZlQy5Fwl.js.map → supervisor-DmWq4vkU.js.map} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +11 -11
- package/dist/{types-BBXdSns1.d.ts → types-DXKDG6DQ.d.ts} +8 -5
- package/package.json +6 -1
- package/dist/executable-spec-7ovS7PBo.js.map +0 -1
package/dist/graph.d.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { $l as MakeWorkerAgent, If as DeliverableSpec, bo as SupervisorAgentDeps, mu as WorkerSpawnContext } from "./index-8b0o4dMF.js";
|
|
2
|
+
import { K as Spend, t as Agent } from "./types-DXKDG6DQ.js";
|
|
3
|
+
import { o as ExecutorConfig } from "./runtime-B0FIFwpH.js";
|
|
4
|
+
import { AgentProfile } from "@tangle-network/agent-interface";
|
|
5
|
+
//#region src/runtime/graph/registry.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* `Registry<T>` — the ONE name→thing shape for the graph engine.
|
|
8
|
+
*
|
|
9
|
+
* The kernel grew fourteen of these (agent-runtime#978) that differ in three properties:
|
|
10
|
+
* whether names can be listed, what a miss does, and whether the table is global. This one
|
|
11
|
+
* fixes all three — enumerable, a miss is refused BY NAME listing what is registered, and every
|
|
12
|
+
* registry is per-instance — and it is lifted from `AgentEnvironmentProviderRegistry`, the
|
|
13
|
+
* richest and best-tested of the fourteen, not invented.
|
|
14
|
+
*
|
|
15
|
+
* Entries are addressed by a versioned handle, `<id>/v<n>`, the same way the prompt registry
|
|
16
|
+
* addresses directives. A graph names a kind by handle; a host registers exact versions; a
|
|
17
|
+
* missing version is refused, never served by a newer one.
|
|
18
|
+
*/
|
|
19
|
+
/** A versioned name: what a graph writes and what a host registers. */
|
|
20
|
+
interface RegistryHandle {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly version: number;
|
|
23
|
+
}
|
|
24
|
+
/** `<id>/v<n>` — the only spelling a handle has on the wire, in a journal, or in an error. */
|
|
25
|
+
declare function formatRegistryHandle(handle: RegistryHandle): string;
|
|
26
|
+
/** Parse the wire spelling back. Refuses anything that is not exactly `<id>/v<n>`. */
|
|
27
|
+
declare function parseRegistryHandle(text: string, context: string): RegistryHandle;
|
|
28
|
+
/** Anything a registry holds carries its own handle, so the table cannot drift from the entry. */
|
|
29
|
+
interface Registered extends RegistryHandle {}
|
|
30
|
+
interface Registry<T extends Registered> {
|
|
31
|
+
/** Add one entry. A second entry under the same handle is refused unless `replace` is set —
|
|
32
|
+
* silently shadowing a registered kind is how a key no caller could produce once survived. */
|
|
33
|
+
register(entry: T, options?: {
|
|
34
|
+
readonly replace?: boolean;
|
|
35
|
+
}): void;
|
|
36
|
+
has(handle: RegistryHandle): boolean;
|
|
37
|
+
get(handle: RegistryHandle): T | undefined;
|
|
38
|
+
/** The entry, or a refusal that names the handle AND lists every registered handle — a miss
|
|
39
|
+
* must be diagnosable from its message alone. */
|
|
40
|
+
require(handle: RegistryHandle, context?: string): T;
|
|
41
|
+
/** Every registered handle, sorted, as wire spellings. The thing the fourteen predecessors
|
|
42
|
+
* mostly could not do and four callers needed. */
|
|
43
|
+
names(): string[];
|
|
44
|
+
/** Every entry, in `names()` order. */
|
|
45
|
+
entries(): T[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a registry. Per-instance by construction: two engines in one process may hold
|
|
49
|
+
* different kind sets, a test is hermetic, and a run can print its own table. There is
|
|
50
|
+
* deliberately no module-level singleton — `builtinShapes` was the one mutable global in the
|
|
51
|
+
* kernel and it had zero tests.
|
|
52
|
+
*/
|
|
53
|
+
declare function createRegistry<T extends Registered>(label: string, seed?: Iterable<T>): Registry<T>;
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/runtime/graph/kind.d.ts
|
|
56
|
+
/** A JSON Schema document as the kernel already spells it: an opaque record, validated by the
|
|
57
|
+
* consumer's own validator, published verbatim. */
|
|
58
|
+
type JsonSchema = Readonly<Record<string, unknown>>;
|
|
59
|
+
/**
|
|
60
|
+
* One declared port on a node. Ports are how a `data` edge binds one node's output to another's
|
|
61
|
+
* input with a type the compiler can check structurally before any spend. A node has two implicit
|
|
62
|
+
* output ports beside its declared ones: `out` (its result) and `trace` (its `WorkerTraceEvidence`
|
|
63
|
+
* by `traceRef`); only an `analyzes` edge may bind `trace`.
|
|
64
|
+
*/
|
|
65
|
+
interface PortSpec {
|
|
66
|
+
readonly name: string;
|
|
67
|
+
readonly schema: JsonSchema;
|
|
68
|
+
readonly description?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What a kind declares it needs from the host. The engine never imports a host capability; it
|
|
72
|
+
* knows only that a kind SAID it needs something under this name and the host PROVIDED something
|
|
73
|
+
* under it. The context a kind receives is narrowed to exactly its declaration — an undeclared
|
|
74
|
+
* effect is `undefined`, never a service locator.
|
|
75
|
+
*/
|
|
76
|
+
type EffectName = string;
|
|
77
|
+
type EffectContext<Effects extends ReadonlyArray<EffectName>> = Readonly<{ [K in Effects[number]]: unknown; }>;
|
|
78
|
+
/**
|
|
79
|
+
* What happens to a node that was IN FLIGHT when the process died. A settled node is never a
|
|
80
|
+
* per-kind choice — it restores from its content-addressed `outRef` on replay. `'restart'` re-runs
|
|
81
|
+
* from the journaled `inputRef`; `'resume'` is legal only for a kind whose executor can re-attach
|
|
82
|
+
* to the live process (the bridge backend's session re-attachment is the existing instance).
|
|
83
|
+
*/
|
|
84
|
+
type OnCrash = 'restart' | 'resume';
|
|
85
|
+
/**
|
|
86
|
+
* Whether a kind's spend enters the conserved pool. `'metered'`: the executor reports `Spend` and
|
|
87
|
+
* settling without one is an ENGINE ERROR — never "free". `'exempt'`: the whole reservation is
|
|
88
|
+
* refunded on settle, keeping the node out of Σk by construction (the kernel's `budgetExempt`).
|
|
89
|
+
*/
|
|
90
|
+
type BudgetMode = 'metered' | 'exempt';
|
|
91
|
+
/** The validated declaration every kind provides. `Config` is the per-node config shape;
|
|
92
|
+
* `Effects` is the tuple of host capabilities it declares, so the context `run` receives is typed
|
|
93
|
+
* to exactly that tuple. */
|
|
94
|
+
interface NodeKind<Config = unknown, Effects extends ReadonlyArray<EffectName> = ReadonlyArray<EffectName>> extends Registered {
|
|
95
|
+
/** Kind id, e.g. `agent`, `integration.invoke`. With `version`, forms the handle `<id>/v<n>`. */
|
|
96
|
+
readonly id: string;
|
|
97
|
+
readonly version: number;
|
|
98
|
+
readonly description: string;
|
|
99
|
+
/** Validate and narrow one node's config. Throw `ValidationError` to refuse; the compiler
|
|
100
|
+
* surfaces the message with the node id prefixed. */
|
|
101
|
+
readonly validateConfig: (raw: unknown, context: string) => Config;
|
|
102
|
+
/** The portable form of `validateConfig`'s accepted shape, for manifests and hosts. */
|
|
103
|
+
readonly configSchema: JsonSchema;
|
|
104
|
+
/** Declared input ports; a `data` edge may bind only these. Empty for a source node. */
|
|
105
|
+
readonly inputs: ReadonlyArray<PortSpec>;
|
|
106
|
+
/** Declared output ports beside the implicit `out` and `trace`. */
|
|
107
|
+
readonly outputs: ReadonlyArray<PortSpec>;
|
|
108
|
+
/** Host capabilities this kind reaches for, by name. The context is narrowed to exactly these. */
|
|
109
|
+
readonly effects: Effects;
|
|
110
|
+
readonly onCrash: OnCrash;
|
|
111
|
+
readonly budget: BudgetMode;
|
|
112
|
+
/**
|
|
113
|
+
* Build the agent for one node instance. The kernel spawns it under `Scope.spawn`, so it is
|
|
114
|
+
* authorized, classified, journaled, pooled and gated like any child — the kind owns only what
|
|
115
|
+
* the agent DOES. `profile` is the node's pinned profile (an `agent`/`supervisor` kind runs it;
|
|
116
|
+
* a `script` kind may ignore it); `inputs` are the resolved, content-addressed port values;
|
|
117
|
+
* `effects` is the narrowed host context; `spawn` is the kernel's per-spawn context when the
|
|
118
|
+
* kind needs it (a supervisor kind threads it into `nodeContext`).
|
|
119
|
+
*/
|
|
120
|
+
readonly run: (args: {
|
|
121
|
+
readonly config: Config;
|
|
122
|
+
readonly profile: AgentProfile;
|
|
123
|
+
readonly inputs: Readonly<Record<string, unknown>>;
|
|
124
|
+
readonly effects: EffectContext<Effects>;
|
|
125
|
+
readonly spawn?: WorkerSpawnContext;
|
|
126
|
+
}) => Agent<unknown, unknown>;
|
|
127
|
+
}
|
|
128
|
+
/** Per-node flags a graph author sets; they are node properties, not kinds (agent-runtime#970). */
|
|
129
|
+
interface NodeFlags {
|
|
130
|
+
/** An oracle — a judge, grader, auditor, trace analyst — may be bound only by an `analyzes`
|
|
131
|
+
* edge. The compiler refuses a `delegates` or `data` edge INTO an oracle: an edge to a grader
|
|
132
|
+
* leaks the rubric. */
|
|
133
|
+
readonly oracle?: boolean;
|
|
134
|
+
/** `script` only: pure over `(config, inputs)` ⇒ budget exempt, output restorable on replay,
|
|
135
|
+
* runs in-process. A pure script that settles with a different `outRef` for the same inputs
|
|
136
|
+
* has lied, and the first replay mismatch is an engine error. */
|
|
137
|
+
readonly pure?: boolean;
|
|
138
|
+
}
|
|
139
|
+
/** Validate a kind declaration at registration — so a malformed kind is refused by name once,
|
|
140
|
+
* not at the first node that uses it. */
|
|
141
|
+
declare function validateNodeKind(kind: NodeKind, context?: string): NodeKind;
|
|
142
|
+
/** The handle a graph writes to name this kind. */
|
|
143
|
+
declare function kindHandle(kind: Pick<NodeKind, 'id' | 'version'>): RegistryHandle;
|
|
144
|
+
/**
|
|
145
|
+
* Narrow a host's effect table to exactly what one kind declared. Anything the kind did not
|
|
146
|
+
* declare is absent — `undefined` on read — so a kind cannot reach past its declaration, and the
|
|
147
|
+
* engine can list a graph's required effects before spending a token.
|
|
148
|
+
*/
|
|
149
|
+
declare function narrowEffects<Effects extends ReadonlyArray<EffectName>>(declared: Effects, provided: Readonly<Record<string, unknown>>, context: string): EffectContext<Effects>;
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/runtime/graph/engine.d.ts
|
|
152
|
+
interface GraphEngineOptions {
|
|
153
|
+
/** Kinds to register beside the core set. A host adds its own here; nothing is global. */
|
|
154
|
+
readonly kinds?: ReadonlyArray<NodeKind>;
|
|
155
|
+
/** The host's effect table, by name. A kind receives only the effects it declared. */
|
|
156
|
+
readonly effects?: Readonly<Record<EffectName, unknown>>;
|
|
157
|
+
/** The core set. Injected so a test can substitute, and so the engine never imports a
|
|
158
|
+
* backend-specific factory at module load. */
|
|
159
|
+
readonly coreKinds: ReadonlyArray<NodeKind>;
|
|
160
|
+
}
|
|
161
|
+
interface GraphEngine {
|
|
162
|
+
readonly kinds: Registry<NodeKind>;
|
|
163
|
+
readonly effects: Readonly<Record<EffectName, unknown>>;
|
|
164
|
+
/** Every effect name any registered kind declares — what a host must provide for this engine's
|
|
165
|
+
* whole kind set to be runnable. Listed, never discovered mid-run. */
|
|
166
|
+
requiredEffects(): string[];
|
|
167
|
+
/** The declared effects no host value covers. Empty means every registered kind is runnable. */
|
|
168
|
+
missingEffects(): string[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Build one engine: a kind registry seeded with the core kinds plus the host's, and the host's
|
|
172
|
+
* effect values. Every kind is validated by name at construction, so a malformed host kind fails
|
|
173
|
+
* here, never at its first node.
|
|
174
|
+
*/
|
|
175
|
+
declare function createGraphEngine(options: GraphEngineOptions): GraphEngine;
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/runtime/graph/kinds.d.ts
|
|
178
|
+
interface AgentKindConfig {
|
|
179
|
+
/** Where this node's profile runs. Omit to inherit the engine's default backend. */
|
|
180
|
+
readonly backend?: ExecutorConfig;
|
|
181
|
+
/** This node's completion check. Omit to inherit the graph's terminal check. */
|
|
182
|
+
readonly deliverable?: DeliverableSpec<unknown>;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* One profile, one run: the kernel's leaf, exactly as `supervise()` derives it from `backend`.
|
|
186
|
+
* The model cannot decide this — it is what gets run.
|
|
187
|
+
*/
|
|
188
|
+
declare function agentKind(defaults: {
|
|
189
|
+
readonly backend?: ExecutorConfig;
|
|
190
|
+
readonly deliverable?: DeliverableSpec<unknown>;
|
|
191
|
+
}): NodeKind<AgentKindConfig>;
|
|
192
|
+
interface SupervisorKindConfig {
|
|
193
|
+
/** Per-child budget reserved from the pool on each spawn this supervisor makes. */
|
|
194
|
+
readonly perWorker: SupervisorAgentDeps['perWorker'];
|
|
195
|
+
readonly maxLiveWorkers?: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The thing that DECIDES: a nested `supervisorAgent` with the coordination verbs. Its children
|
|
199
|
+
* are its own tree — the graph sees one node in, one `Settled` out. A `subgraph` constrains what
|
|
200
|
+
* it may spawn; without one it is free-form under `profileSecurity` and `allowedModels`.
|
|
201
|
+
*/
|
|
202
|
+
declare function supervisorKind(deps: {
|
|
203
|
+
readonly blobs: SupervisorAgentDeps['blobs'];
|
|
204
|
+
readonly makeWorkerAgent: MakeWorkerAgent;
|
|
205
|
+
readonly router?: SupervisorAgentDeps['router'];
|
|
206
|
+
readonly driveHarness?: SupervisorAgentDeps['driveHarness'];
|
|
207
|
+
}): NodeKind<SupervisorKindConfig>;
|
|
208
|
+
/** The caller code a `script` node runs. Receives the resolved inputs; returns the output. */
|
|
209
|
+
type ScriptBody = (inputs: Readonly<Record<string, unknown>>, signal: AbortSignal) => Promise<unknown> | unknown;
|
|
210
|
+
interface ScriptKindConfig {
|
|
211
|
+
readonly body: ScriptBody;
|
|
212
|
+
/**
|
|
213
|
+
* `pure: true` is the promise that the output is a function of `(config, inputs)` alone: the
|
|
214
|
+
* node is then budget-exempt, its output restorable on replay by content address, and it runs
|
|
215
|
+
* in-process. A pure script that settles with a different `outRef` for the same inputs has
|
|
216
|
+
* lied, and the first replay mismatch is an engine error.
|
|
217
|
+
*/
|
|
218
|
+
readonly pure?: boolean;
|
|
219
|
+
/** For a metered script: what it spent. Omit on a pure script. A metered script that reports
|
|
220
|
+
* nothing is metered as NOTHING-KNOWN, never as free. */
|
|
221
|
+
readonly spent?: Spend;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Caller code as a node. The one kind with no kernel primitive behind it: the kernel has no
|
|
225
|
+
* "data→data with no execution" concept (agent-runtime#970 fact-finding), so this is new. It is
|
|
226
|
+
* still a leaf `Agent` carrying an `Executor`, so the journal, the gate and the pool treat it like
|
|
227
|
+
* any other node.
|
|
228
|
+
*/
|
|
229
|
+
declare function scriptKind(): NodeKind<ScriptKindConfig>;
|
|
230
|
+
/**
|
|
231
|
+
* A node carrying its own graph: the constraint on what a supervisor may spawn at depth>1. Its
|
|
232
|
+
* executor is a nested engine run; that needs the scheduler (#980), so until then this kind is
|
|
233
|
+
* registered and REFUSES at run time by name rather than being absent — a graph that names it
|
|
234
|
+
* compiles, and the refusal says exactly what is missing.
|
|
235
|
+
*/
|
|
236
|
+
declare function subgraphKind(): NodeKind<{
|
|
237
|
+
readonly graph: unknown;
|
|
238
|
+
}>;
|
|
239
|
+
//#endregion
|
|
240
|
+
export { type AgentKindConfig, type BudgetMode, type EffectContext, type EffectName, type GraphEngine, type GraphEngineOptions, type JsonSchema, type NodeFlags, type NodeKind, type OnCrash, type PortSpec, type Registered, type Registry, type RegistryHandle, type ScriptBody, type ScriptKindConfig, type SupervisorKindConfig, agentKind, createGraphEngine, createRegistry, formatRegistryHandle, kindHandle, narrowEffects, parseRegistryHandle, scriptKind, subgraphKind, supervisorKind, validateNodeKind };
|
|
241
|
+
//# sourceMappingURL=graph.d.ts.map
|
package/dist/graph.js
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { m as ValidationError } from "./errors-CDZ8XsVj.js";
|
|
2
|
+
import { x as contentAddress } from "./spawn-journal-oewq1g2h.js";
|
|
3
|
+
import { i as workerFromBackend, s as supervisorAgent } from "./supervise-Di6xvnOL.js";
|
|
4
|
+
//#region src/runtime/graph/kind.ts
|
|
5
|
+
/** Validate a kind declaration at registration — so a malformed kind is refused by name once,
|
|
6
|
+
* not at the first node that uses it. */
|
|
7
|
+
function validateNodeKind(kind, context = "registerNodeKind") {
|
|
8
|
+
const who = `${context}: kind ${JSON.stringify(`${kind.id}/v${kind.version}`)}`;
|
|
9
|
+
if (typeof kind.id !== "string" || kind.id.length === 0) throw new ValidationError(`${context}: a kind must carry a non-empty id`);
|
|
10
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(kind.id)) throw new ValidationError(`${who}: id may contain only letters, digits, ".", "_" and "-"`);
|
|
11
|
+
if (!Number.isSafeInteger(kind.version) || kind.version < 1) throw new ValidationError(`${who}: version must be a positive integer`);
|
|
12
|
+
if (typeof kind.description !== "string" || kind.description.trim().length === 0) throw new ValidationError(`${who}: description is required`);
|
|
13
|
+
if (typeof kind.validateConfig !== "function") throw new ValidationError(`${who}: validateConfig must be a function`);
|
|
14
|
+
if (!isRecord(kind.configSchema)) throw new ValidationError(`${who}: configSchema must be a JSON Schema object`);
|
|
15
|
+
for (const [field, ports] of [["inputs", kind.inputs], ["outputs", kind.outputs]]) {
|
|
16
|
+
if (!Array.isArray(ports)) throw new ValidationError(`${who}: ${field} must be an array`);
|
|
17
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const port of ports) {
|
|
19
|
+
if (!port || typeof port.name !== "string" || port.name.length === 0) throw new ValidationError(`${who}: every ${field} port needs a non-empty name`);
|
|
20
|
+
if (field === "outputs" && (port.name === "out" || port.name === "trace")) throw new ValidationError(`${who}: output port ${JSON.stringify(port.name)} is implicit on every node and cannot be declared`);
|
|
21
|
+
if (seen.has(port.name)) throw new ValidationError(`${who}: duplicate ${field} port ${JSON.stringify(port.name)}`);
|
|
22
|
+
seen.add(port.name);
|
|
23
|
+
if (!isRecord(port.schema)) throw new ValidationError(`${who}: ${field} port ${JSON.stringify(port.name)} needs a JSON Schema`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!Array.isArray(kind.effects) || kind.effects.some((e) => typeof e !== "string" || e.length === 0)) throw new ValidationError(`${who}: effects must be an array of non-empty names`);
|
|
27
|
+
if (new Set(kind.effects).size !== kind.effects.length) throw new ValidationError(`${who}: effects must not repeat a name`);
|
|
28
|
+
if (kind.onCrash !== "restart" && kind.onCrash !== "resume") throw new ValidationError(`${who}: onCrash must be "restart" or "resume"`);
|
|
29
|
+
if (kind.budget !== "metered" && kind.budget !== "exempt") throw new ValidationError(`${who}: budget must be "metered" or "exempt"`);
|
|
30
|
+
if (typeof kind.run !== "function") throw new ValidationError(`${who}: run must be a function`);
|
|
31
|
+
return kind;
|
|
32
|
+
}
|
|
33
|
+
/** The handle a graph writes to name this kind. */
|
|
34
|
+
function kindHandle(kind) {
|
|
35
|
+
return {
|
|
36
|
+
id: kind.id,
|
|
37
|
+
version: kind.version
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Narrow a host's effect table to exactly what one kind declared. Anything the kind did not
|
|
42
|
+
* declare is absent — `undefined` on read — so a kind cannot reach past its declaration, and the
|
|
43
|
+
* engine can list a graph's required effects before spending a token.
|
|
44
|
+
*/
|
|
45
|
+
function narrowEffects(declared, provided, context) {
|
|
46
|
+
const out = {};
|
|
47
|
+
const missing = [];
|
|
48
|
+
for (const name of declared) {
|
|
49
|
+
if (!(name in provided)) {
|
|
50
|
+
missing.push(name);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
out[name] = provided[name];
|
|
54
|
+
}
|
|
55
|
+
if (missing.length > 0) throw new ValidationError(`${context}: host provides no effect for ${missing.map((m) => JSON.stringify(m)).join(", ")}; provided: ${Object.keys(provided).sort().join(", ") || "none"}`);
|
|
56
|
+
return Object.freeze(out);
|
|
57
|
+
}
|
|
58
|
+
function isRecord(value) {
|
|
59
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/runtime/graph/registry.ts
|
|
63
|
+
/**
|
|
64
|
+
* `Registry<T>` — the ONE name→thing shape for the graph engine.
|
|
65
|
+
*
|
|
66
|
+
* The kernel grew fourteen of these (agent-runtime#978) that differ in three properties:
|
|
67
|
+
* whether names can be listed, what a miss does, and whether the table is global. This one
|
|
68
|
+
* fixes all three — enumerable, a miss is refused BY NAME listing what is registered, and every
|
|
69
|
+
* registry is per-instance — and it is lifted from `AgentEnvironmentProviderRegistry`, the
|
|
70
|
+
* richest and best-tested of the fourteen, not invented.
|
|
71
|
+
*
|
|
72
|
+
* Entries are addressed by a versioned handle, `<id>/v<n>`, the same way the prompt registry
|
|
73
|
+
* addresses directives. A graph names a kind by handle; a host registers exact versions; a
|
|
74
|
+
* missing version is refused, never served by a newer one.
|
|
75
|
+
*/
|
|
76
|
+
/** `<id>/v<n>` — the only spelling a handle has on the wire, in a journal, or in an error. */
|
|
77
|
+
function formatRegistryHandle(handle) {
|
|
78
|
+
return `${handle.id}/v${handle.version}`;
|
|
79
|
+
}
|
|
80
|
+
/** Parse the wire spelling back. Refuses anything that is not exactly `<id>/v<n>`. */
|
|
81
|
+
function parseRegistryHandle(text, context) {
|
|
82
|
+
const match = /^([A-Za-z0-9][A-Za-z0-9._-]*)\/v(\d+)$/u.exec(text);
|
|
83
|
+
if (!match) throw new ValidationError(`${context}: ${JSON.stringify(text)} is not a registry handle; expected "<id>/v<n>"`);
|
|
84
|
+
const version = Number(match[2]);
|
|
85
|
+
if (!Number.isSafeInteger(version) || version < 1) throw new ValidationError(`${context}: handle version must be a positive integer`);
|
|
86
|
+
return {
|
|
87
|
+
id: match[1],
|
|
88
|
+
version
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a registry. Per-instance by construction: two engines in one process may hold
|
|
93
|
+
* different kind sets, a test is hermetic, and a run can print its own table. There is
|
|
94
|
+
* deliberately no module-level singleton — `builtinShapes` was the one mutable global in the
|
|
95
|
+
* kernel and it had zero tests.
|
|
96
|
+
*/
|
|
97
|
+
function createRegistry(label, seed = []) {
|
|
98
|
+
const table = /* @__PURE__ */ new Map();
|
|
99
|
+
const registry = {
|
|
100
|
+
register(entry, options = {}) {
|
|
101
|
+
if (typeof entry.id !== "string" || entry.id.length === 0) throw new ValidationError(`${label}: an entry must carry a non-empty id`);
|
|
102
|
+
if (!Number.isSafeInteger(entry.version) || entry.version < 1) throw new ValidationError(`${label}: ${JSON.stringify(entry.id)} must carry a positive integer version`);
|
|
103
|
+
const key = formatRegistryHandle(entry);
|
|
104
|
+
if (!options.replace && table.has(key)) throw new ValidationError(`${label}: ${JSON.stringify(key)} is already registered`);
|
|
105
|
+
table.set(key, entry);
|
|
106
|
+
},
|
|
107
|
+
has(handle) {
|
|
108
|
+
return table.has(formatRegistryHandle(handle));
|
|
109
|
+
},
|
|
110
|
+
get(handle) {
|
|
111
|
+
return table.get(formatRegistryHandle(handle));
|
|
112
|
+
},
|
|
113
|
+
require(handle, context = label) {
|
|
114
|
+
const key = formatRegistryHandle(handle);
|
|
115
|
+
const entry = table.get(key);
|
|
116
|
+
if (entry === void 0) {
|
|
117
|
+
const known = registry.names();
|
|
118
|
+
const suffix = known.length > 0 ? `; registered: ${known.join(", ")}` : "; nothing is registered";
|
|
119
|
+
throw new ValidationError(`${context}: ${JSON.stringify(key)} is not registered${suffix}`);
|
|
120
|
+
}
|
|
121
|
+
return entry;
|
|
122
|
+
},
|
|
123
|
+
names() {
|
|
124
|
+
return Array.from(table.keys()).sort();
|
|
125
|
+
},
|
|
126
|
+
entries() {
|
|
127
|
+
return registry.names().map((key) => table.get(key));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
for (const entry of seed) registry.register(entry);
|
|
131
|
+
return registry;
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/runtime/graph/engine.ts
|
|
135
|
+
/**
|
|
136
|
+
* `createGraphEngine` — one engine instance: its kind registry (core kinds pre-registered, host
|
|
137
|
+
* kinds added by the caller), its effect table, and nothing global.
|
|
138
|
+
*
|
|
139
|
+
* The scheduler (#980), journal fold (#981) and the `runGraph` preset (#982) attach here; this
|
|
140
|
+
* file is the part that must exist first so a host can register kinds and a compiler can ask
|
|
141
|
+
* "which effects does this graph need" before a token is spent.
|
|
142
|
+
*/
|
|
143
|
+
/**
|
|
144
|
+
* Build one engine: a kind registry seeded with the core kinds plus the host's, and the host's
|
|
145
|
+
* effect values. Every kind is validated by name at construction, so a malformed host kind fails
|
|
146
|
+
* here, never at its first node.
|
|
147
|
+
*/
|
|
148
|
+
function createGraphEngine(options) {
|
|
149
|
+
const kinds = createRegistry("graph kinds");
|
|
150
|
+
for (const kind of options.coreKinds) kinds.register(validateNodeKind(kind, "createGraphEngine"));
|
|
151
|
+
for (const kind of options.kinds ?? []) kinds.register(validateNodeKind(kind, "createGraphEngine"));
|
|
152
|
+
const effects = Object.freeze({ ...options.effects ?? {} });
|
|
153
|
+
if (Object.keys(effects).some((name) => name.length === 0)) throw new ValidationError("createGraphEngine: an effect name must be non-empty");
|
|
154
|
+
const engine = {
|
|
155
|
+
kinds,
|
|
156
|
+
effects,
|
|
157
|
+
requiredEffects() {
|
|
158
|
+
const names = /* @__PURE__ */ new Set();
|
|
159
|
+
for (const kind of kinds.entries()) for (const name of kind.effects) names.add(name);
|
|
160
|
+
return Array.from(names).sort();
|
|
161
|
+
},
|
|
162
|
+
missingEffects() {
|
|
163
|
+
return engine.requiredEffects().filter((name) => !(name in effects));
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
return engine;
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/runtime/graph/kinds.ts
|
|
170
|
+
/**
|
|
171
|
+
* One profile, one run: the kernel's leaf, exactly as `supervise()` derives it from `backend`.
|
|
172
|
+
* The model cannot decide this — it is what gets run.
|
|
173
|
+
*/
|
|
174
|
+
function agentKind(defaults) {
|
|
175
|
+
return {
|
|
176
|
+
id: "agent",
|
|
177
|
+
version: 1,
|
|
178
|
+
description: "One AgentProfile run as a leaf on a backend; the kernel derives the executor.",
|
|
179
|
+
validateConfig: (raw, context) => {
|
|
180
|
+
const config = asRecord(raw, `${context}: agent config`);
|
|
181
|
+
return {
|
|
182
|
+
...config.backend !== void 0 ? { backend: config.backend } : {},
|
|
183
|
+
...config.deliverable !== void 0 ? { deliverable: config.deliverable } : {}
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
configSchema: {
|
|
187
|
+
type: "object",
|
|
188
|
+
properties: {
|
|
189
|
+
backend: { type: "object" },
|
|
190
|
+
deliverable: { type: "object" }
|
|
191
|
+
},
|
|
192
|
+
additionalProperties: false
|
|
193
|
+
},
|
|
194
|
+
inputs: [],
|
|
195
|
+
outputs: [],
|
|
196
|
+
effects: [],
|
|
197
|
+
onCrash: "restart",
|
|
198
|
+
budget: "metered",
|
|
199
|
+
run: ({ config, profile, spawn }) => {
|
|
200
|
+
const backend = config.backend ?? defaults.backend;
|
|
201
|
+
if (!backend) throw new ValidationError(`agent kind: node ${JSON.stringify(profile.name)} has no backend — set config.backend or the engine default`);
|
|
202
|
+
return workerFromBackend(backend, config.deliverable ?? defaults.deliverable)(profile, spawn);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The thing that DECIDES: a nested `supervisorAgent` with the coordination verbs. Its children
|
|
208
|
+
* are its own tree — the graph sees one node in, one `Settled` out. A `subgraph` constrains what
|
|
209
|
+
* it may spawn; without one it is free-form under `profileSecurity` and `allowedModels`.
|
|
210
|
+
*/
|
|
211
|
+
function supervisorKind(deps) {
|
|
212
|
+
return {
|
|
213
|
+
id: "supervisor",
|
|
214
|
+
version: 1,
|
|
215
|
+
description: "A nested supervisor: spawns, observes, steers and awaits its own children with the coordination verbs.",
|
|
216
|
+
validateConfig: (raw, context) => {
|
|
217
|
+
const config = asRecord(raw, `${context}: supervisor config`);
|
|
218
|
+
return {
|
|
219
|
+
perWorker: asRecord(config.perWorker, `${context}: supervisor config.perWorker`),
|
|
220
|
+
...config.maxLiveWorkers !== void 0 ? { maxLiveWorkers: Number(config.maxLiveWorkers) } : {}
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
configSchema: {
|
|
224
|
+
type: "object",
|
|
225
|
+
properties: {
|
|
226
|
+
perWorker: { type: "object" },
|
|
227
|
+
maxLiveWorkers: { type: "integer" }
|
|
228
|
+
},
|
|
229
|
+
required: ["perWorker"],
|
|
230
|
+
additionalProperties: false
|
|
231
|
+
},
|
|
232
|
+
inputs: [],
|
|
233
|
+
outputs: [],
|
|
234
|
+
effects: [],
|
|
235
|
+
onCrash: "restart",
|
|
236
|
+
budget: "metered",
|
|
237
|
+
run: ({ config, profile }) => supervisorAgent(profile, {
|
|
238
|
+
blobs: deps.blobs,
|
|
239
|
+
makeWorkerAgent: deps.makeWorkerAgent,
|
|
240
|
+
perWorker: config.perWorker,
|
|
241
|
+
...config.maxLiveWorkers !== void 0 ? { maxLiveWorkers: config.maxLiveWorkers } : {},
|
|
242
|
+
...deps.router ? { router: deps.router } : {},
|
|
243
|
+
...deps.driveHarness ? { driveHarness: deps.driveHarness } : {}
|
|
244
|
+
})
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/** The script kind's handle; it also names the kind in every script node's identity. */
|
|
248
|
+
const SCRIPT = {
|
|
249
|
+
id: "script",
|
|
250
|
+
version: 1
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Caller code as a node. The one kind with no kernel primitive behind it: the kernel has no
|
|
254
|
+
* "data→data with no execution" concept (agent-runtime#970 fact-finding), so this is new. It is
|
|
255
|
+
* still a leaf `Agent` carrying an `Executor`, so the journal, the gate and the pool treat it like
|
|
256
|
+
* any other node.
|
|
257
|
+
*/
|
|
258
|
+
function scriptKind() {
|
|
259
|
+
return {
|
|
260
|
+
...SCRIPT,
|
|
261
|
+
description: "Run caller code over the resolved inputs; pure scripts are exempt and restorable.",
|
|
262
|
+
validateConfig: (raw, context) => {
|
|
263
|
+
const config = asRecord(raw, `${context}: script config`);
|
|
264
|
+
if (typeof config.body !== "function") throw new ValidationError(`${context}: script config.body must be a function`);
|
|
265
|
+
if (config.pure !== void 0 && typeof config.pure !== "boolean") throw new ValidationError(`${context}: script config.pure must be a boolean`);
|
|
266
|
+
if (config.pure === true && config.spent !== void 0) throw new ValidationError(`${context}: a pure script is budget-exempt and cannot report spent`);
|
|
267
|
+
return {
|
|
268
|
+
body: config.body,
|
|
269
|
+
...config.pure !== void 0 ? { pure: config.pure } : {},
|
|
270
|
+
...config.spent !== void 0 ? { spent: config.spent } : {}
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
configSchema: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
pure: { type: "boolean" },
|
|
277
|
+
spent: { type: "object" }
|
|
278
|
+
},
|
|
279
|
+
additionalProperties: true
|
|
280
|
+
},
|
|
281
|
+
inputs: [],
|
|
282
|
+
outputs: [],
|
|
283
|
+
effects: [],
|
|
284
|
+
onCrash: "restart",
|
|
285
|
+
budget: "metered",
|
|
286
|
+
run: ({ config, profile, inputs }) => scriptAgent(profile, config, inputs, SCRIPT)
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function scriptAgent(profile, config, inputs, kind) {
|
|
290
|
+
let artifact;
|
|
291
|
+
const spec = {
|
|
292
|
+
profile,
|
|
293
|
+
harness: null,
|
|
294
|
+
executor: {
|
|
295
|
+
runtime: "inline",
|
|
296
|
+
...config.pure ? { budgetExempt: true } : {},
|
|
297
|
+
async execute(_task, signal) {
|
|
298
|
+
const startedAt = Date.now();
|
|
299
|
+
const out = await config.body(inputs, signal);
|
|
300
|
+
const ms = Date.now() - startedAt;
|
|
301
|
+
const spent = config.pure ? {
|
|
302
|
+
iterations: 0,
|
|
303
|
+
tokens: {
|
|
304
|
+
input: 0,
|
|
305
|
+
output: 0
|
|
306
|
+
},
|
|
307
|
+
usd: 0,
|
|
308
|
+
ms
|
|
309
|
+
} : config.spent ?? {
|
|
310
|
+
iterations: 1,
|
|
311
|
+
tokens: {
|
|
312
|
+
input: 0,
|
|
313
|
+
output: 0,
|
|
314
|
+
tokensKnown: false
|
|
315
|
+
},
|
|
316
|
+
tokensKnown: false,
|
|
317
|
+
usd: 0,
|
|
318
|
+
usdKnown: false,
|
|
319
|
+
ms
|
|
320
|
+
};
|
|
321
|
+
artifact = {
|
|
322
|
+
outRef: contentAddress(out),
|
|
323
|
+
out,
|
|
324
|
+
spent
|
|
325
|
+
};
|
|
326
|
+
return artifact;
|
|
327
|
+
},
|
|
328
|
+
teardown: () => Promise.resolve({ destroyed: true }),
|
|
329
|
+
resultArtifact() {
|
|
330
|
+
if (!artifact) throw new ValidationError("script: resultArtifact() read before execute()");
|
|
331
|
+
return artifact;
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
execution: { correlation: { nodeKind: formatRegistryHandle(kind) } }
|
|
335
|
+
};
|
|
336
|
+
return {
|
|
337
|
+
name: profile.name ?? "script",
|
|
338
|
+
act: () => Promise.reject(new ValidationError("script: act() is not the execution path")),
|
|
339
|
+
executorSpec: spec
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* A node carrying its own graph: the constraint on what a supervisor may spawn at depth>1. Its
|
|
344
|
+
* executor is a nested engine run; that needs the scheduler (#980), so until then this kind is
|
|
345
|
+
* registered and REFUSES at run time by name rather than being absent — a graph that names it
|
|
346
|
+
* compiles, and the refusal says exactly what is missing.
|
|
347
|
+
*/
|
|
348
|
+
function subgraphKind() {
|
|
349
|
+
return {
|
|
350
|
+
id: "subgraph",
|
|
351
|
+
version: 1,
|
|
352
|
+
description: "A node that runs its own graph; constrains a supervisor at depth>1.",
|
|
353
|
+
validateConfig: (raw, context) => {
|
|
354
|
+
const config = asRecord(raw, `${context}: subgraph config`);
|
|
355
|
+
if (config.graph === void 0) throw new ValidationError(`${context}: subgraph config.graph is required`);
|
|
356
|
+
return { graph: config.graph };
|
|
357
|
+
},
|
|
358
|
+
configSchema: {
|
|
359
|
+
type: "object",
|
|
360
|
+
properties: { graph: { type: "object" } },
|
|
361
|
+
required: ["graph"]
|
|
362
|
+
},
|
|
363
|
+
inputs: [],
|
|
364
|
+
outputs: [],
|
|
365
|
+
effects: [],
|
|
366
|
+
onCrash: "restart",
|
|
367
|
+
budget: "metered",
|
|
368
|
+
run: ({ profile }) => {
|
|
369
|
+
throw new ValidationError(`subgraph kind: node ${JSON.stringify(profile.name)} cannot run yet — the scheduler that executes a nested graph lands in agent-runtime#980`);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function asRecord(value, context) {
|
|
374
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ValidationError(`${context} must be an object`);
|
|
375
|
+
return value;
|
|
376
|
+
}
|
|
377
|
+
//#endregion
|
|
378
|
+
export { agentKind, createGraphEngine, createRegistry, formatRegistryHandle, kindHandle, narrowEffects, parseRegistryHandle, scriptKind, subgraphKind, supervisorKind, validateNodeKind };
|
|
379
|
+
|
|
380
|
+
//# sourceMappingURL=graph.js.map
|