@lloyal-labs/lloyal-agents 1.7.0 → 2.1.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/README.md +83 -99
- package/dist/Agent.d.ts +29 -3
- package/dist/Agent.d.ts.map +1 -1
- package/dist/Agent.js +24 -2
- package/dist/Agent.js.map +1 -1
- package/dist/AgentPolicy.d.ts +34 -17
- package/dist/AgentPolicy.d.ts.map +1 -1
- package/dist/AgentPolicy.js +87 -25
- package/dist/AgentPolicy.js.map +1 -1
- package/dist/Tool.d.ts +1 -1
- package/dist/Tool.js +1 -1
- package/dist/agent-pool.d.ts +18 -6
- package/dist/agent-pool.d.ts.map +1 -1
- package/dist/agent-pool.js +435 -168
- package/dist/agent-pool.js.map +1 -1
- package/dist/context.d.ts +22 -5
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +22 -5
- package/dist/context.js.map +1 -1
- package/dist/create-agent-pool.d.ts +39 -21
- package/dist/create-agent-pool.d.ts.map +1 -1
- package/dist/create-agent-pool.js +41 -17
- package/dist/create-agent-pool.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/orchestrators.d.ts +161 -0
- package/dist/orchestrators.d.ts.map +1 -0
- package/dist/orchestrators.js +173 -0
- package/dist/orchestrators.js.map +1 -0
- package/dist/replay.d.ts +96 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +108 -0
- package/dist/replay.js.map +1 -0
- package/dist/spine.d.ts +106 -0
- package/dist/spine.d.ts.map +1 -0
- package/dist/spine.js +155 -0
- package/dist/spine.js.map +1 -0
- package/dist/trace-types.d.ts +25 -5
- package/dist/trace-types.d.ts.map +1 -1
- package/dist/types.d.ts +50 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/use-agent.d.ts +3 -3
- package/dist/use-agent.d.ts.map +1 -1
- package/dist/use-agent.js +16 -20
- package/dist/use-agent.js.map +1 -1
- package/package.json +5 -5
- package/dist/shared-root.d.ts +0 -58
- package/dist/shared-root.d.ts.map +0 -1
- package/dist/shared-root.js +0 -125
- package/dist/shared-root.js.map +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { Operation } from 'effection';
|
|
2
|
+
import type { Branch } from '@lloyal-labs/sdk';
|
|
3
|
+
import type { Agent } from './Agent';
|
|
4
|
+
/**
|
|
5
|
+
* Spec for spawning a single agent under a {@link PoolContext}.
|
|
6
|
+
* `parent` defaults to `ctx.spine`.
|
|
7
|
+
*
|
|
8
|
+
* @category Agents
|
|
9
|
+
*/
|
|
10
|
+
export interface SpawnSpec {
|
|
11
|
+
/** User message content — the agent's task. */
|
|
12
|
+
content: string;
|
|
13
|
+
/** Per-agent system prompt. */
|
|
14
|
+
systemPrompt: string;
|
|
15
|
+
/** PRNG seed for sampler diversity. */
|
|
16
|
+
seed?: number;
|
|
17
|
+
/** Parent branch to fork from. Falls back to ctx.spine. */
|
|
18
|
+
parent?: Branch;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Orchestrator-facing API surface exposed by {@link useAgentPool}.
|
|
22
|
+
*
|
|
23
|
+
* The orchestrator drives task spawning, waiting, and spine extension
|
|
24
|
+
* through this object. The pool's tick loop runs concurrently and batches
|
|
25
|
+
* decode across whatever agents are currently active.
|
|
26
|
+
*
|
|
27
|
+
* @category Agents
|
|
28
|
+
*/
|
|
29
|
+
export interface PoolContext {
|
|
30
|
+
/** The pool's spine branch. Orchestrator-provided spawns fork from here by default. */
|
|
31
|
+
readonly spine: Branch;
|
|
32
|
+
/** Fork an agent branch, prefill its suffix, transition to active. Tick loop picks it up. */
|
|
33
|
+
spawn(spec: SpawnSpec): Operation<Agent>;
|
|
34
|
+
/** Suspend until agent.status becomes 'idle' | 'disposed'. Returns agent for chaining. */
|
|
35
|
+
waitFor(agent: Agent): Operation<Agent>;
|
|
36
|
+
/**
|
|
37
|
+
* Serialize a user+assistant turn and prefill it into the spine, advancing spine.position.
|
|
38
|
+
* No-op (returns 0) when assistantContent is empty.
|
|
39
|
+
*/
|
|
40
|
+
extendSpine(userContent: string, assistantContent: string): Operation<number>;
|
|
41
|
+
/** Whether another spawn with this suffix size would fit under current pressure. */
|
|
42
|
+
canFit(estimatedSuffixTokens: number): boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* An orchestrator is a generator that drives a pool via {@link PoolContext}.
|
|
46
|
+
* Returned by the factory functions in this module.
|
|
47
|
+
*
|
|
48
|
+
* @category Agents
|
|
49
|
+
*/
|
|
50
|
+
export type Orchestrator = (ctx: PoolContext) => Operation<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Parallel orchestrator — spawn all tasks upfront, wait for all to complete.
|
|
53
|
+
* This is the default shape that `useAgentPool` used to provide implicitly.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* yield* agentPool({
|
|
58
|
+
* tools: [...],
|
|
59
|
+
* orchestrate: parallel(questions.map(q => ({ content: q, systemPrompt: RESEARCH_PROMPT }))),
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @category Agents
|
|
64
|
+
*/
|
|
65
|
+
export declare const parallel: (tasks: SpawnSpec[]) => Orchestrator;
|
|
66
|
+
/**
|
|
67
|
+
* One step of a {@link chain} orchestrator. Declares the task, optional user
|
|
68
|
+
* content for spine extension after the task reports, and optional observability
|
|
69
|
+
* hooks that fire before the spawn and after the spine extension.
|
|
70
|
+
*
|
|
71
|
+
* The hooks let harnesses emit streaming events (per-task progress, completion
|
|
72
|
+
* telemetry) without dropping down to an inline orchestrator — the factory
|
|
73
|
+
* stays declarative while the hook bodies stay co-located with the step they
|
|
74
|
+
* instrument.
|
|
75
|
+
*
|
|
76
|
+
* @category Agents
|
|
77
|
+
*/
|
|
78
|
+
export interface ChainStep {
|
|
79
|
+
task: SpawnSpec;
|
|
80
|
+
/** User content recorded on the spine (e.g., "Research task: ..."). Omit to skip extension. */
|
|
81
|
+
userContent?: string;
|
|
82
|
+
/** Fires BEFORE `ctx.spawn` for this step. Use for "task starting" events. */
|
|
83
|
+
beforeSpawn?: () => Operation<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Fires AFTER `ctx.extendSpine` for this step (or immediately after waitFor
|
|
86
|
+
* if no extension happened). Receives the number of tokens added to the
|
|
87
|
+
* spine (0 when no extension) and the root's position after any extension.
|
|
88
|
+
* Use for "task done" events with spine telemetry.
|
|
89
|
+
*/
|
|
90
|
+
afterExtend?: (delta: number, position: number) => Operation<void>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Chain orchestrator — sequential execution. Each step may extend the shared
|
|
94
|
+
* root with its findings before the next step forks from the extended position.
|
|
95
|
+
*
|
|
96
|
+
* The second argument maps each item to a ChainStep, so callers can compute
|
|
97
|
+
* per-task prompts and spine labels from their own data model without
|
|
98
|
+
* coupling the factory to a particular task type.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* yield* agentPool({
|
|
103
|
+
* tools: [...],
|
|
104
|
+
* parent: querySpine,
|
|
105
|
+
* orchestrate: chain(researchTasks, (task, i) => ({
|
|
106
|
+
* task: { content: taskToContent(task), systemPrompt: renderWorker({ taskIndex: i }) },
|
|
107
|
+
* userContent: `Research task: ${task.description}`,
|
|
108
|
+
* })),
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @category Agents
|
|
113
|
+
*/
|
|
114
|
+
export declare const chain: <T>(items: T[], toStep: (item: T, index: number) => ChainStep) => Orchestrator;
|
|
115
|
+
/**
|
|
116
|
+
* Fanout orchestrator — landscape task first (optionally extending the spine),
|
|
117
|
+
* then N independent domain tasks in parallel. Domain tasks fork from the
|
|
118
|
+
* post-landscape root and do NOT see each other's findings.
|
|
119
|
+
*
|
|
120
|
+
* The canonical shape for multi-domain queries: one landscape survey that
|
|
121
|
+
* loads vocabulary into the spine, then one task per independent domain.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* yield* agentPool({
|
|
126
|
+
* orchestrate: fanout(
|
|
127
|
+
* { task: { content: landscapeQuery, systemPrompt: WORKER }, userContent: 'Landscape survey' },
|
|
128
|
+
* domainQueries.map(q => ({ content: q, systemPrompt: WORKER })),
|
|
129
|
+
* ),
|
|
130
|
+
* });
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @category Agents
|
|
134
|
+
*/
|
|
135
|
+
export declare const fanout: (landscape: ChainStep, domains: SpawnSpec[]) => Orchestrator;
|
|
136
|
+
/**
|
|
137
|
+
* A node in a {@link dag} orchestrator. Dependencies are referenced by id.
|
|
138
|
+
*
|
|
139
|
+
* @category Agents
|
|
140
|
+
*/
|
|
141
|
+
export interface DAGNode {
|
|
142
|
+
id: string;
|
|
143
|
+
task: SpawnSpec;
|
|
144
|
+
/** Ids of nodes that must complete before this node spawns. */
|
|
145
|
+
dependsOn?: string[];
|
|
146
|
+
/** User content for spine extension when this node reports. Omit to skip extension. */
|
|
147
|
+
userContent?: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* DAG orchestrator — lazy spawn on dependency resolution. Independent nodes
|
|
151
|
+
* run in parallel; dependent nodes wait until their dependencies complete
|
|
152
|
+
* (and their findings extend the spine) before forking.
|
|
153
|
+
*
|
|
154
|
+
* Subsumes the design in `docs/dag-pool.md` — DAG is an orchestration
|
|
155
|
+
* pattern expressed on top of the general primitive, not a pool internals
|
|
156
|
+
* change.
|
|
157
|
+
*
|
|
158
|
+
* @category Agents
|
|
159
|
+
*/
|
|
160
|
+
export declare const dag: (nodes: DAGNode[]) => Orchestrator;
|
|
161
|
+
//# sourceMappingURL=orchestrators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrators.d.ts","sourceRoot":"","sources":["../src/orchestrators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAQ,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,6FAA6F;IAC7F,KAAK,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAEzC,0FAA0F;IAC1F,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAExC;;;OAGG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAE9E,oFAAoF;IACpF,MAAM,CAAC,qBAAqB,EAAE,MAAM,GAAG,OAAO,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC;AAIjE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,SAAS,EAAE,KAAG,YAI3C,CAAC;AAEJ;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EACrB,OAAO,CAAC,EAAE,EACV,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,KAC5C,YAaA,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,MAAM,GAAI,WAAW,SAAS,EAAE,SAAS,SAAS,EAAE,KAAG,YAajE,CAAC;AAEJ;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,GAAG,GAAI,OAAO,OAAO,EAAE,KAAG,YA0CtC,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dag = exports.fanout = exports.chain = exports.parallel = void 0;
|
|
4
|
+
const effection_1 = require("effection");
|
|
5
|
+
// ── Factories ──────────────────────────────────────────────────
|
|
6
|
+
/**
|
|
7
|
+
* Parallel orchestrator — spawn all tasks upfront, wait for all to complete.
|
|
8
|
+
* This is the default shape that `useAgentPool` used to provide implicitly.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* yield* agentPool({
|
|
13
|
+
* tools: [...],
|
|
14
|
+
* orchestrate: parallel(questions.map(q => ({ content: q, systemPrompt: RESEARCH_PROMPT }))),
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @category Agents
|
|
19
|
+
*/
|
|
20
|
+
const parallel = (tasks) => function* (ctx) {
|
|
21
|
+
const agents = yield* (0, effection_1.all)(tasks.map(t => ctx.spawn({ ...t, parent: t.parent ?? ctx.spine })));
|
|
22
|
+
yield* (0, effection_1.all)(agents.map(a => ctx.waitFor(a)));
|
|
23
|
+
};
|
|
24
|
+
exports.parallel = parallel;
|
|
25
|
+
/**
|
|
26
|
+
* Chain orchestrator — sequential execution. Each step may extend the shared
|
|
27
|
+
* root with its findings before the next step forks from the extended position.
|
|
28
|
+
*
|
|
29
|
+
* The second argument maps each item to a ChainStep, so callers can compute
|
|
30
|
+
* per-task prompts and spine labels from their own data model without
|
|
31
|
+
* coupling the factory to a particular task type.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* yield* agentPool({
|
|
36
|
+
* tools: [...],
|
|
37
|
+
* parent: querySpine,
|
|
38
|
+
* orchestrate: chain(researchTasks, (task, i) => ({
|
|
39
|
+
* task: { content: taskToContent(task), systemPrompt: renderWorker({ taskIndex: i }) },
|
|
40
|
+
* userContent: `Research task: ${task.description}`,
|
|
41
|
+
* })),
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @category Agents
|
|
46
|
+
*/
|
|
47
|
+
const chain = (items, toStep) => function* (ctx) {
|
|
48
|
+
for (const [i, item] of items.entries()) {
|
|
49
|
+
const step = toStep(item, i);
|
|
50
|
+
if (step.beforeSpawn)
|
|
51
|
+
yield* step.beforeSpawn();
|
|
52
|
+
const agent = yield* ctx.waitFor(yield* ctx.spawn({ ...step.task, parent: step.task.parent ?? ctx.spine }));
|
|
53
|
+
const delta = agent.result && step.userContent
|
|
54
|
+
? yield* ctx.extendSpine(step.userContent, agent.result)
|
|
55
|
+
: 0;
|
|
56
|
+
if (step.afterExtend)
|
|
57
|
+
yield* step.afterExtend(delta, ctx.spine.position);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
exports.chain = chain;
|
|
61
|
+
/**
|
|
62
|
+
* Fanout orchestrator — landscape task first (optionally extending the spine),
|
|
63
|
+
* then N independent domain tasks in parallel. Domain tasks fork from the
|
|
64
|
+
* post-landscape root and do NOT see each other's findings.
|
|
65
|
+
*
|
|
66
|
+
* The canonical shape for multi-domain queries: one landscape survey that
|
|
67
|
+
* loads vocabulary into the spine, then one task per independent domain.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* yield* agentPool({
|
|
72
|
+
* orchestrate: fanout(
|
|
73
|
+
* { task: { content: landscapeQuery, systemPrompt: WORKER }, userContent: 'Landscape survey' },
|
|
74
|
+
* domainQueries.map(q => ({ content: q, systemPrompt: WORKER })),
|
|
75
|
+
* ),
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @category Agents
|
|
80
|
+
*/
|
|
81
|
+
const fanout = (landscape, domains) => function* (ctx) {
|
|
82
|
+
const l = yield* ctx.waitFor(yield* ctx.spawn({ ...landscape.task, parent: landscape.task.parent ?? ctx.spine }));
|
|
83
|
+
if (l.result && landscape.userContent) {
|
|
84
|
+
yield* ctx.extendSpine(landscape.userContent, l.result);
|
|
85
|
+
}
|
|
86
|
+
const agents = yield* (0, effection_1.all)(domains.map(d => ctx.spawn({ ...d, parent: d.parent ?? ctx.spine })));
|
|
87
|
+
yield* (0, effection_1.all)(agents.map(a => ctx.waitFor(a)));
|
|
88
|
+
};
|
|
89
|
+
exports.fanout = fanout;
|
|
90
|
+
/**
|
|
91
|
+
* DAG orchestrator — lazy spawn on dependency resolution. Independent nodes
|
|
92
|
+
* run in parallel; dependent nodes wait until their dependencies complete
|
|
93
|
+
* (and their findings extend the spine) before forking.
|
|
94
|
+
*
|
|
95
|
+
* Subsumes the design in `docs/dag-pool.md` — DAG is an orchestration
|
|
96
|
+
* pattern expressed on top of the general primitive, not a pool internals
|
|
97
|
+
* change.
|
|
98
|
+
*
|
|
99
|
+
* @category Agents
|
|
100
|
+
*/
|
|
101
|
+
const dag = (nodes) => {
|
|
102
|
+
validateDAG(nodes);
|
|
103
|
+
return function* (ctx) {
|
|
104
|
+
// Each node runs as a child Task. Dependencies are expressed by
|
|
105
|
+
// awaiting the dep's Task (`yield* depTask`) — Task<T> extends
|
|
106
|
+
// Future<T> extends Operation<T>, so this is the canonical Effection
|
|
107
|
+
// cross-task rendezvous (see frontside.com/effection/api/v4/Task).
|
|
108
|
+
//
|
|
109
|
+
// Why this beats the older recursive-spawnNode-with-Sets approach:
|
|
110
|
+
// - No mutable bookkeeping. The "node N is done" signal IS the
|
|
111
|
+
// Task itself; the runtime tracks lifetimes for free.
|
|
112
|
+
// - No race window for double-spawn. Each node spawns exactly
|
|
113
|
+
// once, by definition (one entry per `tasks.set`).
|
|
114
|
+
// - Failure propagates through the dependency edges automatically:
|
|
115
|
+
// if node A throws, every task awaiting A's Task receives the
|
|
116
|
+
// same error, and structured concurrency halts the rest.
|
|
117
|
+
const tasks = new Map();
|
|
118
|
+
function* runNode(n) {
|
|
119
|
+
// Gate: wait for every declared dep's task to complete. The map is
|
|
120
|
+
// fully populated before any node body runs (spawned tasks don't
|
|
121
|
+
// execute until the parent yields, and the spawn loop below is
|
|
122
|
+
// synchronous between iterations).
|
|
123
|
+
for (const depId of n.dependsOn ?? []) {
|
|
124
|
+
yield* tasks.get(depId);
|
|
125
|
+
}
|
|
126
|
+
const agent = yield* ctx.waitFor(yield* ctx.spawn({ ...n.task, parent: n.task.parent ?? ctx.spine }));
|
|
127
|
+
if (agent.result && n.userContent) {
|
|
128
|
+
yield* ctx.extendSpine(n.userContent, agent.result);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const n of nodes) {
|
|
132
|
+
tasks.set(n.id, yield* (0, effection_1.spawn)(() => runNode(n)));
|
|
133
|
+
}
|
|
134
|
+
// Await every task. Roots run first (no deps to await); descendants
|
|
135
|
+
// unblock as their deps complete. Any throw inside a node propagates
|
|
136
|
+
// here and halts the rest via structured concurrency.
|
|
137
|
+
for (const t of tasks.values())
|
|
138
|
+
yield* t;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
exports.dag = dag;
|
|
142
|
+
function validateDAG(nodes) {
|
|
143
|
+
const ids = new Set(nodes.map(n => n.id));
|
|
144
|
+
const duplicates = nodes.filter((n, i) => nodes.findIndex(m => m.id === n.id) !== i);
|
|
145
|
+
if (duplicates.length > 0) {
|
|
146
|
+
throw new Error(`dag: duplicate node ids: ${duplicates.map(n => n.id).join(', ')}`);
|
|
147
|
+
}
|
|
148
|
+
for (const n of nodes) {
|
|
149
|
+
for (const dep of n.dependsOn ?? []) {
|
|
150
|
+
if (!ids.has(dep))
|
|
151
|
+
throw new Error(`dag: node '${n.id}' depends on unknown node '${dep}'`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Cycle detection via DFS
|
|
155
|
+
const visiting = new Set();
|
|
156
|
+
const visited = new Set();
|
|
157
|
+
const byId = new Map(nodes.map(n => [n.id, n]));
|
|
158
|
+
function visit(id, path) {
|
|
159
|
+
if (visited.has(id))
|
|
160
|
+
return;
|
|
161
|
+
if (visiting.has(id))
|
|
162
|
+
throw new Error(`dag: cycle detected: ${[...path, id].join(' -> ')}`);
|
|
163
|
+
visiting.add(id);
|
|
164
|
+
const node = byId.get(id);
|
|
165
|
+
for (const dep of node?.dependsOn ?? [])
|
|
166
|
+
visit(dep, [...path, id]);
|
|
167
|
+
visiting.delete(id);
|
|
168
|
+
visited.add(id);
|
|
169
|
+
}
|
|
170
|
+
for (const n of nodes)
|
|
171
|
+
visit(n.id, []);
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=orchestrators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrators.js","sourceRoot":"","sources":["../src/orchestrators.ts"],"names":[],"mappings":"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"}
|
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Operation } from 'effection';
|
|
2
|
+
import { Branch } from '@lloyal-labs/sdk';
|
|
3
|
+
import type { TraceEvent } from './trace-types';
|
|
4
|
+
/**
|
|
5
|
+
* Serialized state needed to reconstruct a Branch deterministically.
|
|
6
|
+
*
|
|
7
|
+
* `seedPrompt` is the tokenized prompt that initialized a spine — the
|
|
8
|
+
* full formatted chat template including system prompt and any tool schemas.
|
|
9
|
+
* Captured from `prompt:format` events with `role: 'spine'`.
|
|
10
|
+
*
|
|
11
|
+
* `turns` is an ordered list of user/assistant turn pairs that were prefilled
|
|
12
|
+
* on top of the spine via {@link extendSpine}. Empty for pool-start replay
|
|
13
|
+
* (any orchestration shape that doesn't extend the spine — e.g. `parallel`);
|
|
14
|
+
* populated for spine-extending orchestrations (`chain`, `fanout`, `dag`).
|
|
15
|
+
*
|
|
16
|
+
* @category Agents
|
|
17
|
+
*/
|
|
18
|
+
export interface BranchCheckpoint {
|
|
19
|
+
seedPrompt: string;
|
|
20
|
+
turns: Array<{
|
|
21
|
+
userContent: string;
|
|
22
|
+
assistantContent: string;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract the spine seed prompt from a trace, with no spine extensions.
|
|
27
|
+
*
|
|
28
|
+
* Useful for replaying parallel orchestrations, or for forking a fresh
|
|
29
|
+
* agent off the same pool-start state for A/B experiments.
|
|
30
|
+
*
|
|
31
|
+
* @param events - parsed JSONL trace events, in emission order
|
|
32
|
+
* @throws If no `prompt:format` event with `role: 'spine'` is found.
|
|
33
|
+
*
|
|
34
|
+
* @category Agents
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractSpineSeed(events: TraceEvent[]): BranchCheckpoint;
|
|
37
|
+
/**
|
|
38
|
+
* Extract a full spine checkpoint — spine seed prompt plus every
|
|
39
|
+
* `spine:extend` event in emission order.
|
|
40
|
+
*
|
|
41
|
+
* When `opts.poolTraceId` is set, only spine extensions under that pool's
|
|
42
|
+
* scope are included (useful when a trace contains multiple nested or
|
|
43
|
+
* sequential pools with independent spines — typically the research pool
|
|
44
|
+
* vs. a later synthesis pool, both extending their own spines).
|
|
45
|
+
*
|
|
46
|
+
* @param events - parsed JSONL trace events, in emission order
|
|
47
|
+
* @param opts.poolTraceId - filter extensions to this pool's scope
|
|
48
|
+
* @throws If no spine seed prompt is found.
|
|
49
|
+
*
|
|
50
|
+
* @category Agents
|
|
51
|
+
*/
|
|
52
|
+
export declare function extractSpineCheckpoint(events: TraceEvent[], opts?: {
|
|
53
|
+
poolTraceId?: number;
|
|
54
|
+
}): BranchCheckpoint;
|
|
55
|
+
/**
|
|
56
|
+
* Materialize a Branch reflecting the checkpointed state.
|
|
57
|
+
*
|
|
58
|
+
* Creates a fresh spine at position 0 in the active `SessionContext`, prefills
|
|
59
|
+
* the tokenized seed prompt, then applies each turn delta via `buildTurnDelta`
|
|
60
|
+
* + `store.prefill`. Registers an `ensure()` so the subtree is pruned when
|
|
61
|
+
* the caller's scope exits — lifetime follows the enclosing `scoped()` or
|
|
62
|
+
* `resource()`, matching how `withSpine` manages its own spine.
|
|
63
|
+
*
|
|
64
|
+
* Pass the returned branch as `parent` to `agentPool` to run a replacement
|
|
65
|
+
* stage (synth re-run, single-agent replay with modified prompt, etc.) against
|
|
66
|
+
* the reconstructed KV state.
|
|
67
|
+
*
|
|
68
|
+
* @example Replay a pool-start (parallel orchestration) with a modified task
|
|
69
|
+
* ```ts
|
|
70
|
+
* const events = parseTrace(tracePath);
|
|
71
|
+
* const checkpoint = extractSpineSeed(events);
|
|
72
|
+
* const spine = yield* reconstructBranch(checkpoint);
|
|
73
|
+
* yield* agentPool({
|
|
74
|
+
* parent: spine,
|
|
75
|
+
* orchestrate: parallel([{ content: modifiedTask, systemPrompt: modifiedSys }]),
|
|
76
|
+
* ...
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example Replay a spine-chain (research) state with a different synth prompt
|
|
81
|
+
* ```ts
|
|
82
|
+
* const events = parseTrace(tracePath);
|
|
83
|
+
* const checkpoint = extractSpineCheckpoint(events);
|
|
84
|
+
* const spine = yield* reconstructBranch(checkpoint);
|
|
85
|
+
* yield* agentPool({
|
|
86
|
+
* parent: spine,
|
|
87
|
+
* orchestrate: parallel([{ content: SYNTHESIZE.user }]),
|
|
88
|
+
* systemPrompt: SYNTHESIZE.system,
|
|
89
|
+
* ...
|
|
90
|
+
* });
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @category Agents
|
|
94
|
+
*/
|
|
95
|
+
export declare function reconstructBranch(checkpoint: BranchCheckpoint): Operation<Branch>;
|
|
96
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,gBAAgB,CAWvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,EAAE,EACpB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,gBAAgB,CASlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAiB,iBAAiB,CAAC,UAAU,EAAE,gBAAgB,GAAG,SAAS,CAAC,MAAM,CAAC,CAgBlF"}
|
package/dist/replay.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractSpineSeed = extractSpineSeed;
|
|
4
|
+
exports.extractSpineCheckpoint = extractSpineCheckpoint;
|
|
5
|
+
exports.reconstructBranch = reconstructBranch;
|
|
6
|
+
const effection_1 = require("effection");
|
|
7
|
+
const sdk_1 = require("@lloyal-labs/sdk");
|
|
8
|
+
const context_1 = require("./context");
|
|
9
|
+
/**
|
|
10
|
+
* Extract the spine seed prompt from a trace, with no spine extensions.
|
|
11
|
+
*
|
|
12
|
+
* Useful for replaying parallel orchestrations, or for forking a fresh
|
|
13
|
+
* agent off the same pool-start state for A/B experiments.
|
|
14
|
+
*
|
|
15
|
+
* @param events - parsed JSONL trace events, in emission order
|
|
16
|
+
* @throws If no `prompt:format` event with `role: 'spine'` is found.
|
|
17
|
+
*
|
|
18
|
+
* @category Agents
|
|
19
|
+
*/
|
|
20
|
+
function extractSpineSeed(events) {
|
|
21
|
+
const seed = events.find((e) => e.type === 'prompt:format' && e.role === 'spine');
|
|
22
|
+
if (!seed) {
|
|
23
|
+
throw new Error('extractSpineSeed: no prompt:format event with role=spine found in trace');
|
|
24
|
+
}
|
|
25
|
+
return { seedPrompt: seed.promptText, turns: [] };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extract a full spine checkpoint — spine seed prompt plus every
|
|
29
|
+
* `spine:extend` event in emission order.
|
|
30
|
+
*
|
|
31
|
+
* When `opts.poolTraceId` is set, only spine extensions under that pool's
|
|
32
|
+
* scope are included (useful when a trace contains multiple nested or
|
|
33
|
+
* sequential pools with independent spines — typically the research pool
|
|
34
|
+
* vs. a later synthesis pool, both extending their own spines).
|
|
35
|
+
*
|
|
36
|
+
* @param events - parsed JSONL trace events, in emission order
|
|
37
|
+
* @param opts.poolTraceId - filter extensions to this pool's scope
|
|
38
|
+
* @throws If no spine seed prompt is found.
|
|
39
|
+
*
|
|
40
|
+
* @category Agents
|
|
41
|
+
*/
|
|
42
|
+
function extractSpineCheckpoint(events, opts = {}) {
|
|
43
|
+
const seed = extractSpineSeed(events);
|
|
44
|
+
const turns = [];
|
|
45
|
+
for (const e of events) {
|
|
46
|
+
if (e.type !== 'spine:extend')
|
|
47
|
+
continue;
|
|
48
|
+
if (opts.poolTraceId != null && e.parentTraceId !== opts.poolTraceId)
|
|
49
|
+
continue;
|
|
50
|
+
turns.push({ userContent: e.userContent, assistantContent: e.assistantContent });
|
|
51
|
+
}
|
|
52
|
+
return { seedPrompt: seed.seedPrompt, turns };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Materialize a Branch reflecting the checkpointed state.
|
|
56
|
+
*
|
|
57
|
+
* Creates a fresh spine at position 0 in the active `SessionContext`, prefills
|
|
58
|
+
* the tokenized seed prompt, then applies each turn delta via `buildTurnDelta`
|
|
59
|
+
* + `store.prefill`. Registers an `ensure()` so the subtree is pruned when
|
|
60
|
+
* the caller's scope exits — lifetime follows the enclosing `scoped()` or
|
|
61
|
+
* `resource()`, matching how `withSpine` manages its own spine.
|
|
62
|
+
*
|
|
63
|
+
* Pass the returned branch as `parent` to `agentPool` to run a replacement
|
|
64
|
+
* stage (synth re-run, single-agent replay with modified prompt, etc.) against
|
|
65
|
+
* the reconstructed KV state.
|
|
66
|
+
*
|
|
67
|
+
* @example Replay a pool-start (parallel orchestration) with a modified task
|
|
68
|
+
* ```ts
|
|
69
|
+
* const events = parseTrace(tracePath);
|
|
70
|
+
* const checkpoint = extractSpineSeed(events);
|
|
71
|
+
* const spine = yield* reconstructBranch(checkpoint);
|
|
72
|
+
* yield* agentPool({
|
|
73
|
+
* parent: spine,
|
|
74
|
+
* orchestrate: parallel([{ content: modifiedTask, systemPrompt: modifiedSys }]),
|
|
75
|
+
* ...
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example Replay a spine-chain (research) state with a different synth prompt
|
|
80
|
+
* ```ts
|
|
81
|
+
* const events = parseTrace(tracePath);
|
|
82
|
+
* const checkpoint = extractSpineCheckpoint(events);
|
|
83
|
+
* const spine = yield* reconstructBranch(checkpoint);
|
|
84
|
+
* yield* agentPool({
|
|
85
|
+
* parent: spine,
|
|
86
|
+
* orchestrate: parallel([{ content: SYNTHESIZE.user }]),
|
|
87
|
+
* systemPrompt: SYNTHESIZE.system,
|
|
88
|
+
* ...
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @category Agents
|
|
93
|
+
*/
|
|
94
|
+
function* reconstructBranch(checkpoint) {
|
|
95
|
+
const ctx = yield* context_1.Ctx.expect();
|
|
96
|
+
const store = yield* context_1.Store.expect();
|
|
97
|
+
const spine = sdk_1.Branch.create(ctx, 0, {});
|
|
98
|
+
yield* (0, effection_1.ensure)(() => { if (!spine.disposed)
|
|
99
|
+
spine.pruneSubtreeSync(); });
|
|
100
|
+
const seedTokens = ctx.tokenizeSync(checkpoint.seedPrompt, false);
|
|
101
|
+
yield* (0, effection_1.call)(() => spine.prefill(seedTokens));
|
|
102
|
+
for (const turn of checkpoint.turns) {
|
|
103
|
+
const delta = (0, sdk_1.buildTurnDelta)(ctx, turn.userContent, turn.assistantContent);
|
|
104
|
+
yield* (0, effection_1.call)(() => store.prefill([[spine, delta]]));
|
|
105
|
+
}
|
|
106
|
+
return spine;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":";;AAoCA,4CAWC;AAiBD,wDAYC;AA0CD,8CAgBC;AAtID,yCAAyC;AAEzC,0CAA0D;AAC1D,uCAAuC;AAsBvC;;;;;;;;;;GAUG;AACH,SAAgB,gBAAgB,CAAC,MAAoB;IACnD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CACtB,CAAC,CAAC,EAAuD,EAAE,CACzD,CAAC,CAAC,IAAI,KAAK,eAAe,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,CACnD,CAAC;IACF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,sBAAsB,CACpC,MAAoB,EACpB,OAAiC,EAAE;IAEnC,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,KAAK,GAA8B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc;YAAE,SAAS;QACxC,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,IAAI,CAAC,CAAC,aAAa,KAAK,IAAI,CAAC,WAAW;YAAE,SAAS;QAC/E,KAAK,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,QAAe,CAAC,CAAC,iBAAiB,CAAC,UAA4B;IAC7D,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,aAAG,CAAC,MAAM,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,eAAK,CAAC,MAAM,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAG,YAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACxC,KAAK,CAAC,CAAC,IAAA,kBAAM,EAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ;QAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAExE,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClE,KAAK,CAAC,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,IAAA,oBAAc,EAAC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC3E,KAAK,CAAC,CAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/spine.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { Operation } from "effection";
|
|
2
|
+
import { Branch } from "@lloyal-labs/sdk";
|
|
3
|
+
import type { Tool } from "./Tool";
|
|
4
|
+
import type { SamplingParams } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for {@link withSpine}
|
|
7
|
+
*
|
|
8
|
+
* @category Agents
|
|
9
|
+
*/
|
|
10
|
+
export interface SpineOptions {
|
|
11
|
+
/** Sampling parameters for the spine branch */
|
|
12
|
+
params?: SamplingParams;
|
|
13
|
+
/**
|
|
14
|
+
* Set ScratchpadParent context so tools can fork from the spine
|
|
15
|
+
* for scratchpad extraction (fork-attend-extract-prune pattern).
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
enableScratchpad?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Fork the spine from this branch instead of creating at position 0.
|
|
21
|
+
*
|
|
22
|
+
* When provided, the spine inherits the parent's full KV state —
|
|
23
|
+
* every tool call, tool result, and generated token the parent
|
|
24
|
+
* accumulated. Sub-agents forking from this spine attend over the
|
|
25
|
+
* parent's complete attention state (Continuous Context).
|
|
26
|
+
*
|
|
27
|
+
* When omitted, creates a fresh spine at position 0 (cold start).
|
|
28
|
+
*/
|
|
29
|
+
parent?: Branch;
|
|
30
|
+
/**
|
|
31
|
+
* When set, prefill the chat-format `[system + tools]` header onto the
|
|
32
|
+
* spine once at setup. Every agent forking from the spine inherits these
|
|
33
|
+
* tokens via `forkSync`'s metadata-only KV prefix-share — the role and
|
|
34
|
+
* tool schemas appear ONCE in physical KV regardless of how many agents
|
|
35
|
+
* the pool spawns.
|
|
36
|
+
*
|
|
37
|
+
* The resulting `FormatConfig` (parser/grammar/format/triggers) is set
|
|
38
|
+
* on the {@link SpineFmt} context so `setupAgent` can detect shared mode,
|
|
39
|
+
* skip its own system+tools formatting, and inherit the dispatch-side
|
|
40
|
+
* fmt from the spine.
|
|
41
|
+
*
|
|
42
|
+
* Use this for orchestrators where every agent shares the same role —
|
|
43
|
+
* chain-mode research pools, fanout-style same-role pools, etc. Mixed-
|
|
44
|
+
* role workflows (research → compare → synthesize) keep using per-spec
|
|
45
|
+
* `SpawnSpec.systemPrompt` and don't pass this option.
|
|
46
|
+
*/
|
|
47
|
+
systemPrompt?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Tools whose schemas embed into the chat-format header prefilled at setup.
|
|
50
|
+
* Their JSON schemas are decoded into the spine's KV ONCE; every agent
|
|
51
|
+
* forking from the spine inherits the schema tokens via fork prefix-share
|
|
52
|
+
* instead of re-emitting them in its own suffix.
|
|
53
|
+
*
|
|
54
|
+
* The same `Tool[]` is typically also passed to `agentPool` (where it
|
|
55
|
+
* becomes the dispatcher registry). Two roles, one input — schemas
|
|
56
|
+
* decoded once into the spine, instances registered for runtime
|
|
57
|
+
* `tool.execute()` dispatch.
|
|
58
|
+
*
|
|
59
|
+
* Only applied when `systemPrompt` is also set (shared mode); ignored
|
|
60
|
+
* otherwise.
|
|
61
|
+
*/
|
|
62
|
+
tools?: Tool[];
|
|
63
|
+
/**
|
|
64
|
+
* Whether to enable thinking-mode tokens (e.g. `<think>` blocks) when
|
|
65
|
+
* formatting the spine's chat-format header. Threaded through to the
|
|
66
|
+
* chat-format call AND stored on the `SpineFmt` FormatConfig so
|
|
67
|
+
* `setupAgent`'s shared-mode shortcut copies a parser/grammar/triggers
|
|
68
|
+
* configuration consistent with the per-agent suffix formatting.
|
|
69
|
+
*
|
|
70
|
+
* Should match the `enableThinking` value the caller passes to the agent
|
|
71
|
+
* pool — divergent values produce inconsistent grammar between the
|
|
72
|
+
* prefilled spine and per-agent suffixes.
|
|
73
|
+
*
|
|
74
|
+
* @default false
|
|
75
|
+
*/
|
|
76
|
+
enableThinking?: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Scoped spine branch with guaranteed cleanup
|
|
80
|
+
*
|
|
81
|
+
* Creates (or forks) the pool's spine — the shared KV line that agents
|
|
82
|
+
* fork from and that `ctx.extendSpine` writes onto between tasks. The
|
|
83
|
+
* spine is pruned via try/finally when the body returns or throws,
|
|
84
|
+
* regardless of whether children still exist.
|
|
85
|
+
*
|
|
86
|
+
* Each agent's chat format (system + user + generation prompt) is rendered
|
|
87
|
+
* fresh inside `setupAgent`, so this spine carries no chat context itself —
|
|
88
|
+
* it exists as the pool's branching point and as the line that
|
|
89
|
+
* `ctx.extendSpine` writes onto between tasks.
|
|
90
|
+
*
|
|
91
|
+
* **Cold path** (no `parent`): creates a spine at position 0 with no prefill.
|
|
92
|
+
* Agents fork at position 0; their full chat context lives in their own suffix.
|
|
93
|
+
*
|
|
94
|
+
* **Warm path** (`parent` provided): forks from parent and prefills a turn
|
|
95
|
+
* separator so subsequent agent suffixes land on a clean turn boundary.
|
|
96
|
+
* Sub-agents inherit the parent's full KV state via the fork.
|
|
97
|
+
*
|
|
98
|
+
* @param opts - Sampling parameters and optional parent branch
|
|
99
|
+
* @param body - Operation that receives the spine branch and prefix length.
|
|
100
|
+
* Typically calls {@link useAgentPool} inside.
|
|
101
|
+
* @returns The body's return value
|
|
102
|
+
*
|
|
103
|
+
* @category Agents
|
|
104
|
+
*/
|
|
105
|
+
export declare function withSpine<T>(opts: SpineOptions, body: (spine: Branch, prefixLength: number) => Operation<T>): Operation<T>;
|
|
106
|
+
//# sourceMappingURL=spine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spine.d.ts","sourceRoot":"","sources":["../src/spine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAiB,SAAS,CAAC,CAAC,EAC1B,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAC1D,SAAS,CAAC,CAAC,CAAC,CAwHd"}
|