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
package/README.md
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# Loop Graph SDK
|
|
2
|
+
|
|
3
|
+
[中文说明](README-zh.md) | [Roadmap](ROADMAP.md)
|
|
4
|
+
|
|
5
|
+
> ⚠️ **Experimental**: Loop Graph SDK is in early-stage development (0.2.0). Expect instability, rough edges, and breaking API changes. Some features are incomplete. Feedback and testing are welcome, but do not rely on this in production.
|
|
6
|
+
|
|
7
|
+
Turn a complex agent task into a visible, traceable, and auditable workflow graph.
|
|
8
|
+
|
|
9
|
+
Loop Graph SDK is designed for Pi applications that require long-running, multi-phase execution with reviewable results. It lets you express "what to do first, what to check, where to go back on failure, and what to deliver at the end" as a graph, while preserving the agent's freedom to explore and reason.
|
|
10
|
+
|
|
11
|
+
It is especially suited for:
|
|
12
|
+
|
|
13
|
+
- Multi-phase workflows like code organization, generation, review, revision, and re-review;
|
|
14
|
+
- Processes that alternate between code execution and agent reasoning;
|
|
15
|
+
- Agent applications that need tool allowlists, automatic validation, and failure boundaries;
|
|
16
|
+
- Tasks that reuse sub-flows without letting context and state leak between them;
|
|
17
|
+
- Systems where you need to answer "what did the model see, which path did it take, and why was something accepted or rejected" after the run.
|
|
18
|
+
|
|
19
|
+
## Why Loop Graphs over Skills?
|
|
20
|
+
|
|
21
|
+
Skills give the agent natural-language instructions on "how to do it." But skills have inherent limitations:
|
|
22
|
+
|
|
23
|
+
- **No type boundaries**: inputs and outputs are described in prose. The agent can improvise — results are unreliable;
|
|
24
|
+
- **No routing control**: a skill runs and ends. You can't express "check the result, if it fails go back and fix it";
|
|
25
|
+
- **No monitoring or audit**: you can't see what decisions the model made inside the skill, only the final output;
|
|
26
|
+
- **No mechanism constraints**: you can't inject automatic validation, rejection policies, or context compaction at critical points.
|
|
27
|
+
|
|
28
|
+
Loop Graphs upgrade your workflow from "a prompt" to "a typed, routable, monitorable, auditable graph":
|
|
29
|
+
|
|
30
|
+
- Every stage has explicit **input/output schemas** (TypeBox) — results that don't match the contract are auto-rejected;
|
|
31
|
+
- Stages are connected by **explicit routes** — which path to take on success, where to go back on failure, what conditions trigger what actions, all in the graph;
|
|
32
|
+
- Built-in **Mechanism system** lets you inject automatic validation, retry policies, and failure handling at completion points;
|
|
33
|
+
- Full **Recording/Replay** — every run can be traced back: "what did the model see, what decision did it make, why was it accepted or rejected."
|
|
34
|
+
|
|
35
|
+
In short: **Skills have instructions. Loop Graphs have instructions + boundaries + monitoring + audit.**
|
|
36
|
+
|
|
37
|
+
## Let AI Write Your Loop Graphs
|
|
38
|
+
|
|
39
|
+
Loop Graph SDK provides a complete TypeScript API with rich type definitions. **Pi can directly write your graph definition code.** Just describe your workflow — "review the code first, fix any issues found, review again, submit when it passes" — and Pi will generate the corresponding `defineGraph` code.
|
|
40
|
+
|
|
41
|
+
The SDK's documentation is designed to be AI-friendly: detailed JSDoc, clean type hierarchies, rich working examples, and a complete doc tree covering concepts, guides, and reference. Feed the `docs/` directory to Pi and it can understand the entire system to help you write loop graphs efficiently.
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
Loop Graph SDK is primarily a library imported by other projects. Business projects install the package and create their own graphs and Extensions from the root entry point. The bundled `/extension` entry is for debugging and demos only — business code should create its own Extension instance.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Local development: install from a sibling workspace
|
|
49
|
+
npm install ../pi-loop-graph-extension-public
|
|
50
|
+
|
|
51
|
+
# Pre-publish verification: generate a tarball, then install in a business project
|
|
52
|
+
npm pack
|
|
53
|
+
npm install ./pi-loop-graph-sdk-0.2.0.tgz
|
|
54
|
+
|
|
55
|
+
# Git dependency: use the actual repo URL with a fixed tag/commit
|
|
56
|
+
npm install git+https://github.com/<owner>/<repo>.git#<tag-or-commit>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Once installed, use the stable root entry:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import {
|
|
63
|
+
agentNode,
|
|
64
|
+
codeNode,
|
|
65
|
+
createGraphHost,
|
|
66
|
+
createLoopGraphExtension,
|
|
67
|
+
defineGraph,
|
|
68
|
+
graphNode,
|
|
69
|
+
} from "pi-loop-graph-sdk";
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Replay and advanced capabilities use separate entry points:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { parseReplay, exportReplayHtml } from "pi-loop-graph-sdk/replay";
|
|
76
|
+
import { GraphRuntime, validateGraph } from "pi-loop-graph-sdk/advanced";
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> Install via npm registry or Git:
|
|
80
|
+
>
|
|
81
|
+
> ```bash
|
|
82
|
+
> pi install npm:pi-loop-graph-sdk@0.2.0
|
|
83
|
+
> # or
|
|
84
|
+
> pi install git:github.com/0liveiraaa/pi-loop-graph-sdk@v0.2.0
|
|
85
|
+
> ```
|
|
86
|
+
>
|
|
87
|
+
> Local directory, tarball, and Git dependency installs also work.
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
### 1. A minimal code-only graph
|
|
92
|
+
|
|
93
|
+
This graph has a single code phase. No model authentication is needed — it's a good way to confirm how graphs, phases, routes, and results work.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import {
|
|
97
|
+
Type,
|
|
98
|
+
codeNode,
|
|
99
|
+
createGraphHost,
|
|
100
|
+
defineGraph,
|
|
101
|
+
entry,
|
|
102
|
+
finish,
|
|
103
|
+
firstMatch,
|
|
104
|
+
} from "pi-loop-graph-sdk";
|
|
105
|
+
|
|
106
|
+
const Input = Type.Object({ name: Type.String() });
|
|
107
|
+
const Output = Type.Object({ message: Type.String() });
|
|
108
|
+
|
|
109
|
+
const helloGraph = defineGraph({
|
|
110
|
+
id: "hello",
|
|
111
|
+
version: "1",
|
|
112
|
+
goal: "Generate a greeting",
|
|
113
|
+
input: Input,
|
|
114
|
+
output: Output,
|
|
115
|
+
context: {
|
|
116
|
+
background: { select: "all" },
|
|
117
|
+
},
|
|
118
|
+
entries: [entry("main", { to: "greet" })],
|
|
119
|
+
stages: {
|
|
120
|
+
greet: {
|
|
121
|
+
node: codeNode({
|
|
122
|
+
subGoal: "Generate greeting",
|
|
123
|
+
input: Input,
|
|
124
|
+
output: Output,
|
|
125
|
+
execute: ({ input, complete }) =>
|
|
126
|
+
complete({ message: `Hello, ${input.name}` }),
|
|
127
|
+
}),
|
|
128
|
+
route: firstMatch({
|
|
129
|
+
done: finish({
|
|
130
|
+
output: ({ completion }) => completion.result,
|
|
131
|
+
}),
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const host = createGraphHost({ recording: "off" });
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const result = await host.execute(helloGraph, { name: "World" });
|
|
141
|
+
|
|
142
|
+
if (result.status === "completed") {
|
|
143
|
+
console.log(result.output.message);
|
|
144
|
+
} else {
|
|
145
|
+
console.error(result.failure.code, result.failure.message);
|
|
146
|
+
}
|
|
147
|
+
} finally {
|
|
148
|
+
await host.dispose();
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Key relationships:
|
|
153
|
+
|
|
154
|
+
- `defineGraph` describes the entire task graph;
|
|
155
|
+
- `entry` declares where execution starts;
|
|
156
|
+
- `codeNode` defines a code phase;
|
|
157
|
+
- `firstMatch` and `finish` define routing and termination;
|
|
158
|
+
- `createGraphHost` provides an execution channel with a manageable lifecycle.
|
|
159
|
+
|
|
160
|
+
### 2. From code phase to agent phase
|
|
161
|
+
|
|
162
|
+
When a phase needs model reasoning, swap `codeNode` for `agentNode`. Input, output, tools, and skills remain part of the graph definition:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import {
|
|
166
|
+
Type,
|
|
167
|
+
agentNode,
|
|
168
|
+
skillRef,
|
|
169
|
+
toolSet,
|
|
170
|
+
} from "pi-loop-graph-sdk";
|
|
171
|
+
|
|
172
|
+
const DraftInput = Type.Object({ topic: Type.String() });
|
|
173
|
+
const DraftOutput = Type.Object({ answer: Type.String() });
|
|
174
|
+
|
|
175
|
+
const writeDraft = agentNode({
|
|
176
|
+
subGoal: "Write a concise answer for the topic",
|
|
177
|
+
input: DraftInput,
|
|
178
|
+
output: DraftOutput,
|
|
179
|
+
prompt: "Complete the current phase and submit a structured result matching the output contract.",
|
|
180
|
+
tools: toolSet("read"),
|
|
181
|
+
skills: [skillRef("answer-writing", "1")],
|
|
182
|
+
context: {
|
|
183
|
+
focus: { select: "all" },
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The agent must submit results via the protected completion tool:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
__graph_complete__({ result })
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Only `{ result }` is accepted. `status`, `reportedStatus`, and other extra fields are not part of the model protocol and will be rejected. Acceptance, rejection, and failure are decided by the SDK based on output contract, validators, mechanisms, and routing rules — not by the model self-reporting status.
|
|
195
|
+
|
|
196
|
+
### 3. Using as a Pi Extension
|
|
197
|
+
|
|
198
|
+
Business Extensions create their own instance, register graphs, and decide how to expose them:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import {
|
|
202
|
+
createLoopGraphExtension,
|
|
203
|
+
graphRef,
|
|
204
|
+
} from "pi-loop-graph-sdk";
|
|
205
|
+
import { helloGraph } from "./hello-graph.js";
|
|
206
|
+
|
|
207
|
+
export default function setup(pi) {
|
|
208
|
+
const loop = createLoopGraphExtension(pi);
|
|
209
|
+
|
|
210
|
+
loop.registerGraph(helloGraph);
|
|
211
|
+
loop.exposeGraph(graphRef("hello", "1"), {
|
|
212
|
+
kind: "command",
|
|
213
|
+
name: "hello",
|
|
214
|
+
description: "Generate a greeting",
|
|
215
|
+
parseInput: (args) => ({ name: args.trim() || "World" }),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Registration and exposure are separate actions: a graph can be registered once and exposed as a command or tool as needed. Exposed entries run in an isolated Pi Session by default. Only set `execution: "current-session"` when sharing the caller's session state is intentional.
|
|
221
|
+
|
|
222
|
+
### 4. Handling results, cancellation, and lifecycle
|
|
223
|
+
|
|
224
|
+
Every execution returns a `GraphRunResult`:
|
|
225
|
+
|
|
226
|
+
- `completed`: provides `output` that has passed output schema validation;
|
|
227
|
+
- `failed`: provides a structured `failure` with error code, phase, message, and whether it's retryable;
|
|
228
|
+
- `cancelled`: provides the cancellation reason.
|
|
229
|
+
|
|
230
|
+
A single Host allows only one Root Run at a time. Concurrent Root Runs should use independent Hosts. External `AbortSignal` propagates to the active execution; `dispose()` waits for the active run to clean up before releasing resources.
|
|
231
|
+
|
|
232
|
+
### 5. Recording, Replay, and Resume
|
|
233
|
+
|
|
234
|
+
The Host defaults to `replay` recording, writing run data to `.loop-graph/runs/{rootRunId}`. You can also choose per-run: `off`, `events`, `replay`, or `forensic`:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const result = await host.execute(graph, input, {
|
|
238
|
+
recording: "replay",
|
|
239
|
+
recordingRequired: true,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
console.log(result.replay.status, result.replay.location);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Offline replay reading and HTML export come from the dedicated entry point:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import {
|
|
249
|
+
exportReplayHtml,
|
|
250
|
+
parseReplay,
|
|
251
|
+
} from "pi-loop-graph-sdk/replay";
|
|
252
|
+
|
|
253
|
+
const model = parseReplay(replayJsonText);
|
|
254
|
+
const html = exportReplayHtml(model);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Currently, checkpoint/resume supports reliable single-layer Root recovery at phase boundaries. Nested `call`, `compose`, and `delegate` continuation recovery is not yet implemented; encountering such checkpoints returns `resume-incompatible` rather than incorrectly applying child-graph state to the parent.
|
|
258
|
+
|
|
259
|
+
### 6. Choosing the right entry point
|
|
260
|
+
|
|
261
|
+
For everyday business code, use the root entry:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import {
|
|
265
|
+
agentNode,
|
|
266
|
+
codeNode,
|
|
267
|
+
createGraphHost,
|
|
268
|
+
createLoopGraphExtension,
|
|
269
|
+
defineGraph,
|
|
270
|
+
graphNode,
|
|
271
|
+
} from "pi-loop-graph-sdk";
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
For low-level graph runtime, validators, routers, or advanced isolated Hosts:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import {
|
|
278
|
+
GraphRuntime,
|
|
279
|
+
selectEdge,
|
|
280
|
+
validateGraph,
|
|
281
|
+
} from "pi-loop-graph-sdk/advanced";
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
For recording, replay, and checkpoint types:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import {
|
|
288
|
+
FileRunStore,
|
|
289
|
+
decodeCheckpoint,
|
|
290
|
+
parseReplay,
|
|
291
|
+
} from "pi-loop-graph-sdk/replay";
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The legacy global `registerGraph`, `initRegistry`, `findEntry`, and `createAgentExecute` are not part of the 0.2 public API.
|
|
295
|
+
|
|
296
|
+
## Four Core Concepts
|
|
297
|
+
|
|
298
|
+
Once you've completed your first run, these four concepts explain what the SDK solves. Think of it as "an agent workflow engine with function-call boundaries and full work recording" — no need to understand the internal runtime code.
|
|
299
|
+
|
|
300
|
+
### 1. Loop Graphs: draw the task process
|
|
301
|
+
|
|
302
|
+
A graph consists of entries, stages, and routes. After each stage completes, routing decides where to go next: continue, return to a previous stage, or finish and deliver the result.
|
|
303
|
+
|
|
304
|
+
A model can go through many rounds of reasoning and tool calls within a single stage; those details are the stage's internal work. The graph only expresses cross-stage business flow, so you can see how the task progresses at a glance without drowning in a long conversation transcript.
|
|
305
|
+
|
|
306
|
+
### 2. Context Frame Stack: remember work like a call stack
|
|
307
|
+
|
|
308
|
+
Cross-stage information doesn't scatter across global variables — it enters an ordered context frame stack:
|
|
309
|
+
|
|
310
|
+
```text
|
|
311
|
+
Task background
|
|
312
|
+
└─ Working memory left after Stage A completes
|
|
313
|
+
└─ Working memory left after Stage B completes
|
|
314
|
+
└─ Current stage workspace
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
When a stage finishes, the flow only folds what the next stages actually need into a single frame:
|
|
318
|
+
|
|
319
|
+
- Subsequent agents see useful working memory, not all prior raw conversations;
|
|
320
|
+
- Each route decides "what this completion should leave behind" — state transitions aren't hidden in node side effects;
|
|
321
|
+
- `call` working memory is isolated and destroyed when the sub-graph ends; `compose` lets sub-graph frames remain visible to subsequent parent-graph nodes.
|
|
322
|
+
|
|
323
|
+
The frame stack and the full log are separate things: the frame stack serves the next step of work; the log serves later audit and review.
|
|
324
|
+
|
|
325
|
+
### 3. Three invocation boundaries: like three kinds of function calls
|
|
326
|
+
|
|
327
|
+
Sub-graphs can be invoked by another stage like functions, but you explicitly choose how much working context they share with the caller:
|
|
328
|
+
|
|
329
|
+
| Boundary | Think of it as | Best for |
|
|
330
|
+
| ------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
331
|
+
| `call` | Call a function with its own workspace, return result | Reusing complete sub-flows like review, extraction, classification |
|
|
332
|
+
| `compose` | Inline a complex function's steps into the current function | Sub-flows that need to read and write the parent's working memory |
|
|
333
|
+
| `delegate` | Hand off to an independent worker, isolated session and resources | Long tasks, risky tasks, or work needing an independent lifecycle |
|
|
334
|
+
|
|
335
|
+
All three boundaries express sequential invocation — none implies automatic parallelism.
|
|
336
|
+
|
|
337
|
+
### 4. Complete logging system: reconstruct the process after the run
|
|
338
|
+
|
|
339
|
+
The SDK's recording is not a simple one-line "success/failure" log. It can record:
|
|
340
|
+
|
|
341
|
+
- Graph, stage, invocation boundary, and node enter/exit;
|
|
342
|
+
- Agent execution, model turns, tool calls, and tool results;
|
|
343
|
+
- Completion submission, validation, acceptance or rejection;
|
|
344
|
+
- Context snapshots, compaction, expansion mechanisms, and recovery points;
|
|
345
|
+
- Large result file references and safe, sanitized summaries.
|
|
346
|
+
|
|
347
|
+
Recording can be set to off, events, replay, or forensic mode. Replays can be parsed into a structured model or exported as HTML reports to answer "what did the model see" and "why did the system make this decision". Full audit trails never crowd the next stage's working context.
|
|
348
|
+
|
|
349
|
+
## Current Limitations
|
|
350
|
+
|
|
351
|
+
- A graph run follows a single explicit path; no automatic fork/join parallel scheduling;
|
|
352
|
+
- Multi-agent communication is an independent research direction, not a current public capability;
|
|
353
|
+
- `delegate` is an isolated execution boundary, not parallelism;
|
|
354
|
+
- Real LLM tests require valid authentication, network access, and model responses; such tests are skipped by default;
|
|
355
|
+
|
|
356
|
+
Debug log files are not written by default. `debug: true` only exists on the legacy compatibility/characterization path and is not a public configuration of the current 0.2 root entry. Use recording/replay for formal auditing.
|
|
357
|
+
|
|
358
|
+
## Verification
|
|
359
|
+
|
|
360
|
+
Run in the repository:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
npm run typecheck
|
|
364
|
+
npm test
|
|
365
|
+
npm run test:package-consumer
|
|
366
|
+
npm pack --dry-run --json
|
|
367
|
+
git diff --check
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Documentation Index
|
|
371
|
+
|
|
372
|
+
### Getting Started
|
|
373
|
+
|
|
374
|
+
- [10-Minute Quick Start](docs/getting-started.md)
|
|
375
|
+
- [0.1 → 0.2 Migration Guide](docs/migration-0.1-to-0.2.md)
|
|
376
|
+
|
|
377
|
+
### Understanding the System
|
|
378
|
+
|
|
379
|
+
- [Core Concepts Index](docs/concepts/README.md)
|
|
380
|
+
- [Graph Model](docs/concepts/graph-model.md)
|
|
381
|
+
- [Context & State](docs/concepts/context-and-state.md)
|
|
382
|
+
- [Sub-graph Boundaries](docs/concepts/subgraph-boundaries.md)
|
|
383
|
+
- [Mechanisms](docs/concepts/mechanisms.md)
|
|
384
|
+
|
|
385
|
+
### How-To Guides
|
|
386
|
+
|
|
387
|
+
- [Guides Index](docs/guides/README.md)
|
|
388
|
+
- [Building Loops & Conditional Routes](docs/guides/build-a-loop.md)
|
|
389
|
+
- [Mixing Code & Agent](docs/guides/mix-code-and-agent.md)
|
|
390
|
+
- [Calling Sub-graphs](docs/guides/call-subgraphs.md)
|
|
391
|
+
- [Control Tools](docs/guides/control-tools.md)
|
|
392
|
+
- [Customizing Context](docs/guides/customize-context.md)
|
|
393
|
+
- [Observability](docs/guides/observability.md)
|
|
394
|
+
|
|
395
|
+
### Reference
|
|
396
|
+
|
|
397
|
+
- [API Reference Index](docs/reference/README.md)
|
|
398
|
+
- [Configuration](docs/reference/configuration.md)
|
|
399
|
+
- [Lifecycle](docs/reference/lifecycle.md)
|
|
400
|
+
- [Errors & Limits](docs/reference/errors-and-limits.md)
|
|
401
|
+
|
|
402
|
+
### Maintaining the SDK
|
|
403
|
+
|
|
404
|
+
- [Core Design](docs/design/core-design.md)
|
|
405
|
+
- [Internals Index](docs/internals/README.md)
|
|
406
|
+
- [ADRs](docs/adr/)
|
|
407
|
+
|
|
408
|
+
### Research
|
|
409
|
+
|
|
410
|
+
- [Research Documents](docs/research/README.md)
|
|
411
|
+
|
|
412
|
+
### Roadmap
|
|
413
|
+
|
|
414
|
+
- [Roadmap](ROADMAP.md)
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
## 0.2.x — 稳定当前机制,修复已知问题
|
|
4
|
+
|
|
5
|
+
> 短期目标:让现有单图工作流在生产级质量上可靠运行
|
|
6
|
+
|
|
7
|
+
- **Mechanism 系统稳定化**:修复自动校验、重试策略、失败处理中的边界情况
|
|
8
|
+
- **Recording/Replay 完善**:修复嵌套调用边界的 checkpoint/resume 兼容性问题,提升回放可靠性
|
|
9
|
+
- **上下文帧栈 Bug 修复**:修复 `compose`/`call` 边界下上下文折叠的可能
|
|
10
|
+
- 问题
|
|
11
|
+
- **错误信息改进**:让 `GraphFailure` 提供更精确的阶段定位和可操作的错误描述
|
|
12
|
+
- **测试覆盖补充**:补全嵌套子图、边界恢复等场景的测试
|
|
13
|
+
- **性能优化**:减少大型日志文件的写入开销
|
|
14
|
+
|
|
15
|
+
## 0.3.x — 多 Agent 运行时
|
|
16
|
+
|
|
17
|
+
> 中长期目标:类 ROS2 的多 Agent 通讯与并发执行
|
|
18
|
+
|
|
19
|
+
Loop Graph SDK 的多 Agent 通讯是 [独立研究方向](docs/research/multi-agent-communication.md)。0.3 的目标是把它从研究落地为稳定 API。
|
|
20
|
+
|
|
21
|
+
### 通讯机制:类 ROS2 的 Topics/Services
|
|
22
|
+
|
|
23
|
+
- **Topic(发布/订阅)**:多 Agent 之间通过命名 Topic 异步广播消息。支持多种 QoS 策略(best-effort / reliable),不要求 Agent 同时在线
|
|
24
|
+
- **Service(请求/响应)**:类似 ROS2 Service 的同步调用模式,一个 Agent 发起请求,另一个 Agent 处理并返回结果
|
|
25
|
+
- **节点发现**:类似 ROS2 Node Discovery,Agent 可以动态加入/退出通讯网络,不影响已有通讯链路
|
|
26
|
+
|
|
27
|
+
### SDK 侧 API
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// 异步调用:多个子图/Agent 并发运行
|
|
31
|
+
const [review, test, lint] = await host.executeAll([
|
|
32
|
+
{ graph: reviewGraph, input: { code } },
|
|
33
|
+
{ graph: testGraph, input: { code } },
|
|
34
|
+
{ graph: lintGraph, input: { code } },
|
|
35
|
+
]);
|
|
36
|
+
// 三个图并发执行,互不阻塞
|
|
37
|
+
|
|
38
|
+
// 发布消息到 Topic
|
|
39
|
+
await host.publish("code.changes", { file, diff });
|
|
40
|
+
|
|
41
|
+
// 订阅 Topic
|
|
42
|
+
host.subscribe("code.changes", async (msg) => {
|
|
43
|
+
// 收到消息后触发对应图
|
|
44
|
+
await host.execute(reviewGraph, msg.data);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Service 调用
|
|
48
|
+
const result = await host.call("code.format", { code });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 设计原则
|
|
52
|
+
|
|
53
|
+
- **隔离优先**:每个 Agent 运行在独立会话中,上下文和状态互不污染
|
|
54
|
+
- **容错优先**:单个 Agent 失败不影响整体通讯网络,支持超时、重试和降级
|
|
55
|
+
- **可观测优先**:所有通讯链路均可 Recoding/Replay,多 Agent 交互也能完整审计
|
|
56
|
+
- **渐进增强**:不破坏 0.2 的单图 API,多 Agent 能力作为可选扩展层叠加
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
> Loop Graph SDK 目前处于早期实验阶段。路线图会根据实际使用反馈调整,不保证时间表。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// __graph_complete__ 工具定义
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
5
|
+
export const COMPLETE_TOOL_NAME = "__graph_complete__";
|
|
6
|
+
export function createCompleteTool() {
|
|
7
|
+
return {
|
|
8
|
+
name: COMPLETE_TOOL_NAME,
|
|
9
|
+
label: "完成阶段",
|
|
10
|
+
description: "提交当前 Loop Graph 节点的候选结果。只有 Runtime 检查通过后节点才算完成。",
|
|
11
|
+
parameters: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
result: {
|
|
15
|
+
type: "object",
|
|
16
|
+
description: "本阶段产出数据",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ["result"],
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
},
|
|
22
|
+
async execute(_toolCallId, _params) {
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{ type: "text", text: "节点结果已提交,等待检查。" },
|
|
26
|
+
],
|
|
27
|
+
// 原始参数已经存在于 assistant tool call 和 ToolResultEvent.input。
|
|
28
|
+
// 不在 tool result details 中复制,避免其他扩展或 UI 将其误作 Runtime 结论。
|
|
29
|
+
details: undefined,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
renderCall(_args, theme, _context) {
|
|
33
|
+
return new Text(theme.fg("toolTitle", theme.bold("提交节点结果")), 0, 0);
|
|
34
|
+
},
|
|
35
|
+
renderResult(result, _options, theme, _context) {
|
|
36
|
+
const decision = result.details;
|
|
37
|
+
let content;
|
|
38
|
+
if (!decision) {
|
|
39
|
+
content = theme.fg("muted", "节点结果已提交,等待检查");
|
|
40
|
+
}
|
|
41
|
+
else if (decision.decision === "accepted") {
|
|
42
|
+
content = theme.fg("success", "✓ 节点结果已通过检查");
|
|
43
|
+
}
|
|
44
|
+
else if (decision.decision === "rejected") {
|
|
45
|
+
content = theme.fg("error", `✗ 节点结果未被接受:${decision.reason}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
content = theme.fg("error", `✗ ${decision.scope === "graph" ? "图" : "节点"}验收失败:${decision.reason}`);
|
|
49
|
+
}
|
|
50
|
+
return new Text(content, 0, 0);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ContextFrame, NodeCompletion, NodeInput } from "../type.js";
|
|
2
|
+
import type { ProjectionInput, MessageEntry } from "./projection.js";
|
|
3
|
+
/** 截断序列化帧的预览,避免日志爆量。 */
|
|
4
|
+
export declare function safePreview(value: unknown, maxLength?: number): string;
|
|
5
|
+
export declare const debugLog: {
|
|
6
|
+
preview(value: unknown, maxLength?: number): string;
|
|
7
|
+
/** 图启动 */
|
|
8
|
+
graphStart(graphId: string, trigger: unknown): void;
|
|
9
|
+
/** 进入节点 */
|
|
10
|
+
enterNode(depth: number, nodeId: string, scopeId: string, input: NodeInput, frames: ContextFrame[]): void;
|
|
11
|
+
/** 退出节点(折叠帧)。控制信息来自 completion;frame 作为 opaque payload。 */
|
|
12
|
+
exitNode(depth: number, nodeId: string, completion: NodeCompletion, frame: ContextFrame, allFrames: ContextFrame[]): void;
|
|
13
|
+
/** context 钩子投影 */
|
|
14
|
+
projection(input: ProjectionInput, output: MessageEntry[]): void;
|
|
15
|
+
/** 图运行期间发生 compaction 后记录 checkpoint 信息。 */
|
|
16
|
+
scopeCheckpoint(scopeId: string, generation: number, reason: unknown, willRetry: unknown): void;
|
|
17
|
+
/** 共享 Session 的嵌套调用期间阻止 compaction 跨越 GraphCallScope。 */
|
|
18
|
+
compactionBlocked(reason: unknown, depth: number): void;
|
|
19
|
+
/** agent 完成(__graph_complete__ 被调用) */
|
|
20
|
+
agentComplete(nodeId: string, completion: NodeCompletion): void;
|
|
21
|
+
/** agent 未调用 __graph_complete__ 就结束 */
|
|
22
|
+
agentIncomplete(nodeId: string): void;
|
|
23
|
+
/** 完成验证不通过,触发重试 */
|
|
24
|
+
agentRetry(nodeId: string, reason: string): void;
|
|
25
|
+
/** 图结束。控制信息来自 result;frames 作为 opaque payload。 */
|
|
26
|
+
graphEnd(graphId: string, steps: number, resultStatus: string, resultPreview: string, frames: ContextFrame[]): void;
|
|
27
|
+
/** 图错误 */
|
|
28
|
+
graphError(graphId: string, error: string): void;
|
|
29
|
+
/** 子图 push */
|
|
30
|
+
subgraphPush(parentNodeId: string, childGraphId: string): void;
|
|
31
|
+
/** 子图 pop */
|
|
32
|
+
subgraphPop(parentNodeId: string, childGraphId: string, result: unknown): void;
|
|
33
|
+
frameSegmentStart(graphId: string, parentNodeId: string, baseIndex: number, depth: number): void;
|
|
34
|
+
frameSegmentClose(graphId: string, parentNodeId: string, frames: readonly ContextFrame[], completion: NodeCompletion): void;
|
|
35
|
+
frameSegmentRollback(graphId: string, parentNodeId: string, reason: string): void;
|
|
36
|
+
/** 工具切换 */
|
|
37
|
+
toolsChanged(nodeId: string, tools: string[]): void;
|
|
38
|
+
};
|