@yaag/extension 0.9.0 → 0.10.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/docs/authoring.md +24 -0
- package/docs/examples/06-lineage.ts +33 -0
- package/docs/examples.md +17 -0
- package/package.json +4 -4
- package/src/docs/docs-root.ts +2 -1
- package/src/record/run-summary-parse.ts +4 -0
- package/src/view/run-details.ts +19 -2
package/docs/authoring.md
CHANGED
|
@@ -113,3 +113,27 @@ rules. ADR-0022 plans the human-in-the-loop verbs; they are not available yet.
|
|
|
113
113
|
program again and replays the recorded Asks. A resume needs the program too,
|
|
114
114
|
because a Cassette holds the history of a Run and never the program. See
|
|
115
115
|
[Examples](examples.md#05--record-and-resume).
|
|
116
|
+
|
|
117
|
+
## Lineage
|
|
118
|
+
|
|
119
|
+
`parent` names the Agent a spawn belongs under. It is data only: it sets the
|
|
120
|
+
Agent's place in the Run tree, so the TUI and the Run Summary draw the child
|
|
121
|
+
under its parent. It opens no channel between the two Agents, and it ends no
|
|
122
|
+
lifetime — every Agent still dies with the Run.
|
|
123
|
+
|
|
124
|
+
`parent` takes a Handle a previous spawn in this Run returned, and it works the
|
|
125
|
+
same way as an override on a definition: `ctx.spawn(reviewer, { parent })`. A
|
|
126
|
+
parent that already exited stays a valid parent.
|
|
127
|
+
|
|
128
|
+
An Agent cannot spawn. It can *ask* for a helper through `outputSchema`, and
|
|
129
|
+
the program decides:
|
|
130
|
+
|
|
131
|
+
<!-- embed: docs/examples/06-lineage.ts -->
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
for (const helper of wish.helpers) {
|
|
135
|
+
if (!ALLOWED_ROLES.has(helper.role)) continue;
|
|
136
|
+
const agent = await ctx.spawn({ name: helper.role, parent: implementer });
|
|
137
|
+
reports.push(await agent.ask(prompt`Review this report: ${wish.report}`));
|
|
138
|
+
}
|
|
139
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mediated autonomy. The implementer reports which helpers it wants; the
|
|
3
|
+
* program is the gate that decides, spawns them, and places them in the tree
|
|
4
|
+
* under the implementer with `parent`.
|
|
5
|
+
*/
|
|
6
|
+
import { defineRun, prompt } from "@yaag/runtime";
|
|
7
|
+
import { Type } from "typebox";
|
|
8
|
+
|
|
9
|
+
const ALLOWED_ROLES = new Set(["reviewer", "summarizer"]);
|
|
10
|
+
|
|
11
|
+
const Wish = Type.Object({
|
|
12
|
+
report: Type.String(),
|
|
13
|
+
helpers: Type.Array(Type.Object({ role: Type.String(), reason: Type.String() })),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export default defineRun({
|
|
17
|
+
name: "lineage",
|
|
18
|
+
description: "Spawns the helpers an implementer asks for, under the implementer.",
|
|
19
|
+
async run(ctx) {
|
|
20
|
+
const implementer = await ctx.spawn({ name: "implementer" });
|
|
21
|
+
const wish = await implementer.ask(
|
|
22
|
+
prompt`Report one paragraph about this repository, and the helper roles you want.`,
|
|
23
|
+
{ outputSchema: Wish },
|
|
24
|
+
);
|
|
25
|
+
const reports: string[] = [];
|
|
26
|
+
for (const helper of wish.helpers) {
|
|
27
|
+
if (!ALLOWED_ROLES.has(helper.role)) continue;
|
|
28
|
+
const agent = await ctx.spawn({ name: helper.role, parent: implementer });
|
|
29
|
+
reports.push(await agent.ask(prompt`Review this report: ${wish.report}`));
|
|
30
|
+
}
|
|
31
|
+
return [wish.report, ...reports].join("\n\n");
|
|
32
|
+
},
|
|
33
|
+
});
|
package/docs/examples.md
CHANGED
|
@@ -100,3 +100,20 @@ holds it.
|
|
|
100
100
|
Full file: [`examples/05-record-resume.ts`](examples/05-record-resume.ts). Run
|
|
101
101
|
it with `yaag run examples/05-record-resume.ts --record run.json`, then resume
|
|
102
102
|
it with `yaag run examples/05-record-resume.ts --resume run.json`.
|
|
103
|
+
|
|
104
|
+
## 06 — lineage
|
|
105
|
+
|
|
106
|
+
`parent` places an Agent under another Agent in the Run tree. Here the
|
|
107
|
+
implementer reports the helper roles it wants, and the program decides which of
|
|
108
|
+
them to spawn. The helpers become a subtree of the implementer, and no Agent
|
|
109
|
+
ever spawns anything itself.
|
|
110
|
+
|
|
111
|
+
<!-- embed: docs/examples/06-lineage.ts -->
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const agent = await ctx.spawn({ name: helper.role, parent: implementer });
|
|
115
|
+
reports.push(await agent.ask(prompt`Review this report: ${wish.report}`));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Full file: [`examples/06-lineage.ts`](examples/06-lineage.ts). Run it with
|
|
119
|
+
`yaag run examples/06-lineage.ts`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaag/extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@earendil-works/pi-tui": "^0.84.0",
|
|
28
|
-
"@yaag/cli": "0.
|
|
29
|
-
"@yaag/runtime": "0.
|
|
30
|
-
"@yaag/tui": "0.
|
|
28
|
+
"@yaag/cli": "0.10.0",
|
|
29
|
+
"@yaag/runtime": "0.10.0",
|
|
30
|
+
"@yaag/tui": "0.10.0",
|
|
31
31
|
"nanoid": "^6.0.1"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
package/src/docs/docs-root.ts
CHANGED
|
@@ -18,13 +18,14 @@ export const DOC_PAGES = [
|
|
|
18
18
|
"troubleshooting.md",
|
|
19
19
|
] as const;
|
|
20
20
|
|
|
21
|
-
/** The
|
|
21
|
+
/** The shipped example programs, in reading order. */
|
|
22
22
|
export const EXAMPLE_FILES = [
|
|
23
23
|
"01-minimal.ts",
|
|
24
24
|
"02-args.ts",
|
|
25
25
|
"03-fan-out.ts",
|
|
26
26
|
"04-controlled-ask.ts",
|
|
27
27
|
"05-record-resume.ts",
|
|
28
|
+
"06-lineage.ts",
|
|
28
29
|
] as const;
|
|
29
30
|
|
|
30
31
|
/** Absolute path of one shipped doc file, named relative to the docs root. */
|
|
@@ -150,6 +150,10 @@ function parseAgent(value: unknown): AgentInfo | null {
|
|
|
150
150
|
nodes,
|
|
151
151
|
finishedNodesPruned: stored.finishedNodesPruned,
|
|
152
152
|
modelFallbacks: fallbacks,
|
|
153
|
+
// Lineage is tolerant for the same reason as the counters: a record written
|
|
154
|
+
// before the Parent Link carries neither field.
|
|
155
|
+
parent: isNullableString(stored.parent) ? stored.parent : null,
|
|
156
|
+
origin: stored.origin === "fork" ? ("fork" as const) : ("spawn" as const),
|
|
153
157
|
// Tolerant for the same reason as the Run-level counter.
|
|
154
158
|
modelFallbacksPruned:
|
|
155
159
|
typeof stored.modelFallbacksPruned === "number" ? stored.modelFallbacksPruned : 0,
|
package/src/view/run-details.ts
CHANGED
|
@@ -82,6 +82,8 @@ function normalizeAgents(value: RunSummary): RunSummary {
|
|
|
82
82
|
finishedNodesPruned: agent.finishedNodesPruned ?? 0,
|
|
83
83
|
modelFallbacks: agent.modelFallbacks ?? [],
|
|
84
84
|
modelFallbacksPruned: agent.modelFallbacksPruned ?? 0,
|
|
85
|
+
parent: agent.parent ?? null,
|
|
86
|
+
origin: normalizeOrigin(agent.origin),
|
|
85
87
|
};
|
|
86
88
|
}
|
|
87
89
|
return { ...value, agents, modelFallbacks: value.modelFallbacks ?? 0 };
|
|
@@ -166,10 +168,23 @@ function isAgentBase(value: Record<string, unknown>): boolean {
|
|
|
166
168
|
optionalNodes(value.nodes) &&
|
|
167
169
|
(value.finishedNodesPruned === undefined || natural(value.finishedNodesPruned)) &&
|
|
168
170
|
optionalFallbacks(value.modelFallbacks) &&
|
|
169
|
-
(value.modelFallbacksPruned === undefined || natural(value.modelFallbacksPruned))
|
|
171
|
+
(value.modelFallbacksPruned === undefined || natural(value.modelFallbacksPruned)) &&
|
|
172
|
+
// Lineage is tolerated as absent: a details blob persisted before the
|
|
173
|
+
// Parent Link carries neither field.
|
|
174
|
+
(value.parent === undefined || nullableString(value.parent)) &&
|
|
175
|
+
(value.origin === undefined || typeof value.origin === "string")
|
|
170
176
|
);
|
|
171
177
|
}
|
|
172
178
|
|
|
179
|
+
/**
|
|
180
|
+
* An origin a newer CLI may not have shipped yet reads as "spawn", the same way
|
|
181
|
+
* a stored Run record treats it. A value outside the contract must never drop
|
|
182
|
+
* the whole details blob and blank the Run view.
|
|
183
|
+
*/
|
|
184
|
+
function normalizeOrigin(value: unknown): "spawn" | "fork" {
|
|
185
|
+
return value === "fork" ? "fork" : "spawn";
|
|
186
|
+
}
|
|
187
|
+
|
|
173
188
|
function optionalEvent(value: unknown): value is LifecycleEvent | undefined {
|
|
174
189
|
return value === undefined || isEvent(value);
|
|
175
190
|
}
|
|
@@ -183,7 +198,9 @@ function isEvent(value: unknown): value is LifecycleEvent {
|
|
|
183
198
|
return (
|
|
184
199
|
strings(value.agent, value.model, value.cwd) &&
|
|
185
200
|
optionalString(value.branch) &&
|
|
186
|
-
optionalString(value.sessionFile)
|
|
201
|
+
optionalString(value.sessionFile) &&
|
|
202
|
+
optionalString(value.parent) &&
|
|
203
|
+
(value.origin === undefined || typeof value.origin === "string")
|
|
187
204
|
);
|
|
188
205
|
case "ask_start":
|
|
189
206
|
return (
|