pi-loop-graph-sdk 0.2.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/LICENSE +21 -0
- package/README-zh.md +416 -0
- package/README.md +414 -0
- package/ROADMAP.md +60 -0
- package/dist/adapter/complete-tool.d.ts +3 -0
- package/dist/adapter/complete-tool.js +53 -0
- package/dist/adapter/debug-log.d.ts +38 -0
- package/dist/adapter/debug-log.js +151 -0
- package/dist/adapter/extension.d.ts +2 -0
- package/dist/adapter/extension.js +11 -0
- package/dist/adapter/graph-execution-host.d.ts +73 -0
- package/dist/adapter/graph-execution-host.js +181 -0
- package/dist/adapter/isolated-graph-session.d.ts +75 -0
- package/dist/adapter/isolated-graph-session.js +313 -0
- package/dist/adapter/loop-graph-extension.d.ts +96 -0
- package/dist/adapter/loop-graph-extension.js +487 -0
- package/dist/adapter/mechanism-runtime.d.ts +97 -0
- package/dist/adapter/mechanism-runtime.js +670 -0
- package/dist/adapter/model-messages.d.ts +17 -0
- package/dist/adapter/model-messages.js +11 -0
- package/dist/adapter/observability.d.ts +88 -0
- package/dist/adapter/observability.js +31 -0
- package/dist/adapter/output-contract.d.ts +12 -0
- package/dist/adapter/output-contract.js +87 -0
- package/dist/adapter/pi-node-context.d.ts +132 -0
- package/dist/adapter/pi-node-context.js +619 -0
- package/dist/adapter/projection.d.ts +121 -0
- package/dist/adapter/projection.js +169 -0
- package/dist/adapter/skill-content.d.ts +16 -0
- package/dist/adapter/skill-content.js +16 -0
- package/dist/advanced.d.ts +32 -0
- package/dist/advanced.js +17 -0
- package/dist/builders/graph.d.ts +27 -0
- package/dist/builders/graph.js +39 -0
- package/dist/builders/node.d.ts +8 -0
- package/dist/builders/node.js +9 -0
- package/dist/builders/refs.d.ts +5 -0
- package/dist/builders/refs.js +10 -0
- package/dist/builders/route.d.ts +11 -0
- package/dist/builders/route.js +18 -0
- package/dist/core/context.d.ts +73 -0
- package/dist/core/context.js +229 -0
- package/dist/core/graph.d.ts +172 -0
- package/dist/core/graph.js +57 -0
- package/dist/core/json.d.ts +8 -0
- package/dist/core/json.js +46 -0
- package/dist/core/limits.d.ts +7 -0
- package/dist/core/limits.js +14 -0
- package/dist/core/mechanism.d.ts +88 -0
- package/dist/core/mechanism.js +5 -0
- package/dist/core/result.d.ts +41 -0
- package/dist/core/result.js +1 -0
- package/dist/core/schema.d.ts +8 -0
- package/dist/core/schema.js +23 -0
- package/dist/core/skill.d.ts +12 -0
- package/dist/core/skill.js +1 -0
- package/dist/host/baseline.d.ts +11 -0
- package/dist/host/baseline.js +4 -0
- package/dist/host/graph-catalog.d.ts +8 -0
- package/dist/host/graph-catalog.js +24 -0
- package/dist/host/graph-host.d.ts +53 -0
- package/dist/host/graph-host.js +181 -0
- package/dist/host/preflight.d.ts +17 -0
- package/dist/host/preflight.js +81 -0
- package/dist/host/skill-catalog.d.ts +24 -0
- package/dist/host/skill-catalog.js +92 -0
- package/dist/host/tool-catalog.d.ts +27 -0
- package/dist/host/tool-catalog.js +33 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +10 -0
- package/dist/replay/checkpoint.d.ts +40 -0
- package/dist/replay/checkpoint.js +57 -0
- package/dist/replay/events.d.ts +40 -0
- package/dist/replay/events.js +1 -0
- package/dist/replay/finalizer.d.ts +26 -0
- package/dist/replay/finalizer.js +117 -0
- package/dist/replay/html.d.ts +3 -0
- package/dist/replay/html.js +270 -0
- package/dist/replay/index.d.ts +13 -0
- package/dist/replay/index.js +7 -0
- package/dist/replay/model.d.ts +81 -0
- package/dist/replay/model.js +1 -0
- package/dist/replay/parser.d.ts +3 -0
- package/dist/replay/parser.js +332 -0
- package/dist/replay/recorder.d.ts +30 -0
- package/dist/replay/recorder.js +195 -0
- package/dist/replay/store.d.ts +41 -0
- package/dist/replay/store.js +94 -0
- package/dist/router.d.ts +4 -0
- package/dist/router.js +61 -0
- package/dist/runtime/event-bus.d.ts +101 -0
- package/dist/runtime/event-bus.js +18 -0
- package/dist/runtime/graph-runtime.d.ts +173 -0
- package/dist/runtime/graph-runtime.js +1293 -0
- package/dist/runtime/invocation-budget.d.ts +22 -0
- package/dist/runtime/invocation-budget.js +52 -0
- package/dist/runtime/mechanism-runtime.d.ts +92 -0
- package/dist/runtime/mechanism-runtime.js +387 -0
- package/dist/runtime.d.ts +91 -0
- package/dist/runtime.js +258 -0
- package/dist/tools-resolve.d.ts +20 -0
- package/dist/tools-resolve.js +52 -0
- package/dist/type.d.ts +593 -0
- package/dist/type.js +30 -0
- package/dist/validate.d.ts +25 -0
- package/dist/validate.js +203 -0
- package/package.json +69 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/** Canonical, scope-owned context state for one Graph Invocation. */
|
|
2
|
+
export class ContextState {
|
|
3
|
+
options;
|
|
4
|
+
graphLayers = Object.freeze([]);
|
|
5
|
+
contributions = new Map();
|
|
6
|
+
frameRevision;
|
|
7
|
+
memoryCache = null;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.frameRevision = options.frameRevision ?? { value: 0 };
|
|
11
|
+
}
|
|
12
|
+
async initialize() {
|
|
13
|
+
const { graph, graphInput, graphSkills } = this.options;
|
|
14
|
+
const background = await materializeProjection(graph.context.background, graphInput, {
|
|
15
|
+
graph: { id: graph.id, version: graph.version, goal: graph.goal },
|
|
16
|
+
skills: graphSkills,
|
|
17
|
+
}, defaultGraphRenderer);
|
|
18
|
+
this.graphLayers = Object.freeze([
|
|
19
|
+
...(this.options.hostContent == null ? [] : [{
|
|
20
|
+
name: "host",
|
|
21
|
+
scopeId: this.options.rootRunId,
|
|
22
|
+
retention: "sticky",
|
|
23
|
+
content: this.options.hostContent,
|
|
24
|
+
}]),
|
|
25
|
+
...(background == null ? [] : [{
|
|
26
|
+
name: "graph",
|
|
27
|
+
scopeId: this.options.graphInvocationId,
|
|
28
|
+
retention: "sticky",
|
|
29
|
+
content: background,
|
|
30
|
+
}]),
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
bumpMemoryRevision() {
|
|
34
|
+
this.frameRevision.value += 1;
|
|
35
|
+
}
|
|
36
|
+
async materializeNode(nodeVisitId, stageId, stage, nodeInput, nodeSkills) {
|
|
37
|
+
const memory = await this.materializeMemory();
|
|
38
|
+
const nodeLayer = stage.node.kind === "graph"
|
|
39
|
+
? null
|
|
40
|
+
: await materializeNodeLayer(stage.node, stage, nodeInput, nodeSkills, nodeVisitId);
|
|
41
|
+
const layers = [...this.graphLayers];
|
|
42
|
+
if (memory != null) {
|
|
43
|
+
layers.push({
|
|
44
|
+
name: "memory",
|
|
45
|
+
scopeId: this.options.graphInvocationId,
|
|
46
|
+
retention: "foldable",
|
|
47
|
+
content: memory,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (nodeLayer)
|
|
51
|
+
layers.push(nodeLayer);
|
|
52
|
+
return {
|
|
53
|
+
nodeVisitId,
|
|
54
|
+
stageId,
|
|
55
|
+
snapshot: this.snapshot(nodeVisitId, layers),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
snapshot(nodeVisitId, layers = this.graphLayers) {
|
|
59
|
+
const contributions = [
|
|
60
|
+
...(this.options.externalContributions?.(nodeVisitId) ?? []),
|
|
61
|
+
...this.contributions.values(),
|
|
62
|
+
];
|
|
63
|
+
const mechanismLayers = ["sticky", "foldable", "transient"].flatMap((retention) => {
|
|
64
|
+
const content = contributions
|
|
65
|
+
.filter((item) => item.retention === retention)
|
|
66
|
+
.flatMap((item) => typeof item.content === "string"
|
|
67
|
+
? [{ type: "text", text: item.content }]
|
|
68
|
+
: item.content);
|
|
69
|
+
return content.length === 0 ? [] : [{
|
|
70
|
+
name: "mechanism",
|
|
71
|
+
scopeId: nodeVisitId ?? this.options.graphInvocationId,
|
|
72
|
+
retention,
|
|
73
|
+
content: Object.freeze(content.map((block) => Object.freeze({ ...block }))),
|
|
74
|
+
}];
|
|
75
|
+
});
|
|
76
|
+
const projectedLayers = [...layers, ...mechanismLayers];
|
|
77
|
+
return Object.freeze({
|
|
78
|
+
rootRunId: this.options.rootRunId,
|
|
79
|
+
graphInvocationId: this.options.graphInvocationId,
|
|
80
|
+
nodeVisitId,
|
|
81
|
+
graphId: this.options.graph.id,
|
|
82
|
+
graphVersion: this.options.graph.version,
|
|
83
|
+
memoryRevision: this.frameRevision.value,
|
|
84
|
+
layers: Object.freeze(projectedLayers.map((layer) => Object.freeze({ ...layer }))),
|
|
85
|
+
contributions: Object.freeze(contributions.map((item) => Object.freeze({ ...item }))),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
refreshSnapshot(snapshot, agentRunId) {
|
|
89
|
+
return Object.freeze({
|
|
90
|
+
...this.snapshot(snapshot.nodeVisitId, snapshot.layers.filter((layer) => layer.name !== "mechanism")),
|
|
91
|
+
...(agentRunId === undefined ? {} : { agentRunId }),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
addContribution(contribution) {
|
|
95
|
+
if (this.contributions.has(contribution.id))
|
|
96
|
+
throw new Error(`Context contribution already exists: ${contribution.id}`);
|
|
97
|
+
this.contributions.set(contribution.id, freezeContribution(contribution));
|
|
98
|
+
let active = true;
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
id: contribution.id,
|
|
101
|
+
update: (content) => {
|
|
102
|
+
if (!active)
|
|
103
|
+
throw new Error(`Context contribution is disposed: ${contribution.id}`);
|
|
104
|
+
const current = this.contributions.get(contribution.id);
|
|
105
|
+
if (!current)
|
|
106
|
+
throw new Error(`Context contribution is unavailable: ${contribution.id}`);
|
|
107
|
+
this.contributions.set(contribution.id, freezeContribution({ ...current, content }));
|
|
108
|
+
},
|
|
109
|
+
dispose: () => {
|
|
110
|
+
if (!active)
|
|
111
|
+
return;
|
|
112
|
+
active = false;
|
|
113
|
+
this.contributions.delete(contribution.id);
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async materializeMemory() {
|
|
118
|
+
const projection = this.options.graph.context.memory ?? { select: "all" };
|
|
119
|
+
if (this.memoryCache?.revision === this.frameRevision.value)
|
|
120
|
+
return this.memoryCache.content;
|
|
121
|
+
const content = await materializeProjection(projection, this.options.frames, {
|
|
122
|
+
graph: { id: this.options.graph.id, version: this.options.graph.version },
|
|
123
|
+
revision: this.frameRevision.value,
|
|
124
|
+
}, defaultMemoryRenderer);
|
|
125
|
+
this.memoryCache = { revision: this.frameRevision.value, content };
|
|
126
|
+
return content;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function freezeContribution(contribution) {
|
|
130
|
+
const content = normalizeContextContent(contribution.content);
|
|
131
|
+
if (content == null)
|
|
132
|
+
throw new Error("Context contribution content cannot be empty");
|
|
133
|
+
return Object.freeze({ ...contribution, content });
|
|
134
|
+
}
|
|
135
|
+
async function materializeNodeLayer(node, stage, nodeInput, skills, nodeVisitId) {
|
|
136
|
+
const projection = node.context?.focus ?? (node.kind === "agent"
|
|
137
|
+
? { select: "all" }
|
|
138
|
+
: { select: "none" });
|
|
139
|
+
const content = await materializeProjection(projection, nodeInput, {
|
|
140
|
+
node: {
|
|
141
|
+
kind: node.kind,
|
|
142
|
+
subGoal: node.subGoal,
|
|
143
|
+
identity: node.identity,
|
|
144
|
+
},
|
|
145
|
+
skills,
|
|
146
|
+
connections: stage.route.connections.map((connection) => ({
|
|
147
|
+
id: connection.id,
|
|
148
|
+
to: connection.to,
|
|
149
|
+
})),
|
|
150
|
+
}, defaultNodeRenderer);
|
|
151
|
+
return content == null ? null : {
|
|
152
|
+
name: "node",
|
|
153
|
+
scopeId: nodeVisitId,
|
|
154
|
+
retention: "sticky",
|
|
155
|
+
content,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export async function materializeProjection(projection, source, meta, fallback) {
|
|
159
|
+
const selected = projection.select === "all"
|
|
160
|
+
? source
|
|
161
|
+
: projection.select === "none"
|
|
162
|
+
? null
|
|
163
|
+
: await projection.select(source);
|
|
164
|
+
const frozen = selected == null ? null : deepFreeze(cloneJson(selected));
|
|
165
|
+
const rendered = projection.render
|
|
166
|
+
? await projection.render({ selected: frozen, meta: Object.freeze(meta) })
|
|
167
|
+
: fallback({ selected: frozen, meta: Object.freeze(meta) });
|
|
168
|
+
return normalizeContextContent(rendered);
|
|
169
|
+
}
|
|
170
|
+
function defaultGraphRenderer(input) {
|
|
171
|
+
const lines = [`=== GRAPH GOAL ===\n${input.meta.graph.goal}`];
|
|
172
|
+
if (input.selected != null)
|
|
173
|
+
lines.push(`=== BACKGROUND ===\n${JSON.stringify(input.selected)}`);
|
|
174
|
+
for (const skill of input.meta.skills)
|
|
175
|
+
lines.push(skill.content);
|
|
176
|
+
return lines.join("\n");
|
|
177
|
+
}
|
|
178
|
+
function defaultMemoryRenderer(input) {
|
|
179
|
+
return input.selected == null || Array.isArray(input.selected) && input.selected.length === 0
|
|
180
|
+
? null
|
|
181
|
+
: `=== COMPLETED WORK ===\n${JSON.stringify(input.selected)}`;
|
|
182
|
+
}
|
|
183
|
+
function defaultNodeRenderer(input) {
|
|
184
|
+
const lines = [
|
|
185
|
+
`=== NODE SUBGOAL ===\n${input.meta.node.subGoal}`,
|
|
186
|
+
];
|
|
187
|
+
if (input.selected != null)
|
|
188
|
+
lines.push(`=== NODE FOCUS ===\n${JSON.stringify(input.selected)}`);
|
|
189
|
+
if (input.meta.connections.length)
|
|
190
|
+
lines.push(`=== CONNECTIONS ===\n${JSON.stringify(input.meta.connections)}`);
|
|
191
|
+
for (const skill of input.meta.skills)
|
|
192
|
+
lines.push(skill.content);
|
|
193
|
+
return lines.join("\n");
|
|
194
|
+
}
|
|
195
|
+
function normalizeContextContent(content) {
|
|
196
|
+
if (content == null)
|
|
197
|
+
return null;
|
|
198
|
+
const values = Array.isArray(content) ? content : [content];
|
|
199
|
+
const blocks = [];
|
|
200
|
+
for (const value of values) {
|
|
201
|
+
if (typeof value === "string")
|
|
202
|
+
blocks.push({ type: "text", text: value });
|
|
203
|
+
else if (isContextBlock(value))
|
|
204
|
+
blocks.push(Object.freeze({ ...value }));
|
|
205
|
+
else
|
|
206
|
+
throw new Error("Context renderer returned invalid content");
|
|
207
|
+
}
|
|
208
|
+
return Object.freeze(blocks.length === 1 && blocks[0].type === "text" ? blocks[0].text : blocks);
|
|
209
|
+
}
|
|
210
|
+
function isContextBlock(value) {
|
|
211
|
+
if (!value || typeof value !== "object")
|
|
212
|
+
return false;
|
|
213
|
+
const block = value;
|
|
214
|
+
return block.type === "text" && typeof block.text === "string"
|
|
215
|
+
|| block.type === "image" && typeof block.data === "string" && typeof block.mimeType === "string";
|
|
216
|
+
}
|
|
217
|
+
function deepFreeze(value, seen = new WeakSet()) {
|
|
218
|
+
if (!value || typeof value !== "object")
|
|
219
|
+
return value;
|
|
220
|
+
if (seen.has(value))
|
|
221
|
+
return value;
|
|
222
|
+
seen.add(value);
|
|
223
|
+
for (const child of Object.values(value))
|
|
224
|
+
deepFreeze(child, seen);
|
|
225
|
+
return Object.freeze(value);
|
|
226
|
+
}
|
|
227
|
+
function cloneJson(value) {
|
|
228
|
+
return JSON.parse(JSON.stringify(value));
|
|
229
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { TSchema, Static } from "typebox";
|
|
2
|
+
import type { JsonSchema, JsonValue } from "./json.js";
|
|
3
|
+
import type { ResolvedSkillView, SkillRef } from "./skill.js";
|
|
4
|
+
import type { Mechanism } from "./mechanism.js";
|
|
5
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
6
|
+
export type SchemaValue<S extends TSchema> = Static<S>;
|
|
7
|
+
export interface GraphRef {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly version: string;
|
|
10
|
+
}
|
|
11
|
+
export interface NodeIdentity {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly version?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ContextBlockText {
|
|
16
|
+
readonly type: "text";
|
|
17
|
+
readonly text: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ContextBlockImage {
|
|
20
|
+
readonly type: "image";
|
|
21
|
+
readonly data: string;
|
|
22
|
+
readonly mimeType: string;
|
|
23
|
+
}
|
|
24
|
+
export type ContextBlock = ContextBlockText | ContextBlockImage;
|
|
25
|
+
export type ContextContent = string | readonly ContextBlock[];
|
|
26
|
+
export type ContextSelector<TSource, TSelected extends JsonValue> = "all" | "none" | ((source: Readonly<TSource>) => Awaitable<TSelected | null>);
|
|
27
|
+
export interface ContextRenderInput<TSelected extends JsonValue, TMeta> {
|
|
28
|
+
readonly selected: Readonly<TSelected> | null;
|
|
29
|
+
readonly meta: Readonly<TMeta>;
|
|
30
|
+
}
|
|
31
|
+
export type ContextRenderer<TSelected extends JsonValue, TMeta> = (input: ContextRenderInput<TSelected, TMeta>) => Awaitable<ContextContent | readonly ContextContent[] | null>;
|
|
32
|
+
export interface ContextProjection<TSource, TSelected extends JsonValue, TMeta> {
|
|
33
|
+
readonly select: ContextSelector<TSource, TSelected>;
|
|
34
|
+
readonly render?: ContextRenderer<TSelected, TMeta>;
|
|
35
|
+
}
|
|
36
|
+
export interface GraphContextMeta {
|
|
37
|
+
readonly graph: {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly version: string;
|
|
40
|
+
readonly goal: string;
|
|
41
|
+
};
|
|
42
|
+
readonly skills: readonly ResolvedSkillView[];
|
|
43
|
+
}
|
|
44
|
+
export interface GraphMemoryMeta {
|
|
45
|
+
readonly graph: {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly version: string;
|
|
48
|
+
};
|
|
49
|
+
readonly revision: number;
|
|
50
|
+
}
|
|
51
|
+
export interface NodeContextMeta {
|
|
52
|
+
readonly node: {
|
|
53
|
+
readonly kind: "agent" | "code" | "graph";
|
|
54
|
+
readonly subGoal: string;
|
|
55
|
+
readonly identity?: NodeIdentity;
|
|
56
|
+
};
|
|
57
|
+
readonly skills: readonly ResolvedSkillView[];
|
|
58
|
+
readonly connections: readonly ConnectionView[];
|
|
59
|
+
}
|
|
60
|
+
export interface ConnectionView {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly to: string | "__graph_finish__";
|
|
63
|
+
}
|
|
64
|
+
export interface GraphContextDefinition<TInput, TBackground extends JsonValue, TMemory extends JsonValue> {
|
|
65
|
+
readonly background: ContextProjection<TInput, TBackground, GraphContextMeta>;
|
|
66
|
+
readonly memory?: ContextProjection<readonly JsonValue[], TMemory, GraphMemoryMeta>;
|
|
67
|
+
}
|
|
68
|
+
export interface NodeContextDefinition<TInput, TFocus extends JsonValue = any> {
|
|
69
|
+
readonly focus: ContextProjection<TInput, TFocus, NodeContextMeta>;
|
|
70
|
+
}
|
|
71
|
+
export interface NodeCompletion<TResult = JsonValue> {
|
|
72
|
+
readonly result: TResult;
|
|
73
|
+
}
|
|
74
|
+
export interface CodeNodeExecution<TInput, TResult> {
|
|
75
|
+
readonly input: Readonly<TInput>;
|
|
76
|
+
readonly complete: (result: TResult) => TResult;
|
|
77
|
+
readonly runAgent: (request: AgentRunRequest) => Promise<NodeCompletion>;
|
|
78
|
+
/** Present during resume; use for idempotent-side-effect guards. */
|
|
79
|
+
readonly resumeAttempt?: number;
|
|
80
|
+
readonly nodeVisitId?: string;
|
|
81
|
+
readonly rootRunId?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface AgentRunRequest {
|
|
84
|
+
readonly prompt: string;
|
|
85
|
+
readonly output?: JsonSchema;
|
|
86
|
+
}
|
|
87
|
+
export interface AgentNodeDefinition<TInputSchema extends TSchema = TSchema, TResultSchema extends TSchema = TSchema, TFocus extends JsonValue = any> {
|
|
88
|
+
readonly kind: "agent";
|
|
89
|
+
readonly subGoal: string;
|
|
90
|
+
readonly input: JsonSchema<TInputSchema>;
|
|
91
|
+
readonly output: JsonSchema<TResultSchema>;
|
|
92
|
+
readonly identity?: NodeIdentity;
|
|
93
|
+
readonly context?: NodeContextDefinition<SchemaValue<TInputSchema>, TFocus>;
|
|
94
|
+
readonly tools?: readonly string[] | "all";
|
|
95
|
+
readonly skills?: readonly SkillRef[];
|
|
96
|
+
readonly prompt?: string;
|
|
97
|
+
readonly mechanisms?: readonly Mechanism[];
|
|
98
|
+
}
|
|
99
|
+
export interface CodeNodeDefinition<TInputSchema extends TSchema = TSchema, TResultSchema extends TSchema = TSchema, TFocus extends JsonValue = any> {
|
|
100
|
+
readonly kind: "code";
|
|
101
|
+
readonly subGoal: string;
|
|
102
|
+
readonly input: JsonSchema<TInputSchema>;
|
|
103
|
+
readonly output: JsonSchema<TResultSchema>;
|
|
104
|
+
readonly identity?: NodeIdentity;
|
|
105
|
+
readonly context?: NodeContextDefinition<SchemaValue<TInputSchema>, TFocus>;
|
|
106
|
+
readonly tools?: readonly string[] | "all";
|
|
107
|
+
readonly skills?: readonly SkillRef[];
|
|
108
|
+
readonly execute: (execution: CodeNodeExecution<SchemaValue<TInputSchema>, SchemaValue<TResultSchema>>) => Awaitable<SchemaValue<TResultSchema>>;
|
|
109
|
+
readonly mechanisms?: readonly Mechanism[];
|
|
110
|
+
}
|
|
111
|
+
export interface GraphNodeDefinition<TInputSchema extends TSchema = TSchema, TResultSchema extends TSchema = TSchema> {
|
|
112
|
+
readonly kind: "graph";
|
|
113
|
+
readonly subGoal: string;
|
|
114
|
+
readonly input: JsonSchema<TInputSchema>;
|
|
115
|
+
readonly output: JsonSchema<TResultSchema>;
|
|
116
|
+
readonly identity?: NodeIdentity;
|
|
117
|
+
readonly graph: GraphRef;
|
|
118
|
+
readonly boundary: "call" | "compose" | "delegate";
|
|
119
|
+
readonly skills?: readonly SkillRef[];
|
|
120
|
+
readonly mechanisms?: readonly Mechanism[];
|
|
121
|
+
}
|
|
122
|
+
export type NodeDefinition = AgentNodeDefinition<any, any, any> | CodeNodeDefinition<any, any, any> | GraphNodeDefinition<any, any>;
|
|
123
|
+
export interface Entry<TInput = JsonValue> {
|
|
124
|
+
readonly id: string;
|
|
125
|
+
readonly to: string;
|
|
126
|
+
readonly guard?: (input: Readonly<TInput>) => Awaitable<boolean>;
|
|
127
|
+
readonly mapInput?: (input: Readonly<TInput>) => JsonValue;
|
|
128
|
+
}
|
|
129
|
+
export interface Transition<TCompletion extends JsonValue = JsonValue, TFrame extends JsonValue = JsonValue, TInput extends JsonValue = JsonValue> {
|
|
130
|
+
readonly guard?: (input: Readonly<TCompletion>) => Awaitable<boolean>;
|
|
131
|
+
readonly frame?: (input: {
|
|
132
|
+
readonly completion: Readonly<NodeCompletion<TCompletion>>;
|
|
133
|
+
}) => TFrame;
|
|
134
|
+
readonly map?: (input: {
|
|
135
|
+
readonly completion: Readonly<NodeCompletion<TCompletion>>;
|
|
136
|
+
}) => TInput;
|
|
137
|
+
readonly output?: (input: {
|
|
138
|
+
readonly completion: Readonly<NodeCompletion<TCompletion>>;
|
|
139
|
+
}) => JsonValue;
|
|
140
|
+
}
|
|
141
|
+
export type ContextFrame = JsonValue;
|
|
142
|
+
export interface Connection {
|
|
143
|
+
readonly id: string;
|
|
144
|
+
readonly to: string | "__graph_finish__";
|
|
145
|
+
readonly transition: Transition;
|
|
146
|
+
}
|
|
147
|
+
export interface Route {
|
|
148
|
+
readonly kind: "first-match" | "priority-first" | "agent-choice" | "custom";
|
|
149
|
+
readonly connections: readonly Connection[];
|
|
150
|
+
}
|
|
151
|
+
export interface Stage {
|
|
152
|
+
readonly node: NodeDefinition;
|
|
153
|
+
readonly route: Route;
|
|
154
|
+
}
|
|
155
|
+
export type GraphToolPolicy = readonly string[];
|
|
156
|
+
export interface GraphDefinition<TInputSchema extends TSchema = TSchema, TOutputSchema extends TSchema = TSchema, TBackground extends JsonValue = any, TMemory extends JsonValue = any> {
|
|
157
|
+
readonly id: string;
|
|
158
|
+
readonly version: string;
|
|
159
|
+
readonly goal: string;
|
|
160
|
+
readonly input: JsonSchema<TInputSchema>;
|
|
161
|
+
readonly output: JsonSchema<TOutputSchema>;
|
|
162
|
+
readonly context: GraphContextDefinition<SchemaValue<TInputSchema>, TBackground, TMemory>;
|
|
163
|
+
readonly entries: readonly Entry<SchemaValue<TInputSchema>>[];
|
|
164
|
+
readonly stages: Readonly<Record<string, Stage>>;
|
|
165
|
+
readonly tools?: GraphToolPolicy;
|
|
166
|
+
readonly skills?: readonly SkillRef[];
|
|
167
|
+
readonly mechanisms?: readonly Mechanism[];
|
|
168
|
+
}
|
|
169
|
+
export type Graph<TInputSchema extends TSchema = TSchema, TOutputSchema extends TSchema = TSchema, TBackground extends JsonValue = any, TMemory extends JsonValue = any> = GraphDefinition<TInputSchema, TOutputSchema, TBackground, TMemory>;
|
|
170
|
+
export declare function graphRef(id: string, version: string): GraphRef;
|
|
171
|
+
export declare function defineGraph<TInputSchema extends TSchema, TOutputSchema extends TSchema, TBackground extends JsonValue, TMemory extends JsonValue = JsonValue>(graph: GraphDefinition<TInputSchema, TOutputSchema, TBackground, TMemory>): Graph<TInputSchema, TOutputSchema, TBackground, TMemory>;
|
|
172
|
+
export declare function validateGraphDefinition<TInputSchema extends TSchema, TOutputSchema extends TSchema, TBackground extends JsonValue, TMemory extends JsonValue>(graph: GraphDefinition<TInputSchema, TOutputSchema, TBackground, TMemory>): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function graphRef(id, version) {
|
|
2
|
+
if (!id || !version)
|
|
3
|
+
throw new Error("GraphRef requires id and version");
|
|
4
|
+
return Object.freeze({ id, version });
|
|
5
|
+
}
|
|
6
|
+
export function defineGraph(graph) {
|
|
7
|
+
validateGraphDefinition(graph);
|
|
8
|
+
const stages = Object.fromEntries(Object.entries(graph.stages).map(([id, stage]) => [
|
|
9
|
+
id,
|
|
10
|
+
Object.freeze({
|
|
11
|
+
...stage,
|
|
12
|
+
route: Object.freeze({
|
|
13
|
+
...stage.route,
|
|
14
|
+
connections: Object.freeze(stage.route.connections.map((connection) => Object.freeze({
|
|
15
|
+
...connection,
|
|
16
|
+
transition: Object.freeze({ ...connection.transition }),
|
|
17
|
+
}))),
|
|
18
|
+
}),
|
|
19
|
+
}),
|
|
20
|
+
]));
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
...graph,
|
|
23
|
+
stages: Object.freeze(stages),
|
|
24
|
+
entries: Object.freeze(graph.entries.map((item) => Object.freeze({ ...item }))),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function validateGraphDefinition(graph) {
|
|
28
|
+
if (!graph.id || !graph.version || !graph.goal)
|
|
29
|
+
throw new Error("Graph requires id, version, and goal");
|
|
30
|
+
if (!graph.entries.length)
|
|
31
|
+
throw new Error("Graph requires at least one entry");
|
|
32
|
+
const entryIds = new Set();
|
|
33
|
+
for (const entry of graph.entries) {
|
|
34
|
+
if (entryIds.has(entry.id))
|
|
35
|
+
throw new Error(`Duplicate Entry ID: ${entry.id}`);
|
|
36
|
+
entryIds.add(entry.id);
|
|
37
|
+
}
|
|
38
|
+
for (const [stageId, stage] of Object.entries(graph.stages)) {
|
|
39
|
+
if (!stageId)
|
|
40
|
+
throw new Error("Stage ID cannot be empty");
|
|
41
|
+
const connectionIds = new Set();
|
|
42
|
+
for (const connection of stage.route.connections) {
|
|
43
|
+
if (connectionIds.has(connection.id))
|
|
44
|
+
throw new Error(`Duplicate Connection ID in Stage "${stageId}": ${connection.id}`);
|
|
45
|
+
connectionIds.add(connection.id);
|
|
46
|
+
if (connection.to !== "__graph_finish__" && !(connection.to in graph.stages)) {
|
|
47
|
+
throw new Error(`Connection "${connection.id}" targets missing Stage "${connection.to}"`);
|
|
48
|
+
}
|
|
49
|
+
if (connection.to === "__graph_finish__" && !connection.transition.output) {
|
|
50
|
+
throw new Error(`Finish Connection "${connection.id}" requires an explicit output mapper`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const entry of graph.entries)
|
|
55
|
+
if (!(entry.to in graph.stages))
|
|
56
|
+
throw new Error(`Entry "${entry.id}" targets missing Stage "${entry.to}"`);
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TSchema, Static } from "typebox";
|
|
2
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
3
|
+
export type JsonValue = JsonPrimitive | readonly JsonValue[] | {
|
|
4
|
+
readonly [key: string]: JsonValue;
|
|
5
|
+
};
|
|
6
|
+
export type JsonSchema<T extends TSchema = TSchema> = T;
|
|
7
|
+
export type JsonSchemaValue<T extends TSchema> = Static<T> & JsonValue;
|
|
8
|
+
export declare function isJsonValue(value: unknown): value is JsonValue;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function isJsonValue(value) {
|
|
2
|
+
return isJsonValueInternal(value, new WeakSet());
|
|
3
|
+
}
|
|
4
|
+
function isJsonValueInternal(value, ancestors) {
|
|
5
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
6
|
+
return true;
|
|
7
|
+
if (typeof value === "number")
|
|
8
|
+
return Number.isFinite(value);
|
|
9
|
+
if (typeof value !== "object")
|
|
10
|
+
return false;
|
|
11
|
+
if (ancestors.has(value))
|
|
12
|
+
return false;
|
|
13
|
+
ancestors.add(value);
|
|
14
|
+
try {
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
if (Object.getOwnPropertySymbols(value).length > 0)
|
|
17
|
+
return false;
|
|
18
|
+
if (Object.getOwnPropertyNames(value).length !== value.length + 1)
|
|
19
|
+
return false;
|
|
20
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
21
|
+
if (!Object.hasOwn(value, index) || !isJsonValueInternal(value[index], ancestors))
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const prototype = Object.getPrototypeOf(value);
|
|
27
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
28
|
+
return false;
|
|
29
|
+
if (Object.getOwnPropertySymbols(value).length > 0)
|
|
30
|
+
return false;
|
|
31
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
32
|
+
for (const descriptor of Object.values(descriptors)) {
|
|
33
|
+
if (!descriptor.enumerable || !("value" in descriptor))
|
|
34
|
+
return false;
|
|
35
|
+
if (!isJsonValueInternal(descriptor.value, ancestors))
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
ancestors.delete(value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface InvocationLimits {
|
|
2
|
+
readonly maxGraphDepth: number;
|
|
3
|
+
readonly maxGraphInvocations: number;
|
|
4
|
+
readonly maxTotalNodeVisits: number;
|
|
5
|
+
}
|
|
6
|
+
export declare const DEFAULT_INVOCATION_LIMITS: InvocationLimits;
|
|
7
|
+
export declare function resolveInvocationLimits(limits?: Partial<InvocationLimits>): InvocationLimits;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const DEFAULT_INVOCATION_LIMITS = Object.freeze({
|
|
2
|
+
maxGraphDepth: 8,
|
|
3
|
+
maxGraphInvocations: 64,
|
|
4
|
+
maxTotalNodeVisits: 500,
|
|
5
|
+
});
|
|
6
|
+
export function resolveInvocationLimits(limits = {}) {
|
|
7
|
+
const resolved = { ...DEFAULT_INVOCATION_LIMITS, ...limits };
|
|
8
|
+
for (const [name, value] of Object.entries(resolved)) {
|
|
9
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
10
|
+
throw new Error(`${name} must be a positive integer`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return Object.freeze(resolved);
|
|
14
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ContextContent } from "./graph.js";
|
|
2
|
+
import type { ContextLifetime, ContextRetention, ContextContributionHandle } from "./context.js";
|
|
3
|
+
import type { JsonValue } from "./json.js";
|
|
4
|
+
export type MechanismInstallation = "host" | "graph" | "node";
|
|
5
|
+
export type MechanismFailurePolicy = "continue" | "fail-node" | "fail-graph";
|
|
6
|
+
export type MechanismHookName = "onRootEnter" | "onRootExit" | "onGraphEnter" | "onGraphExit" | "onGraphError" | "onNodeEnter" | "onNodeExit" | "onNodeError" | "beforeAgentRun" | "afterAgentRun" | "validateCompletion";
|
|
7
|
+
export interface MechanismScope {
|
|
8
|
+
readonly scopeId: string;
|
|
9
|
+
readonly installation: MechanismInstallation;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
isActive(): boolean;
|
|
12
|
+
onCleanup(cleanup: () => void | Promise<void>): void;
|
|
13
|
+
}
|
|
14
|
+
export interface MechanismContextApi {
|
|
15
|
+
add(id: string, content: ContextContent, options?: {
|
|
16
|
+
readonly lifetime?: ContextLifetime;
|
|
17
|
+
readonly retention?: ContextRetention;
|
|
18
|
+
}): ContextContributionHandle;
|
|
19
|
+
}
|
|
20
|
+
export interface MechanismExecResult {
|
|
21
|
+
readonly exitCode: number;
|
|
22
|
+
readonly stdout: string;
|
|
23
|
+
readonly stderr: string;
|
|
24
|
+
readonly truncated: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface MechanismExec {
|
|
27
|
+
run(file: string, args?: readonly string[], options?: {
|
|
28
|
+
readonly cwd?: string;
|
|
29
|
+
readonly timeoutMs?: number;
|
|
30
|
+
}): Promise<MechanismExecResult>;
|
|
31
|
+
}
|
|
32
|
+
export interface MechanismDecisionTrace {
|
|
33
|
+
readonly mechanismName: string;
|
|
34
|
+
readonly hook: MechanismHookName;
|
|
35
|
+
readonly decision: "allow" | "reject" | "fail-node" | "fail-graph";
|
|
36
|
+
readonly reason?: string;
|
|
37
|
+
readonly timestamp: number;
|
|
38
|
+
}
|
|
39
|
+
export interface MechanismContext<TState = JsonValue> {
|
|
40
|
+
readonly rootRunId: string;
|
|
41
|
+
readonly graphInvocationId?: string;
|
|
42
|
+
readonly nodeVisitId?: string;
|
|
43
|
+
readonly agentRunId?: string;
|
|
44
|
+
readonly stageId?: string;
|
|
45
|
+
readonly state: TState;
|
|
46
|
+
readonly scope: MechanismScope;
|
|
47
|
+
readonly context: MechanismContextApi;
|
|
48
|
+
readonly exec: MechanismExec;
|
|
49
|
+
/** Direct Host adapter access is unmanaged and generates a runtime warning when read. */
|
|
50
|
+
readonly pi?: unknown;
|
|
51
|
+
}
|
|
52
|
+
export type MechanismCompletionDecision = {
|
|
53
|
+
readonly action: "allow";
|
|
54
|
+
readonly verifiedResult?: JsonValue;
|
|
55
|
+
} | {
|
|
56
|
+
readonly action: "reject" | "fail-node" | "fail-graph";
|
|
57
|
+
readonly reason: string;
|
|
58
|
+
};
|
|
59
|
+
export interface Mechanism<TState extends JsonValue = JsonValue> {
|
|
60
|
+
readonly name: string;
|
|
61
|
+
readonly allowMultiple?: boolean;
|
|
62
|
+
readonly failurePolicy?: MechanismFailurePolicy;
|
|
63
|
+
createState?(): TState;
|
|
64
|
+
snapshot?(state: Readonly<TState>): JsonValue;
|
|
65
|
+
restore?(snapshot: JsonValue): TState;
|
|
66
|
+
onRootEnter?(ctx: MechanismContext<TState>): void | Promise<void>;
|
|
67
|
+
onRootExit?(ctx: MechanismContext<TState>): void | Promise<void>;
|
|
68
|
+
onGraphEnter?(ctx: MechanismContext<TState>): void | Promise<void>;
|
|
69
|
+
onGraphExit?(ctx: MechanismContext<TState>): void | Promise<void>;
|
|
70
|
+
onGraphError?(ctx: MechanismContext<TState> & {
|
|
71
|
+
readonly error: unknown;
|
|
72
|
+
}): void | Promise<void>;
|
|
73
|
+
onNodeEnter?(ctx: MechanismContext<TState>): void | Promise<void>;
|
|
74
|
+
onNodeExit?(ctx: MechanismContext<TState> & {
|
|
75
|
+
readonly completion: JsonValue;
|
|
76
|
+
}): void | Promise<void>;
|
|
77
|
+
onNodeError?(ctx: MechanismContext<TState> & {
|
|
78
|
+
readonly error: unknown;
|
|
79
|
+
}): void | Promise<void>;
|
|
80
|
+
beforeAgentRun?(ctx: MechanismContext<TState> & {
|
|
81
|
+
readonly prompt: string;
|
|
82
|
+
}): void | Promise<void>;
|
|
83
|
+
afterAgentRun?(ctx: MechanismContext<TState>): void | Promise<void>;
|
|
84
|
+
validateCompletion?(ctx: MechanismContext<TState> & {
|
|
85
|
+
readonly completion: JsonValue;
|
|
86
|
+
}): MechanismCompletionDecision | Promise<MechanismCompletionDecision>;
|
|
87
|
+
}
|
|
88
|
+
export declare function defineMechanism<TState extends JsonValue>(mechanism: Mechanism<TState>): Mechanism<TState>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { JsonValue } from "./json.js";
|
|
2
|
+
export type GraphFailureCode = "invalid-graph" | "invalid-input" | "entry-not-found" | "tool-unavailable" | "host-unavailable" | "agent-timeout" | "agent-ended-without-completion" | "validation-exhausted" | "max-steps-exceeded" | "no-route" | "transition-failed" | "mechanism-failed" | "persistence-failed" | "resume-incompatible" | "runtime-error" | "cancelled";
|
|
3
|
+
export type GraphFailurePhase = "root" | "graph" | "entry" | "node" | "agent" | "route" | "transition" | "host";
|
|
4
|
+
export interface GraphFailure {
|
|
5
|
+
readonly code: GraphFailureCode;
|
|
6
|
+
readonly phase: GraphFailurePhase;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
readonly retryable: boolean;
|
|
9
|
+
readonly stageId?: string;
|
|
10
|
+
readonly cause?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export type RecordingMode = "off" | "events" | "replay" | "forensic";
|
|
13
|
+
export interface ReplayReference {
|
|
14
|
+
readonly mode: RecordingMode;
|
|
15
|
+
readonly status: "off" | "complete" | "incomplete" | "failed";
|
|
16
|
+
readonly location?: string;
|
|
17
|
+
readonly issues?: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export interface GraphRunCommon {
|
|
20
|
+
readonly rootRunId: string;
|
|
21
|
+
readonly graphId: string;
|
|
22
|
+
readonly graphVersion: string;
|
|
23
|
+
readonly steps: number;
|
|
24
|
+
readonly durationMs: number;
|
|
25
|
+
readonly replay: ReplayReference;
|
|
26
|
+
}
|
|
27
|
+
export interface CompletedGraphRun<TOutput = JsonValue> extends GraphRunCommon {
|
|
28
|
+
readonly status: "completed";
|
|
29
|
+
readonly output: TOutput;
|
|
30
|
+
}
|
|
31
|
+
export interface FailedGraphRun extends GraphRunCommon {
|
|
32
|
+
readonly status: "failed";
|
|
33
|
+
readonly failure: GraphFailure;
|
|
34
|
+
}
|
|
35
|
+
export interface CancelledGraphRun extends GraphRunCommon {
|
|
36
|
+
readonly status: "cancelled";
|
|
37
|
+
readonly failure: GraphFailure & {
|
|
38
|
+
readonly code: "cancelled";
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export type GraphRunResult<TOutput = JsonValue> = CompletedGraphRun<TOutput> | FailedGraphRun | CancelledGraphRun;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|