@topce/pizx 0.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/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3146 -0
- package/dist/cli.js.map +7 -0
- package/dist/globals.d.ts +15 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2963 -0
- package/dist/index.js.map +7 -0
- package/dist/pi-agent.d.ts +58 -0
- package/dist/pi-agent.d.ts.map +1 -0
- package/dist/pi-output.d.ts +37 -0
- package/dist/pi-output.d.ts.map +1 -0
- package/dist/pi.d.ts +33 -0
- package/dist/pi.d.ts.map +1 -0
- package/package.json +96 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2963 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export * from "zx";
|
|
3
|
+
|
|
4
|
+
// src/patterns/types.ts
|
|
5
|
+
import { completeSimple, getEnvApiKey, getModels, getProviders } from "@earendil-works/pi-ai";
|
|
6
|
+
|
|
7
|
+
// src/load-pi-settings.ts
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
function getPiAgentDir() {
|
|
12
|
+
const envDir = process.env.PI_CODING_AGENT_DIR;
|
|
13
|
+
if (envDir) return envDir;
|
|
14
|
+
return join(homedir(), ".pi", "agent");
|
|
15
|
+
}
|
|
16
|
+
function isPiInstalled() {
|
|
17
|
+
return existsSync(join(getPiAgentDir(), "auth.json"));
|
|
18
|
+
}
|
|
19
|
+
function loadPiSettings(agentDir) {
|
|
20
|
+
const dir = agentDir ?? getPiAgentDir();
|
|
21
|
+
const path = join(dir, "settings.json");
|
|
22
|
+
if (!existsSync(path)) return {};
|
|
23
|
+
try {
|
|
24
|
+
const raw = readFileSync(path, "utf-8");
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
return {
|
|
27
|
+
defaultModel: parsed.defaultModel ?? void 0,
|
|
28
|
+
defaultProvider: parsed.defaultProvider ?? void 0,
|
|
29
|
+
defaultThinkingLevel: parsed.defaultThinkingLevel ?? void 0
|
|
30
|
+
};
|
|
31
|
+
} catch {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/patterns/types.ts
|
|
37
|
+
var PatternOutput = class {
|
|
38
|
+
constructor(text, startTime = Date.now(), endTime = Date.now()) {
|
|
39
|
+
this.text = text;
|
|
40
|
+
this.startTime = startTime;
|
|
41
|
+
this.endTime = endTime;
|
|
42
|
+
}
|
|
43
|
+
text;
|
|
44
|
+
startTime;
|
|
45
|
+
endTime;
|
|
46
|
+
/** Duration in milliseconds */
|
|
47
|
+
get duration() {
|
|
48
|
+
return this.endTime - this.startTime;
|
|
49
|
+
}
|
|
50
|
+
toString() {
|
|
51
|
+
return this.text;
|
|
52
|
+
}
|
|
53
|
+
valueOf() {
|
|
54
|
+
return this.text;
|
|
55
|
+
}
|
|
56
|
+
[Symbol.toPrimitive]() {
|
|
57
|
+
return this.text;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var PatternPromise = class extends Promise {
|
|
61
|
+
};
|
|
62
|
+
function build(pieces, args) {
|
|
63
|
+
let s = "";
|
|
64
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
65
|
+
s += pieces[i];
|
|
66
|
+
if (i < args.length) s += String(args[i]);
|
|
67
|
+
}
|
|
68
|
+
return s.trim();
|
|
69
|
+
}
|
|
70
|
+
var _piSettings;
|
|
71
|
+
function getPiDefaults() {
|
|
72
|
+
if (_piSettings === void 0) {
|
|
73
|
+
_piSettings = isPiInstalled() ? loadPiSettings() : {};
|
|
74
|
+
}
|
|
75
|
+
return _piSettings;
|
|
76
|
+
}
|
|
77
|
+
function allModels() {
|
|
78
|
+
const result = [];
|
|
79
|
+
for (const p of getProviders()) {
|
|
80
|
+
const ms = getModels(p);
|
|
81
|
+
if (ms && ms.length > 0) result.push(...ms);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function getConfiguredProviders() {
|
|
86
|
+
return getProviders().filter((p) => getEnvApiKey(p) !== void 0);
|
|
87
|
+
}
|
|
88
|
+
function configuredModels() {
|
|
89
|
+
const configured = new Set(getConfiguredProviders());
|
|
90
|
+
return allModels().filter((m) => configured.has(m.provider));
|
|
91
|
+
}
|
|
92
|
+
function findModelById(id) {
|
|
93
|
+
const all = allModels();
|
|
94
|
+
if (id.includes("/")) {
|
|
95
|
+
const [provider, modelId] = id.split("/", 2);
|
|
96
|
+
return all.find(
|
|
97
|
+
(m) => m.provider === provider && (m.id === modelId || m.id.endsWith(`/${modelId}`))
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return all.find((m) => m.id === id || m.id.endsWith(`/${id}`));
|
|
101
|
+
}
|
|
102
|
+
function pickModel(preferred) {
|
|
103
|
+
if (preferred) {
|
|
104
|
+
const hit = findModelById(preferred);
|
|
105
|
+
if (hit) return hit;
|
|
106
|
+
}
|
|
107
|
+
const settings = getPiDefaults();
|
|
108
|
+
if (settings.defaultModel) {
|
|
109
|
+
const hit = findModelById(settings.defaultModel);
|
|
110
|
+
if (hit) return hit;
|
|
111
|
+
}
|
|
112
|
+
if (settings.defaultProvider) {
|
|
113
|
+
const providerModels = getModels(settings.defaultProvider);
|
|
114
|
+
if (providerModels && providerModels.length > 0) {
|
|
115
|
+
const configured = new Set(getConfiguredProviders());
|
|
116
|
+
if (configured.has(settings.defaultProvider)) {
|
|
117
|
+
return providerModels[0];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const available = configuredModels();
|
|
122
|
+
if (available.length > 0) {
|
|
123
|
+
const order2 = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
|
|
124
|
+
for (const id of order2) {
|
|
125
|
+
const m = available.find((m2) => m2.id.includes(id));
|
|
126
|
+
if (m) return m;
|
|
127
|
+
}
|
|
128
|
+
return available[0];
|
|
129
|
+
}
|
|
130
|
+
const models = allModels();
|
|
131
|
+
if (models.length === 0) return void 0;
|
|
132
|
+
const order = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
|
|
133
|
+
for (const id of order) {
|
|
134
|
+
const m = models.find((m2) => m2.id.includes(id));
|
|
135
|
+
if (m) return m;
|
|
136
|
+
}
|
|
137
|
+
return models[0];
|
|
138
|
+
}
|
|
139
|
+
async function ask(prompt, opts = {}) {
|
|
140
|
+
const model = pickModel(opts.model);
|
|
141
|
+
if (!model) throw new Error("pizx/patterns: No AI models configured. Run `pi auth login` first.");
|
|
142
|
+
const result = await completeSimple(
|
|
143
|
+
model,
|
|
144
|
+
{
|
|
145
|
+
systemPrompt: opts.system,
|
|
146
|
+
messages: [{ role: "user", content: prompt, timestamp: Date.now() }]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
maxTokens: opts.maxTokens ?? 4096,
|
|
150
|
+
reasoning: opts.thinkingLevel ?? "medium"
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
154
|
+
return text.trim();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/patterns/adaptive.ts
|
|
158
|
+
var defaults = {
|
|
159
|
+
maxTokens: 4096,
|
|
160
|
+
thinkingLevel: "medium",
|
|
161
|
+
maxSteps: 5,
|
|
162
|
+
qualityThreshold: 0.8
|
|
163
|
+
};
|
|
164
|
+
var AdaptiveStep = class {
|
|
165
|
+
constructor(step, action, result, quality, adaptation) {
|
|
166
|
+
this.step = step;
|
|
167
|
+
this.action = action;
|
|
168
|
+
this.result = result;
|
|
169
|
+
this.quality = quality;
|
|
170
|
+
this.adaptation = adaptation;
|
|
171
|
+
}
|
|
172
|
+
step;
|
|
173
|
+
action;
|
|
174
|
+
result;
|
|
175
|
+
quality;
|
|
176
|
+
adaptation;
|
|
177
|
+
};
|
|
178
|
+
var AdaptiveOutput = class extends PatternOutput {
|
|
179
|
+
constructor(text, finalResult, steps, totalSteps, startTime, endTime) {
|
|
180
|
+
super(text, startTime, endTime);
|
|
181
|
+
this.finalResult = finalResult;
|
|
182
|
+
this.steps = steps;
|
|
183
|
+
this.totalSteps = totalSteps;
|
|
184
|
+
}
|
|
185
|
+
finalResult;
|
|
186
|
+
steps;
|
|
187
|
+
totalSteps;
|
|
188
|
+
};
|
|
189
|
+
var PLAN_SYSTEM = `You are an adaptive workflow planner. Given a goal, produce a step-by-step execution plan.
|
|
190
|
+
|
|
191
|
+
Output format:
|
|
192
|
+
PLAN:
|
|
193
|
+
1. Step description
|
|
194
|
+
2. Step description
|
|
195
|
+
...
|
|
196
|
+
|
|
197
|
+
Each step must be concrete and self-contained. Generate at most 5 steps.`;
|
|
198
|
+
var EXECUTE_SYSTEM = `You are a task executor. Execute the assigned step. Output your result directly \u2014 no meta-commentary. Be specific and actionable.`;
|
|
199
|
+
var EVALUATE_SYSTEM = `You are a quality evaluator. Review the execution result and provide:
|
|
200
|
+
1. Quality score: a number from 0.0 (poor) to 1.0 (perfect)
|
|
201
|
+
2. Brief assessment (1 sentence)
|
|
202
|
+
3. Adaptation recommendation: "CONTINUE" to proceed as planned, "REFINE" to redo this step, "SKIP_NEXT" to skip the next planned step, or "ADD [description]" to insert a new step before continuing
|
|
203
|
+
|
|
204
|
+
Output format:
|
|
205
|
+
SCORE: 0.XX
|
|
206
|
+
ASSESSMENT: (one sentence)
|
|
207
|
+
ADAPTATION: CONTINUE | REFINE | SKIP_NEXT | ADD (description)`;
|
|
208
|
+
async function execute(pieces, args, opts) {
|
|
209
|
+
const goal = build(pieces, args);
|
|
210
|
+
const t0 = Date.now();
|
|
211
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
212
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
213
|
+
if (!opts.quiet) {
|
|
214
|
+
process.stderr.write(`\u0391: Adaptive \u2014 "${goal.slice(0, 80)}${goal.length > 80 ? "..." : ""}"
|
|
215
|
+
`);
|
|
216
|
+
}
|
|
217
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
|
|
218
|
+
const planText = await ask(goal, {
|
|
219
|
+
model: plannerModel,
|
|
220
|
+
maxTokens: opts.maxTokens,
|
|
221
|
+
thinkingLevel: "high",
|
|
222
|
+
system: PLAN_SYSTEM
|
|
223
|
+
});
|
|
224
|
+
const planLines = planText.split("\n");
|
|
225
|
+
const plannedSteps = [];
|
|
226
|
+
for (const line of planLines) {
|
|
227
|
+
const match = line.match(/^\d+[.)]\s*(.+)/);
|
|
228
|
+
if (match) plannedSteps.push(match[1].trim());
|
|
229
|
+
}
|
|
230
|
+
const steps = plannedSteps.length > 0 ? plannedSteps : [goal];
|
|
231
|
+
if (!opts.quiet) {
|
|
232
|
+
process.stderr.write(` \u2192 ${steps.length} step(s) planned
|
|
233
|
+
`);
|
|
234
|
+
for (let i = 0; i < steps.length; i++) {
|
|
235
|
+
const s = steps[i];
|
|
236
|
+
process.stderr.write(` [${i + 1}] ${s.slice(0, 60)}${s.length > 60 ? "..." : ""}
|
|
237
|
+
`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const adaptiveSteps = [];
|
|
241
|
+
const maxSteps = opts.maxSteps ?? 5;
|
|
242
|
+
const threshold = opts.qualityThreshold ?? 0.8;
|
|
243
|
+
let stepIndex = 0;
|
|
244
|
+
let executionStep = 0;
|
|
245
|
+
while (stepIndex < steps.length && executionStep < maxSteps) {
|
|
246
|
+
executionStep++;
|
|
247
|
+
const currentStep = steps[stepIndex];
|
|
248
|
+
if (!opts.quiet)
|
|
249
|
+
process.stderr.write(` \u2192 Step ${executionStep}: ${currentStep.slice(0, 60)}...
|
|
250
|
+
`);
|
|
251
|
+
const result = await ask(currentStep, {
|
|
252
|
+
model: workerModel,
|
|
253
|
+
maxTokens: opts.maxTokens,
|
|
254
|
+
thinkingLevel: opts.thinkingLevel,
|
|
255
|
+
system: EXECUTE_SYSTEM
|
|
256
|
+
});
|
|
257
|
+
const evaluation = await ask(
|
|
258
|
+
`Goal: ${goal}
|
|
259
|
+
Step executed: ${currentStep}
|
|
260
|
+
Result: ${result}
|
|
261
|
+
|
|
262
|
+
Evaluate the result.`,
|
|
263
|
+
{
|
|
264
|
+
model: plannerModel,
|
|
265
|
+
maxTokens: 512,
|
|
266
|
+
thinkingLevel: "high",
|
|
267
|
+
system: EVALUATE_SYSTEM
|
|
268
|
+
}
|
|
269
|
+
);
|
|
270
|
+
const scoreMatch = evaluation.match(/SCORE:\s*([\d.]+)/i);
|
|
271
|
+
const assessMatch = evaluation.match(/ASSESSMENT:\s*(.+)/i);
|
|
272
|
+
const adaptMatch = evaluation.match(/ADAPTATION:\s*(.+)/i);
|
|
273
|
+
const quality = scoreMatch ? parseFloat(scoreMatch[1]) : 0.5;
|
|
274
|
+
const assessment = assessMatch?.[1] ?? "(no assessment)";
|
|
275
|
+
const adaptation = adaptMatch?.[1] ?? "CONTINUE";
|
|
276
|
+
adaptiveSteps.push(new AdaptiveStep(executionStep, currentStep, result, quality, adaptation));
|
|
277
|
+
if (!opts.quiet) {
|
|
278
|
+
process.stderr.write(` Quality: ${quality.toFixed(2)} | ${assessment.slice(0, 60)}...
|
|
279
|
+
`);
|
|
280
|
+
process.stderr.write(` Adaptation: ${adaptation}
|
|
281
|
+
`);
|
|
282
|
+
}
|
|
283
|
+
if (quality >= threshold) {
|
|
284
|
+
if (!opts.quiet)
|
|
285
|
+
process.stderr.write(` \u2713 Quality threshold (${threshold}) met \u2014 stopping early
|
|
286
|
+
`);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
const adaptUpper = adaptation.toUpperCase();
|
|
290
|
+
if (adaptUpper.startsWith("REFINE")) {
|
|
291
|
+
} else if (adaptUpper.startsWith("SKIP_NEXT")) {
|
|
292
|
+
stepIndex += 2;
|
|
293
|
+
} else if (adaptUpper.startsWith("ADD")) {
|
|
294
|
+
const newStep = adaptation.replace(/^ADD\s*/i, "");
|
|
295
|
+
steps.splice(stepIndex + 1, 0, newStep);
|
|
296
|
+
stepIndex++;
|
|
297
|
+
} else {
|
|
298
|
+
stepIndex++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const t1 = Date.now();
|
|
302
|
+
const finalResult = adaptiveSteps.length > 0 ? adaptiveSteps[adaptiveSteps.length - 1].result : "";
|
|
303
|
+
const summary = adaptiveSteps.map(
|
|
304
|
+
(s) => `Step ${s.step}: ${s.action.slice(0, 80)}...
|
|
305
|
+
Quality: ${s.quality.toFixed(2)}
|
|
306
|
+
Adaptation: ${s.adaptation}`
|
|
307
|
+
).join("\n\n");
|
|
308
|
+
return new AdaptiveOutput(summary, finalResult, adaptiveSteps, executionStep, t0, t1);
|
|
309
|
+
}
|
|
310
|
+
function makeAdaptive(opts = {}) {
|
|
311
|
+
const merged = { ...defaults, ...opts };
|
|
312
|
+
const fn = ((pieces, ...args) => {
|
|
313
|
+
if (!Array.isArray(pieces)) {
|
|
314
|
+
return makeAdaptive({ ...merged, ...pieces });
|
|
315
|
+
}
|
|
316
|
+
return new PatternPromise((resolve, reject) => {
|
|
317
|
+
execute(pieces, args, merged).then(resolve, reject);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
let _quiet;
|
|
321
|
+
Object.defineProperty(fn, "quiet", {
|
|
322
|
+
get() {
|
|
323
|
+
if (!_quiet) _quiet = makeAdaptive({ ...merged, quiet: true });
|
|
324
|
+
return _quiet;
|
|
325
|
+
},
|
|
326
|
+
enumerable: true,
|
|
327
|
+
configurable: true
|
|
328
|
+
});
|
|
329
|
+
return fn;
|
|
330
|
+
}
|
|
331
|
+
var \u0391 = makeAdaptive();
|
|
332
|
+
|
|
333
|
+
// src/patterns/broadcast.ts
|
|
334
|
+
var defaults2 = {
|
|
335
|
+
maxTokens: 4096,
|
|
336
|
+
thinkingLevel: "medium",
|
|
337
|
+
workers: 4
|
|
338
|
+
};
|
|
339
|
+
var ROLE_SETS = {
|
|
340
|
+
2: [
|
|
341
|
+
"Technical Expert \u2014 evaluate technical feasibility",
|
|
342
|
+
"Business Expert \u2014 evaluate business viability"
|
|
343
|
+
],
|
|
344
|
+
3: [
|
|
345
|
+
"Technical Expert \u2014 evaluate technical feasibility",
|
|
346
|
+
"Business Expert \u2014 evaluate business viability",
|
|
347
|
+
"User Expert \u2014 evaluate user experience and adoption"
|
|
348
|
+
],
|
|
349
|
+
4: [
|
|
350
|
+
"Technical Expert",
|
|
351
|
+
"Business Expert",
|
|
352
|
+
"User Expert",
|
|
353
|
+
"Risk Expert \u2014 identify risks, compliance, and security concerns"
|
|
354
|
+
],
|
|
355
|
+
5: [
|
|
356
|
+
"Technical Expert",
|
|
357
|
+
"Business Expert",
|
|
358
|
+
"User Expert",
|
|
359
|
+
"Risk Expert",
|
|
360
|
+
"Innovation Expert \u2014 suggest novel approaches and alternatives"
|
|
361
|
+
]
|
|
362
|
+
};
|
|
363
|
+
var BroadcastResponse = class {
|
|
364
|
+
constructor(role, response, success, error) {
|
|
365
|
+
this.role = role;
|
|
366
|
+
this.response = response;
|
|
367
|
+
this.success = success;
|
|
368
|
+
this.error = error;
|
|
369
|
+
}
|
|
370
|
+
role;
|
|
371
|
+
response;
|
|
372
|
+
success;
|
|
373
|
+
error;
|
|
374
|
+
};
|
|
375
|
+
var BroadcastOutput = class extends PatternOutput {
|
|
376
|
+
constructor(text, synthesis, responses, startTime, endTime) {
|
|
377
|
+
super(text, startTime, endTime);
|
|
378
|
+
this.synthesis = synthesis;
|
|
379
|
+
this.responses = responses;
|
|
380
|
+
}
|
|
381
|
+
synthesis;
|
|
382
|
+
responses;
|
|
383
|
+
};
|
|
384
|
+
var WORKER_PROMPT = `You are a {role}.
|
|
385
|
+
|
|
386
|
+
A question has been broadcast to you and your fellow specialists:
|
|
387
|
+
|
|
388
|
+
{question}
|
|
389
|
+
|
|
390
|
+
Provide your expert analysis and recommendation from your specific perspective.
|
|
391
|
+
Be thorough but concise \u2014 under 200 words.`;
|
|
392
|
+
var SYNTHESIS_SYSTEM = `You are a lead strategist. You broadcast a question to your specialist team. Now synthesize their collective responses into a coherent, actionable recommendation. Weigh conflicting opinions, identify consensus, and present the best path forward.`;
|
|
393
|
+
async function execute2(pieces, args, opts) {
|
|
394
|
+
const question = build(pieces, args);
|
|
395
|
+
const t0 = Date.now();
|
|
396
|
+
const workerCount = opts.workers ?? 4;
|
|
397
|
+
const roles = opts.roles ?? ROLE_SETS[workerCount] ?? ROLE_SETS[4] ?? [];
|
|
398
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
399
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
400
|
+
if (!opts.quiet) {
|
|
401
|
+
process.stderr.write(`\u0392: Broadcast \u2014 ${workerCount} worker(s)
|
|
402
|
+
`);
|
|
403
|
+
process.stderr.write(
|
|
404
|
+
` Question: "${question.slice(0, 80)}${question.length > 80 ? "..." : ""}"
|
|
405
|
+
`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Broadcasting to workers...\n");
|
|
409
|
+
const broadcastResults = await Promise.allSettled(
|
|
410
|
+
roles.map(async (role) => {
|
|
411
|
+
const prompt = WORKER_PROMPT.replace("{role}", role).replace("{question}", question);
|
|
412
|
+
const text = await ask(prompt, {
|
|
413
|
+
model: workerModel,
|
|
414
|
+
maxTokens: opts.maxTokens,
|
|
415
|
+
thinkingLevel: opts.thinkingLevel
|
|
416
|
+
});
|
|
417
|
+
return new BroadcastResponse(role, text, true);
|
|
418
|
+
})
|
|
419
|
+
);
|
|
420
|
+
const responses = [];
|
|
421
|
+
for (const r of broadcastResults) {
|
|
422
|
+
if (r.status === "fulfilled") {
|
|
423
|
+
responses.push(r.value);
|
|
424
|
+
} else {
|
|
425
|
+
const msg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
426
|
+
responses.push(new BroadcastResponse("(failed)", "", false, msg));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing responses...\n");
|
|
430
|
+
const responsesText = responses.map((wr) => `--- ${wr.role} ---
|
|
431
|
+
${wr.response}`).join("\n\n");
|
|
432
|
+
const synthesis = await ask(
|
|
433
|
+
`Original question:
|
|
434
|
+
${question}
|
|
435
|
+
|
|
436
|
+
Worker responses:
|
|
437
|
+
${responsesText}
|
|
438
|
+
|
|
439
|
+
Synthesize a cohesive, actionable recommendation.`,
|
|
440
|
+
{
|
|
441
|
+
model: plannerModel,
|
|
442
|
+
maxTokens: opts.maxTokens,
|
|
443
|
+
thinkingLevel: "high",
|
|
444
|
+
system: SYNTHESIS_SYSTEM
|
|
445
|
+
}
|
|
446
|
+
);
|
|
447
|
+
const t1 = Date.now();
|
|
448
|
+
const summary = responses.map(
|
|
449
|
+
(wr) => `[${wr.role}]: ${wr.response.slice(0, 150)}${wr.response.length > 150 ? "..." : ""}`
|
|
450
|
+
).join("\n");
|
|
451
|
+
return new BroadcastOutput(summary, synthesis, responses, t0, t1);
|
|
452
|
+
}
|
|
453
|
+
function makeBroadcast(opts = {}) {
|
|
454
|
+
const merged = { ...defaults2, ...opts };
|
|
455
|
+
const fn = ((pieces, ...args) => {
|
|
456
|
+
if (!Array.isArray(pieces)) {
|
|
457
|
+
return makeBroadcast({ ...merged, ...pieces });
|
|
458
|
+
}
|
|
459
|
+
return new PatternPromise((resolve, reject) => {
|
|
460
|
+
execute2(pieces, args, merged).then(resolve, reject);
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
let _quiet;
|
|
464
|
+
Object.defineProperty(fn, "quiet", {
|
|
465
|
+
get() {
|
|
466
|
+
if (!_quiet) _quiet = makeBroadcast({ ...merged, quiet: true });
|
|
467
|
+
return _quiet;
|
|
468
|
+
},
|
|
469
|
+
enumerable: true,
|
|
470
|
+
configurable: true
|
|
471
|
+
});
|
|
472
|
+
return fn;
|
|
473
|
+
}
|
|
474
|
+
var \u0392 = makeBroadcast();
|
|
475
|
+
|
|
476
|
+
// src/patterns/chi.ts
|
|
477
|
+
var defaults3 = {
|
|
478
|
+
maxTokens: 4096,
|
|
479
|
+
thinkingLevel: "high"
|
|
480
|
+
};
|
|
481
|
+
var LearningInsight = class {
|
|
482
|
+
constructor(category, pattern, recommendation, confidence) {
|
|
483
|
+
this.category = category;
|
|
484
|
+
this.pattern = pattern;
|
|
485
|
+
this.recommendation = recommendation;
|
|
486
|
+
this.confidence = confidence;
|
|
487
|
+
}
|
|
488
|
+
category;
|
|
489
|
+
pattern;
|
|
490
|
+
recommendation;
|
|
491
|
+
confidence;
|
|
492
|
+
};
|
|
493
|
+
var ChiOutput = class extends PatternOutput {
|
|
494
|
+
constructor(text, insights, summary, suggestedChanges, startTime, endTime) {
|
|
495
|
+
super(text, startTime, endTime);
|
|
496
|
+
this.insights = insights;
|
|
497
|
+
this.summary = summary;
|
|
498
|
+
this.suggestedChanges = suggestedChanges;
|
|
499
|
+
}
|
|
500
|
+
insights;
|
|
501
|
+
summary;
|
|
502
|
+
suggestedChanges;
|
|
503
|
+
};
|
|
504
|
+
var ANALYSIS_SYSTEM = `You are an agent team performance analyst. Review a multi-agent execution and extract structured learnings.
|
|
505
|
+
|
|
506
|
+
Analyze across these 4 categories:
|
|
507
|
+
|
|
508
|
+
1. COMMUNICATION: How well did agents share information? Bottlenecks? Gaps?
|
|
509
|
+
2. WORKFLOW: Was execution order optimal? Unnecessary or missing steps?
|
|
510
|
+
3. QUALITY: Were there gaps, inconsistencies, or errors in outputs?
|
|
511
|
+
4. EFFICIENCY: Where could parallelism, caching, or batching improve speed?
|
|
512
|
+
|
|
513
|
+
For EACH category, output exactly:
|
|
514
|
+
|
|
515
|
+
CATEGORY: (communication | workflow | quality | efficiency)
|
|
516
|
+
PATTERN: (one specific, observed pattern \u2014 be concrete)
|
|
517
|
+
RECOMMENDATION: (one actionable, specific improvement)
|
|
518
|
+
CONFIDENCE: 0.XX
|
|
519
|
+
|
|
520
|
+
After all categories, output:
|
|
521
|
+
|
|
522
|
+
SUMMARY: (2-3 sentence executive summary of key findings)
|
|
523
|
+
|
|
524
|
+
CHANGES: (concrete, specific changes to agent roles, prompts, or workflow structure \u2014 cite specific phases or prompts that should change)`;
|
|
525
|
+
function resolveInput(opts, template) {
|
|
526
|
+
if (opts.trace) return opts.trace;
|
|
527
|
+
if (opts.source) {
|
|
528
|
+
const sourceType = opts.source.constructor.name;
|
|
529
|
+
return `Execution trace from ${sourceType} pattern:
|
|
530
|
+
|
|
531
|
+
${opts.source.text}`;
|
|
532
|
+
}
|
|
533
|
+
return `Description of an agent execution:
|
|
534
|
+
|
|
535
|
+
${template}
|
|
536
|
+
|
|
537
|
+
(Analyze this described execution and extract learnings.)`;
|
|
538
|
+
}
|
|
539
|
+
function parseInsights(response) {
|
|
540
|
+
const insights = [];
|
|
541
|
+
const categoryRegex = /CATEGORY\s*:\s*(\w+)[\s\S]*?PATTERN\s*:\s*(.+?)\nRECOMMENDATION\s*:\s*(.+?)\nCONFIDENCE\s*:\s*([\d.]+)/gi;
|
|
542
|
+
let match;
|
|
543
|
+
while ((match = categoryRegex.exec(response)) !== null) {
|
|
544
|
+
const category = match[1].trim().toLowerCase();
|
|
545
|
+
const pattern = match[2].trim();
|
|
546
|
+
const recommendation = match[3].trim();
|
|
547
|
+
const confidence = parseFloat(match[4]) || 0.5;
|
|
548
|
+
if (["communication", "workflow", "quality", "efficiency"].includes(category)) {
|
|
549
|
+
insights.push(
|
|
550
|
+
new LearningInsight(category, pattern, recommendation, Math.min(1, Math.max(0, confidence)))
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const summaryMatch = response.match(/SUMMARY\s*:\s*(.+?)(?:\n\n|$)/is);
|
|
555
|
+
const summary = summaryMatch?.[1]?.trim() ?? "No summary extracted.";
|
|
556
|
+
const changesMatch = response.match(/CHANGES\s*:\s*(.+?)$/is);
|
|
557
|
+
const suggestedChanges = changesMatch?.[1]?.trim() ?? "No specific changes recommended.";
|
|
558
|
+
return { insights, summary, suggestedChanges };
|
|
559
|
+
}
|
|
560
|
+
async function execute3(pieces, args, opts) {
|
|
561
|
+
const template = build(pieces, args);
|
|
562
|
+
const input = resolveInput(opts, template);
|
|
563
|
+
const t0 = Date.now();
|
|
564
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
565
|
+
if (!opts.quiet) {
|
|
566
|
+
const label = opts.source ? opts.source.constructor.name : opts.trace ? "trace" : "analysis";
|
|
567
|
+
process.stderr.write(`\u03A7: Cross-Agent Learning \u2014 analyzing ${label}
|
|
568
|
+
`);
|
|
569
|
+
}
|
|
570
|
+
const response = await ask(input, {
|
|
571
|
+
model: plannerModel,
|
|
572
|
+
maxTokens: opts.maxTokens,
|
|
573
|
+
thinkingLevel: opts.thinkingLevel,
|
|
574
|
+
system: ANALYSIS_SYSTEM
|
|
575
|
+
});
|
|
576
|
+
const { insights, summary, suggestedChanges } = parseInsights(response);
|
|
577
|
+
const t1 = Date.now();
|
|
578
|
+
const text = [
|
|
579
|
+
`Insights: ${insights.length} extracted`,
|
|
580
|
+
insights.map(
|
|
581
|
+
(i) => ` [${i.category}] ${i.pattern.slice(0, 80)}... (confidence: ${i.confidence.toFixed(2)})`
|
|
582
|
+
).join("\n"),
|
|
583
|
+
`
|
|
584
|
+
Summary: ${summary}`,
|
|
585
|
+
`
|
|
586
|
+
Changes: ${suggestedChanges}`
|
|
587
|
+
].join("\n");
|
|
588
|
+
return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1);
|
|
589
|
+
}
|
|
590
|
+
function makeChi(opts = {}) {
|
|
591
|
+
const merged = { ...defaults3, ...opts };
|
|
592
|
+
const fn = ((pieces, ...args) => {
|
|
593
|
+
if (!Array.isArray(pieces)) {
|
|
594
|
+
return makeChi({ ...merged, ...pieces });
|
|
595
|
+
}
|
|
596
|
+
return new PatternPromise((resolve, reject) => {
|
|
597
|
+
execute3(pieces, args, merged).then(resolve, reject);
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
let _quiet;
|
|
601
|
+
Object.defineProperty(fn, "quiet", {
|
|
602
|
+
get() {
|
|
603
|
+
if (!_quiet) _quiet = makeChi({ ...merged, quiet: true });
|
|
604
|
+
return _quiet;
|
|
605
|
+
},
|
|
606
|
+
enumerable: true,
|
|
607
|
+
configurable: true
|
|
608
|
+
});
|
|
609
|
+
return fn;
|
|
610
|
+
}
|
|
611
|
+
var \u03A7 = makeChi();
|
|
612
|
+
|
|
613
|
+
// src/patterns/critique.ts
|
|
614
|
+
var defaults4 = {
|
|
615
|
+
maxTokens: 4096,
|
|
616
|
+
thinkingLevel: "medium",
|
|
617
|
+
rounds: 1
|
|
618
|
+
};
|
|
619
|
+
var CritiqueRound = class {
|
|
620
|
+
constructor(content, critique, round) {
|
|
621
|
+
this.content = content;
|
|
622
|
+
this.critique = critique;
|
|
623
|
+
this.round = round;
|
|
624
|
+
}
|
|
625
|
+
content;
|
|
626
|
+
critique;
|
|
627
|
+
round;
|
|
628
|
+
};
|
|
629
|
+
var CritiqueOutput = class extends PatternOutput {
|
|
630
|
+
constructor(text, finalContent, rounds, startTime, endTime) {
|
|
631
|
+
super(text, startTime, endTime);
|
|
632
|
+
this.finalContent = finalContent;
|
|
633
|
+
this.rounds = rounds;
|
|
634
|
+
}
|
|
635
|
+
finalContent;
|
|
636
|
+
rounds;
|
|
637
|
+
};
|
|
638
|
+
var CRITIQUE_SYSTEM = `You are a thorough, constructive critic. Review the following content and identify:
|
|
639
|
+
1. Strengths (what works well)
|
|
640
|
+
2. Weaknesses (what could be improved)
|
|
641
|
+
3. Specific suggestions for improvement
|
|
642
|
+
|
|
643
|
+
Be specific, actionable, and constructive. Focus on substance, not style.`;
|
|
644
|
+
var IMPROVE_SYSTEM = `You are a skilled editor. Revise the original content based on the critique provided.
|
|
645
|
+
Incorporate the feedback while maintaining the original intent and voice.
|
|
646
|
+
Output ONLY the improved content \u2014 no commentary or explanation.`;
|
|
647
|
+
async function execute4(pieces, args, opts) {
|
|
648
|
+
const prompt = build(pieces, args);
|
|
649
|
+
const t0 = Date.now();
|
|
650
|
+
const rounds = Math.min(opts.rounds ?? 1, 3);
|
|
651
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
652
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
653
|
+
if (!opts.quiet) {
|
|
654
|
+
process.stderr.write(
|
|
655
|
+
`\u03A8: Critique \u2014 "${prompt.slice(0, 80)}${prompt.length > 80 ? "..." : ""}"
|
|
656
|
+
`
|
|
657
|
+
);
|
|
658
|
+
process.stderr.write(` \u2192 ${rounds} critique round(s)
|
|
659
|
+
`);
|
|
660
|
+
}
|
|
661
|
+
const critiqueRounds = [];
|
|
662
|
+
let currentContent = "";
|
|
663
|
+
for (let r = 0; r < rounds; r++) {
|
|
664
|
+
if (r === 0) {
|
|
665
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Generating initial content...\n");
|
|
666
|
+
currentContent = await ask(prompt, {
|
|
667
|
+
model: workerModel,
|
|
668
|
+
maxTokens: opts.maxTokens,
|
|
669
|
+
thinkingLevel: opts.thinkingLevel,
|
|
670
|
+
system: void 0
|
|
671
|
+
});
|
|
672
|
+
} else {
|
|
673
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Improving (round ${r + 1})...
|
|
674
|
+
`);
|
|
675
|
+
const prevCritique = critiqueRounds[r - 1]?.critique ?? "";
|
|
676
|
+
currentContent = await ask(
|
|
677
|
+
`Original request: ${prompt}
|
|
678
|
+
|
|
679
|
+
Critique:
|
|
680
|
+
${prevCritique}
|
|
681
|
+
|
|
682
|
+
Content to improve:
|
|
683
|
+
${currentContent}
|
|
684
|
+
|
|
685
|
+
Revise the content based on the critique.`,
|
|
686
|
+
{
|
|
687
|
+
model: workerModel,
|
|
688
|
+
maxTokens: opts.maxTokens,
|
|
689
|
+
thinkingLevel: opts.thinkingLevel,
|
|
690
|
+
system: IMPROVE_SYSTEM
|
|
691
|
+
}
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Critiquing (round ${r + 1})...
|
|
695
|
+
`);
|
|
696
|
+
const critique = await ask(currentContent, {
|
|
697
|
+
model: plannerModel,
|
|
698
|
+
maxTokens: opts.maxTokens,
|
|
699
|
+
thinkingLevel: opts.thinkingLevel,
|
|
700
|
+
system: CRITIQUE_SYSTEM
|
|
701
|
+
});
|
|
702
|
+
critiqueRounds.push(new CritiqueRound(currentContent, critique, r));
|
|
703
|
+
}
|
|
704
|
+
const t1 = Date.now();
|
|
705
|
+
const finalContent = currentContent;
|
|
706
|
+
const summary = critiqueRounds.map(
|
|
707
|
+
(cr) => `Round ${cr.round + 1}:
|
|
708
|
+
${cr.content.slice(0, 200)}${cr.content.length > 200 ? "..." : ""}
|
|
709
|
+
Critique: ${cr.critique.slice(0, 200)}${cr.critique.length > 200 ? "..." : ""}`
|
|
710
|
+
).join("\n\n");
|
|
711
|
+
return new CritiqueOutput(summary, finalContent, critiqueRounds, t0, t1);
|
|
712
|
+
}
|
|
713
|
+
function makeCritique(opts = {}) {
|
|
714
|
+
const merged = { ...defaults4, ...opts };
|
|
715
|
+
const fn = ((pieces, ...args) => {
|
|
716
|
+
if (!Array.isArray(pieces)) {
|
|
717
|
+
return makeCritique({ ...merged, ...pieces });
|
|
718
|
+
}
|
|
719
|
+
return new PatternPromise((resolve, reject) => {
|
|
720
|
+
execute4(pieces, args, merged).then(resolve, reject);
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
let _quiet;
|
|
724
|
+
Object.defineProperty(fn, "quiet", {
|
|
725
|
+
get() {
|
|
726
|
+
if (!_quiet) _quiet = makeCritique({ ...merged, quiet: true });
|
|
727
|
+
return _quiet;
|
|
728
|
+
},
|
|
729
|
+
enumerable: true,
|
|
730
|
+
configurable: true
|
|
731
|
+
});
|
|
732
|
+
return fn;
|
|
733
|
+
}
|
|
734
|
+
var \u03A8 = makeCritique();
|
|
735
|
+
|
|
736
|
+
// src/patterns/debate.ts
|
|
737
|
+
var defaults5 = {
|
|
738
|
+
maxTokens: 4096,
|
|
739
|
+
thinkingLevel: "medium",
|
|
740
|
+
perspectives: 3,
|
|
741
|
+
rounds: 1
|
|
742
|
+
};
|
|
743
|
+
var ROLE_SETS2 = {
|
|
744
|
+
2: [
|
|
745
|
+
"Optimist \u2014 advocate for the most ambitious approach",
|
|
746
|
+
"Pessimist \u2014 identify risks and failure modes"
|
|
747
|
+
],
|
|
748
|
+
3: [
|
|
749
|
+
"Optimist \u2014 advocate the benefits and opportunities",
|
|
750
|
+
"Pessimist \u2014 identify risks, costs, and failure modes",
|
|
751
|
+
"Pragmatist \u2014 focus on practical trade-offs and implementation"
|
|
752
|
+
],
|
|
753
|
+
4: [
|
|
754
|
+
"Optimist \u2014 argue for the best-case potential",
|
|
755
|
+
"Pessimist \u2014 highlight worst-case risks and downsides",
|
|
756
|
+
"Pragmatist \u2014 balance pros/cons with practical constraints",
|
|
757
|
+
"Innovator \u2014 propose creative alternatives and novel approaches"
|
|
758
|
+
],
|
|
759
|
+
5: [
|
|
760
|
+
"Optimist",
|
|
761
|
+
"Pessimist",
|
|
762
|
+
"Pragmatist",
|
|
763
|
+
"Innovator",
|
|
764
|
+
"User Advocate \u2014 focus on end-user experience and accessibility"
|
|
765
|
+
]
|
|
766
|
+
};
|
|
767
|
+
var DebatePerspective = class {
|
|
768
|
+
constructor(role, argument, round = 1) {
|
|
769
|
+
this.role = role;
|
|
770
|
+
this.argument = argument;
|
|
771
|
+
this.round = round;
|
|
772
|
+
}
|
|
773
|
+
role;
|
|
774
|
+
argument;
|
|
775
|
+
round;
|
|
776
|
+
};
|
|
777
|
+
var DebateOutput = class extends PatternOutput {
|
|
778
|
+
constructor(text, conclusion, perspectives, rounds, startTime, endTime) {
|
|
779
|
+
super(text, startTime, endTime);
|
|
780
|
+
this.conclusion = conclusion;
|
|
781
|
+
this.perspectives = perspectives;
|
|
782
|
+
this.rounds = rounds;
|
|
783
|
+
}
|
|
784
|
+
conclusion;
|
|
785
|
+
perspectives;
|
|
786
|
+
rounds;
|
|
787
|
+
};
|
|
788
|
+
var PERSPECTIVE_SYSTEM = (role) => `You are a debater with the role: ${role}. Analyze the question from your perspective. Be thorough and specific. Provide evidence and reasoning for your position.`;
|
|
789
|
+
var REBUTTAL_SYSTEM = (role) => `You are a debater with the role: ${role}. Review the debate so far \u2014 including arguments from all other perspectives \u2014 and refine your position. Address counter-arguments directly. Challenge weak points in opposing views. Strengthen your original position with rebuttals. Be specific and responsive.`;
|
|
790
|
+
var SYNTHESIS_SYSTEM2 = `You are a neutral moderator. Synthesize the different perspectives into a balanced, reasoned conclusion. Weigh the evidence from each perspective and provide a final recommendation. Be specific and actionable.`;
|
|
791
|
+
async function execute5(pieces, args, opts) {
|
|
792
|
+
const question = build(pieces, args);
|
|
793
|
+
const t0 = Date.now();
|
|
794
|
+
const count = opts.perspectives ?? 3;
|
|
795
|
+
const totalRounds = opts.rounds ?? 1;
|
|
796
|
+
const roles = opts.roles ?? ROLE_SETS2[count] ?? ROLE_SETS2[3] ?? [];
|
|
797
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
798
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
799
|
+
if (!opts.quiet) {
|
|
800
|
+
process.stderr.write(
|
|
801
|
+
`\u0394: Debate \u2014 "${question.slice(0, 80)}${question.length > 80 ? "..." : ""}"
|
|
802
|
+
`
|
|
803
|
+
);
|
|
804
|
+
process.stderr.write(` \u2192 ${roles.length} perspective(s), ${totalRounds} round(s)
|
|
805
|
+
`);
|
|
806
|
+
}
|
|
807
|
+
const allPerspectives = [];
|
|
808
|
+
let debateHistory = `Question: ${question}
|
|
809
|
+
`;
|
|
810
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Round 1: Initial perspectives...\n");
|
|
811
|
+
const round1Results = await Promise.allSettled(
|
|
812
|
+
roles.map(
|
|
813
|
+
(role) => ask(question, {
|
|
814
|
+
model: workerModel,
|
|
815
|
+
maxTokens: opts.maxTokens,
|
|
816
|
+
thinkingLevel: opts.thinkingLevel,
|
|
817
|
+
system: PERSPECTIVE_SYSTEM(role)
|
|
818
|
+
}).then((text) => new DebatePerspective(role, text, 1))
|
|
819
|
+
)
|
|
820
|
+
);
|
|
821
|
+
const round1Perspectives = [];
|
|
822
|
+
round1Results.forEach((r, i) => {
|
|
823
|
+
if (r.status === "fulfilled") {
|
|
824
|
+
round1Perspectives.push(r.value);
|
|
825
|
+
} else {
|
|
826
|
+
round1Perspectives.push(
|
|
827
|
+
new DebatePerspective(roles[i] ?? `Perspective ${i + 1}`, `(failed: ${r.reason})`, 1)
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
allPerspectives.push(...round1Perspectives);
|
|
832
|
+
debateHistory += `${round1Perspectives.map((p) => `[Round 1] ${p.role}: ${p.argument}`).join("\n\n")}
|
|
833
|
+
`;
|
|
834
|
+
for (let round = 2; round <= totalRounds; round++) {
|
|
835
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Round ${round}: Rebuttals...
|
|
836
|
+
`);
|
|
837
|
+
const roundResults = await Promise.allSettled(
|
|
838
|
+
roles.map((role) => {
|
|
839
|
+
const othersText = allPerspectives.filter((p) => p.role !== role).map((p) => `[${p.role}, Round ${p.round}]: ${p.argument}`).join("\n\n");
|
|
840
|
+
const ownText = allPerspectives.filter((p) => p.role === role).map((p) => `[Your Round ${p.round}]: ${p.argument}`).join("\n\n");
|
|
841
|
+
const prompt = `Question: ${question}
|
|
842
|
+
|
|
843
|
+
Your previous position:
|
|
844
|
+
${ownText}
|
|
845
|
+
|
|
846
|
+
Counter-arguments from other perspectives:
|
|
847
|
+
${othersText}
|
|
848
|
+
|
|
849
|
+
Refine your position. Address the counter-arguments directly. Strengthen your argument with rebuttals.`;
|
|
850
|
+
return ask(prompt, {
|
|
851
|
+
model: workerModel,
|
|
852
|
+
maxTokens: opts.maxTokens,
|
|
853
|
+
thinkingLevel: opts.thinkingLevel,
|
|
854
|
+
system: REBUTTAL_SYSTEM(role)
|
|
855
|
+
}).then((text) => new DebatePerspective(role, text, round));
|
|
856
|
+
})
|
|
857
|
+
);
|
|
858
|
+
const roundPerspectives = [];
|
|
859
|
+
roundResults.forEach((r, i) => {
|
|
860
|
+
if (r.status === "fulfilled") {
|
|
861
|
+
roundPerspectives.push(r.value);
|
|
862
|
+
} else {
|
|
863
|
+
roundPerspectives.push(
|
|
864
|
+
new DebatePerspective(roles[i] ?? `Perspective ${i + 1}`, `(failed: ${r.reason})`, round)
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
allPerspectives.push(...roundPerspectives);
|
|
869
|
+
debateHistory += `${roundPerspectives.map((p) => `[Round ${round}] ${p.role}: ${p.argument}`).join("\n\n")}
|
|
870
|
+
`;
|
|
871
|
+
}
|
|
872
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing perspectives...\n");
|
|
873
|
+
const conclusion = await ask(
|
|
874
|
+
`${debateHistory}
|
|
875
|
+
|
|
876
|
+
Synthesize a balanced conclusion from the full debate above. Weigh the evidence from all rounds.`,
|
|
877
|
+
{
|
|
878
|
+
model: plannerModel,
|
|
879
|
+
maxTokens: opts.maxTokens,
|
|
880
|
+
thinkingLevel: "high",
|
|
881
|
+
system: SYNTHESIS_SYSTEM2
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
const t1 = Date.now();
|
|
885
|
+
return new DebateOutput(conclusion, conclusion, allPerspectives, totalRounds, t0, t1);
|
|
886
|
+
}
|
|
887
|
+
function makeDebate(opts = {}) {
|
|
888
|
+
const merged = { ...defaults5, ...opts };
|
|
889
|
+
const fn = ((pieces, ...args) => {
|
|
890
|
+
if (!Array.isArray(pieces)) {
|
|
891
|
+
return makeDebate({ ...merged, ...pieces });
|
|
892
|
+
}
|
|
893
|
+
return new PatternPromise((resolve, reject) => {
|
|
894
|
+
execute5(pieces, args, merged).then(resolve, reject);
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
let _quiet;
|
|
898
|
+
Object.defineProperty(fn, "quiet", {
|
|
899
|
+
get() {
|
|
900
|
+
if (!_quiet) _quiet = makeDebate({ ...merged, quiet: true });
|
|
901
|
+
return _quiet;
|
|
902
|
+
},
|
|
903
|
+
enumerable: true,
|
|
904
|
+
configurable: true
|
|
905
|
+
});
|
|
906
|
+
return fn;
|
|
907
|
+
}
|
|
908
|
+
var \u0394 = makeDebate();
|
|
909
|
+
|
|
910
|
+
// src/patterns/fleet.ts
|
|
911
|
+
var defaults6 = {
|
|
912
|
+
maxTokens: 4096,
|
|
913
|
+
thinkingLevel: "medium",
|
|
914
|
+
concurrency: 5
|
|
915
|
+
};
|
|
916
|
+
var FleetMemberOutput = class {
|
|
917
|
+
constructor(task, text, success, error) {
|
|
918
|
+
this.task = task;
|
|
919
|
+
this.text = text;
|
|
920
|
+
this.success = success;
|
|
921
|
+
this.error = error;
|
|
922
|
+
}
|
|
923
|
+
task;
|
|
924
|
+
text;
|
|
925
|
+
success;
|
|
926
|
+
error;
|
|
927
|
+
};
|
|
928
|
+
var FleetOutput = class extends PatternOutput {
|
|
929
|
+
constructor(text, members, startTime, endTime) {
|
|
930
|
+
super(text, startTime, endTime);
|
|
931
|
+
this.members = members;
|
|
932
|
+
}
|
|
933
|
+
members;
|
|
934
|
+
/** Number of successful members */
|
|
935
|
+
get successCount() {
|
|
936
|
+
return this.members.filter((m) => m.success).length;
|
|
937
|
+
}
|
|
938
|
+
/** Number of failed members */
|
|
939
|
+
get failureCount() {
|
|
940
|
+
return this.members.filter((m) => !m.success).length;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
function parseTasks(template, explicitTasks) {
|
|
944
|
+
if (explicitTasks && explicitTasks.length > 0) return explicitTasks;
|
|
945
|
+
const lines = template.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
946
|
+
const bullets = lines.filter((l) => /^[-*]\s/.test(l));
|
|
947
|
+
if (bullets.length > 1) return bullets.map((b) => b.replace(/^[-*]\s+/, ""));
|
|
948
|
+
const numbered = lines.filter((l) => /^\d+[.)]\s/.test(l));
|
|
949
|
+
if (numbered.length > 1) return numbered.map((n) => n.replace(/^\d+[.)]\s+/, ""));
|
|
950
|
+
if (lines.length > 1) return lines;
|
|
951
|
+
return [template];
|
|
952
|
+
}
|
|
953
|
+
var FLEET_SYSTEM = `You are a focused task specialist. Complete the assigned task concisely and accurately. Output only the result \u2014 no commentary about being an AI.`;
|
|
954
|
+
async function executeTask(task, opts, workerModel) {
|
|
955
|
+
const model = workerModel ?? opts.model;
|
|
956
|
+
try {
|
|
957
|
+
const text = await ask(task, {
|
|
958
|
+
model,
|
|
959
|
+
maxTokens: opts.maxTokens,
|
|
960
|
+
thinkingLevel: opts.thinkingLevel,
|
|
961
|
+
system: opts.system ?? FLEET_SYSTEM
|
|
962
|
+
});
|
|
963
|
+
return new FleetMemberOutput(task, text, true);
|
|
964
|
+
} catch (err) {
|
|
965
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
966
|
+
return new FleetMemberOutput(task, "", false, msg);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
async function execute6(pieces, args, opts) {
|
|
970
|
+
const template = build(pieces, args);
|
|
971
|
+
const tasks = parseTasks(template, opts.tasks);
|
|
972
|
+
const t0 = Date.now();
|
|
973
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
974
|
+
if (!opts.quiet) {
|
|
975
|
+
process.stderr.write(`\u03A6: Fleet executing ${tasks.length} task(s) in parallel
|
|
976
|
+
`);
|
|
977
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
978
|
+
const t = tasks[i];
|
|
979
|
+
process.stderr.write(` [${i + 1}] ${t.slice(0, 60)}${t.length > 60 ? "..." : ""}
|
|
980
|
+
`);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
const results = [];
|
|
984
|
+
const concurrency = opts.concurrency ?? 5;
|
|
985
|
+
for (let i = 0; i < tasks.length; i += concurrency) {
|
|
986
|
+
const batch = tasks.slice(i, i + concurrency);
|
|
987
|
+
const batchResults = await Promise.allSettled(
|
|
988
|
+
batch.map((task) => executeTask(task, opts, workerModel))
|
|
989
|
+
);
|
|
990
|
+
batchResults.forEach((r, idx) => {
|
|
991
|
+
if (r.status === "fulfilled") {
|
|
992
|
+
results.push(r.value);
|
|
993
|
+
} else {
|
|
994
|
+
results.push(new FleetMemberOutput(batch[idx], "", false, r.reason?.toString()));
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
const t1 = Date.now();
|
|
999
|
+
const summary = results.map(
|
|
1000
|
+
(m, i) => `[${i + 1}] ${m.task}
|
|
1001
|
+
${m.success ? "\u2713" : "\u2717"} ${m.text.slice(0, 200)}${m.text.length > 200 ? "..." : ""}`
|
|
1002
|
+
).join("\n\n");
|
|
1003
|
+
const header = `Fleet Results: ${results.filter((r) => r.success).length}/${results.length} succeeded
|
|
1004
|
+
|
|
1005
|
+
`;
|
|
1006
|
+
return new FleetOutput(header + summary, results, t0, t1);
|
|
1007
|
+
}
|
|
1008
|
+
function makeFleet(opts = {}) {
|
|
1009
|
+
const merged = { ...defaults6, ...opts };
|
|
1010
|
+
const fn = ((pieces, ...args) => {
|
|
1011
|
+
if (!Array.isArray(pieces)) {
|
|
1012
|
+
return makeFleet({ ...merged, ...pieces });
|
|
1013
|
+
}
|
|
1014
|
+
return new PatternPromise((resolve, reject) => {
|
|
1015
|
+
execute6(pieces, args, merged).then(resolve, reject);
|
|
1016
|
+
});
|
|
1017
|
+
});
|
|
1018
|
+
let _quiet;
|
|
1019
|
+
Object.defineProperty(fn, "quiet", {
|
|
1020
|
+
get() {
|
|
1021
|
+
if (!_quiet) _quiet = makeFleet({ ...merged, quiet: true });
|
|
1022
|
+
return _quiet;
|
|
1023
|
+
},
|
|
1024
|
+
enumerable: true,
|
|
1025
|
+
configurable: true
|
|
1026
|
+
});
|
|
1027
|
+
return fn;
|
|
1028
|
+
}
|
|
1029
|
+
var \u03A6 = makeFleet();
|
|
1030
|
+
|
|
1031
|
+
// src/patterns/graph.ts
|
|
1032
|
+
var defaults7 = {
|
|
1033
|
+
maxTokens: 4096,
|
|
1034
|
+
thinkingLevel: "medium"
|
|
1035
|
+
};
|
|
1036
|
+
var GraphNodeResult = class {
|
|
1037
|
+
constructor(nodeId, task, output, success) {
|
|
1038
|
+
this.nodeId = nodeId;
|
|
1039
|
+
this.task = task;
|
|
1040
|
+
this.output = output;
|
|
1041
|
+
this.success = success;
|
|
1042
|
+
}
|
|
1043
|
+
nodeId;
|
|
1044
|
+
task;
|
|
1045
|
+
output;
|
|
1046
|
+
success;
|
|
1047
|
+
};
|
|
1048
|
+
var GraphOutput = class extends PatternOutput {
|
|
1049
|
+
constructor(text, finalOutput, nodeResults, startTime, endTime) {
|
|
1050
|
+
super(text, startTime, endTime);
|
|
1051
|
+
this.finalOutput = finalOutput;
|
|
1052
|
+
this.nodeResults = nodeResults;
|
|
1053
|
+
}
|
|
1054
|
+
finalOutput;
|
|
1055
|
+
nodeResults;
|
|
1056
|
+
};
|
|
1057
|
+
function parseGraph(template, separator) {
|
|
1058
|
+
const sep = separator ?? "\u2192";
|
|
1059
|
+
const parts = template.split(sep).map((s) => s.trim()).filter(Boolean);
|
|
1060
|
+
if (parts.length <= 1) {
|
|
1061
|
+
return {
|
|
1062
|
+
nodes: [{ id: "root", task: template.trim() }],
|
|
1063
|
+
edges: []
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
const nodes = [];
|
|
1067
|
+
const edges = [];
|
|
1068
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1069
|
+
const id = `step_${i + 1}`;
|
|
1070
|
+
nodes.push({ id, task: parts[i] });
|
|
1071
|
+
if (i > 0) {
|
|
1072
|
+
edges.push({ from: nodes[i - 1].id, to: id });
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
return { nodes, edges };
|
|
1076
|
+
}
|
|
1077
|
+
var NODE_SYSTEM = `You are a task specialist. Execute the assigned task and output your result. Be thorough but concise. Output only the result \u2014 no meta-commentary.`;
|
|
1078
|
+
function topoBatches(nodes, edges) {
|
|
1079
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
1080
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
1081
|
+
const adj = /* @__PURE__ */ new Map();
|
|
1082
|
+
for (const id of nodeIds) {
|
|
1083
|
+
inDegree.set(id, 0);
|
|
1084
|
+
adj.set(id, []);
|
|
1085
|
+
}
|
|
1086
|
+
for (const edge of edges) {
|
|
1087
|
+
if (!nodeIds.has(edge.from) || !nodeIds.has(edge.to)) continue;
|
|
1088
|
+
inDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);
|
|
1089
|
+
adj.get(edge.from)?.push(edge.to);
|
|
1090
|
+
}
|
|
1091
|
+
const batches = [];
|
|
1092
|
+
const ready = [];
|
|
1093
|
+
for (const [id, deg] of inDegree) {
|
|
1094
|
+
if (deg === 0) ready.push(id);
|
|
1095
|
+
}
|
|
1096
|
+
while (ready.length > 0) {
|
|
1097
|
+
batches.push([...ready]);
|
|
1098
|
+
const nextBatch = [];
|
|
1099
|
+
for (const node of ready) {
|
|
1100
|
+
for (const neighbor of adj.get(node) ?? []) {
|
|
1101
|
+
const newDeg = (inDegree.get(neighbor) ?? 1) - 1;
|
|
1102
|
+
inDegree.set(neighbor, newDeg);
|
|
1103
|
+
if (newDeg === 0) nextBatch.push(neighbor);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
ready.length = 0;
|
|
1107
|
+
ready.push(...nextBatch);
|
|
1108
|
+
}
|
|
1109
|
+
return batches;
|
|
1110
|
+
}
|
|
1111
|
+
async function execute7(pieces, args, opts) {
|
|
1112
|
+
const template = build(pieces, args);
|
|
1113
|
+
const t0 = Date.now();
|
|
1114
|
+
const { nodes, edges } = opts.graph ?? parseGraph(template, opts.separator);
|
|
1115
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
1116
|
+
if (!opts.quiet) {
|
|
1117
|
+
process.stderr.write(`\u0393: DAG Graph \u2014 ${nodes.length} node(s), ${edges.length} edge(s)
|
|
1118
|
+
`);
|
|
1119
|
+
for (const n of nodes) {
|
|
1120
|
+
process.stderr.write(` [${n.id}] ${n.task.slice(0, 60)}${n.task.length > 60 ? "..." : ""}
|
|
1121
|
+
`);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const batches = topoBatches(nodes, edges);
|
|
1125
|
+
const results = /* @__PURE__ */ new Map();
|
|
1126
|
+
const nodeResults = [];
|
|
1127
|
+
for (let bi = 0; bi < batches.length; bi++) {
|
|
1128
|
+
const batch = batches[bi];
|
|
1129
|
+
if (!opts.quiet)
|
|
1130
|
+
process.stderr.write(` \u2192 Batch ${bi + 1}/${batches.length}: ${batch.join(", ")}
|
|
1131
|
+
`);
|
|
1132
|
+
const batchResults = await Promise.allSettled(
|
|
1133
|
+
batch.map(async (nodeId) => {
|
|
1134
|
+
const node = nodes.find((n) => n.id === nodeId);
|
|
1135
|
+
if (!node) return { nodeId, task: "", text: "", success: false };
|
|
1136
|
+
const deps = edges.filter((e) => e.to === nodeId);
|
|
1137
|
+
let context = node.task;
|
|
1138
|
+
if (deps.length > 0) {
|
|
1139
|
+
const depResults = deps.map((d) => {
|
|
1140
|
+
const depResult = results.get(d.from);
|
|
1141
|
+
return depResult ? `[${d.from} output]: ${depResult}` : "";
|
|
1142
|
+
}).filter(Boolean).join("\n\n");
|
|
1143
|
+
if (depResults) {
|
|
1144
|
+
context = `Previous results:
|
|
1145
|
+
${depResults}
|
|
1146
|
+
|
|
1147
|
+
Your task: ${node.task}`;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
const text = await ask(context, {
|
|
1151
|
+
model: workerModel,
|
|
1152
|
+
maxTokens: opts.maxTokens,
|
|
1153
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1154
|
+
system: NODE_SYSTEM
|
|
1155
|
+
});
|
|
1156
|
+
return { nodeId, task: node.task, text, success: true };
|
|
1157
|
+
})
|
|
1158
|
+
);
|
|
1159
|
+
for (const r of batchResults) {
|
|
1160
|
+
if (r.status === "fulfilled") {
|
|
1161
|
+
results.set(r.value.nodeId, r.value.text);
|
|
1162
|
+
nodeResults.push(
|
|
1163
|
+
new GraphNodeResult(r.value.nodeId, r.value.task, r.value.text, r.value.success)
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
const t1 = Date.now();
|
|
1169
|
+
const lastBatch = batches[batches.length - 1] ?? [];
|
|
1170
|
+
const finalNodeResults = lastBatch.map((id) => results.get(id)).filter(Boolean);
|
|
1171
|
+
const finalOutput = finalNodeResults.length > 0 ? finalNodeResults.join("\n\n") : "";
|
|
1172
|
+
const summary = nodeResults.map(
|
|
1173
|
+
(nr) => `[${nr.nodeId}] ${nr.task.slice(0, 80)}...
|
|
1174
|
+
${nr.output.slice(0, 200)}${nr.output.length > 200 ? "..." : ""}`
|
|
1175
|
+
).join("\n\n");
|
|
1176
|
+
return new GraphOutput(summary, finalOutput, nodeResults, t0, t1);
|
|
1177
|
+
}
|
|
1178
|
+
function makeGraph(opts = {}) {
|
|
1179
|
+
const merged = { ...defaults7, ...opts };
|
|
1180
|
+
const fn = ((pieces, ...args) => {
|
|
1181
|
+
if (!Array.isArray(pieces)) {
|
|
1182
|
+
return makeGraph({ ...merged, ...pieces });
|
|
1183
|
+
}
|
|
1184
|
+
return new PatternPromise((resolve, reject) => {
|
|
1185
|
+
execute7(pieces, args, merged).then(resolve, reject);
|
|
1186
|
+
});
|
|
1187
|
+
});
|
|
1188
|
+
let _quiet;
|
|
1189
|
+
Object.defineProperty(fn, "quiet", {
|
|
1190
|
+
get() {
|
|
1191
|
+
if (!_quiet) _quiet = makeGraph({ ...merged, quiet: true });
|
|
1192
|
+
return _quiet;
|
|
1193
|
+
},
|
|
1194
|
+
enumerable: true,
|
|
1195
|
+
configurable: true
|
|
1196
|
+
});
|
|
1197
|
+
return fn;
|
|
1198
|
+
}
|
|
1199
|
+
var \u0393 = makeGraph();
|
|
1200
|
+
|
|
1201
|
+
// src/patterns/memory.ts
|
|
1202
|
+
var defaults8 = {
|
|
1203
|
+
maxTokens: 4096,
|
|
1204
|
+
thinkingLevel: "medium",
|
|
1205
|
+
agents: 3,
|
|
1206
|
+
rounds: 1
|
|
1207
|
+
};
|
|
1208
|
+
var ROLE_SETS3 = {
|
|
1209
|
+
2: ["Analyst \u2014 deep analysis of core aspects", "Reviewer \u2014 check for gaps and blind spots"],
|
|
1210
|
+
3: [
|
|
1211
|
+
"Analyst \u2014 deep analysis of core aspects",
|
|
1212
|
+
"Reviewer \u2014 check for gaps, edge cases, and blind spots",
|
|
1213
|
+
"Strategist \u2014 connect findings to actionable insights"
|
|
1214
|
+
],
|
|
1215
|
+
4: [
|
|
1216
|
+
"Analyst",
|
|
1217
|
+
"Reviewer",
|
|
1218
|
+
"Strategist",
|
|
1219
|
+
"Innovator \u2014 propose novel angles and creative solutions"
|
|
1220
|
+
],
|
|
1221
|
+
5: [
|
|
1222
|
+
"Analyst",
|
|
1223
|
+
"Reviewer",
|
|
1224
|
+
"Strategist",
|
|
1225
|
+
"Innovator",
|
|
1226
|
+
"Skeptic \u2014 challenge assumptions and stress-test conclusions"
|
|
1227
|
+
]
|
|
1228
|
+
};
|
|
1229
|
+
var MemoryEntry = class {
|
|
1230
|
+
constructor(role, round, content) {
|
|
1231
|
+
this.role = role;
|
|
1232
|
+
this.round = round;
|
|
1233
|
+
this.content = content;
|
|
1234
|
+
}
|
|
1235
|
+
role;
|
|
1236
|
+
round;
|
|
1237
|
+
content;
|
|
1238
|
+
};
|
|
1239
|
+
var MemoryOutput = class extends PatternOutput {
|
|
1240
|
+
constructor(text, synthesis, entries, startTime, endTime) {
|
|
1241
|
+
super(text, startTime, endTime);
|
|
1242
|
+
this.synthesis = synthesis;
|
|
1243
|
+
this.entries = entries;
|
|
1244
|
+
}
|
|
1245
|
+
synthesis;
|
|
1246
|
+
entries;
|
|
1247
|
+
};
|
|
1248
|
+
var WRITER_PROMPT = `You are a specialist with role: {role}.
|
|
1249
|
+
|
|
1250
|
+
Topic: {topic}
|
|
1251
|
+
|
|
1252
|
+
Current findings on the shared blackboard (written by other agents):
|
|
1253
|
+
{context}
|
|
1254
|
+
|
|
1255
|
+
Add your contribution to the blackboard. Be specific, add unique insights.
|
|
1256
|
+
Don't repeat what others have already covered \u2014 fill gaps, add depth, or challenge.
|
|
1257
|
+
Keep your contribution under 200 words.`;
|
|
1258
|
+
var CONSOLIDATOR_SYSTEM = `You are a research director. Consolidate findings from multiple specialists into a comprehensive, well-structured synthesis. Combine overlapping insights, resolve contradictions, prioritize the most impactful findings.`;
|
|
1259
|
+
function buildWriterPrompt(role, topic, context) {
|
|
1260
|
+
return WRITER_PROMPT.replace("{role}", role).replace("{topic}", topic).replace("{context}", context || "(No prior entries yet. You are the first contributor.)");
|
|
1261
|
+
}
|
|
1262
|
+
async function execute8(pieces, args, opts) {
|
|
1263
|
+
const topic = build(pieces, args);
|
|
1264
|
+
const t0 = Date.now();
|
|
1265
|
+
const agentCount = opts.agents ?? 3;
|
|
1266
|
+
const totalRounds = opts.rounds ?? 1;
|
|
1267
|
+
const roles = opts.roles ?? ROLE_SETS3[agentCount] ?? ROLE_SETS3[3] ?? [];
|
|
1268
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
1269
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
1270
|
+
if (!opts.quiet) {
|
|
1271
|
+
process.stderr.write(`\u039C: Shared Memory \u2014 ${agentCount} agent(s), ${totalRounds} round(s)
|
|
1272
|
+
`);
|
|
1273
|
+
process.stderr.write(` Topic: "${topic.slice(0, 80)}${topic.length > 80 ? "..." : ""}"
|
|
1274
|
+
`);
|
|
1275
|
+
}
|
|
1276
|
+
const entries = [];
|
|
1277
|
+
let blackboard = "";
|
|
1278
|
+
for (let round = 1; round <= totalRounds; round++) {
|
|
1279
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Round ${round}/${totalRounds}
|
|
1280
|
+
`);
|
|
1281
|
+
const roundResults = await Promise.allSettled(
|
|
1282
|
+
roles.map(async (role) => {
|
|
1283
|
+
const prompt = buildWriterPrompt(role, topic, blackboard);
|
|
1284
|
+
const text = await ask(prompt, {
|
|
1285
|
+
model: workerModel,
|
|
1286
|
+
maxTokens: opts.maxTokens,
|
|
1287
|
+
thinkingLevel: opts.thinkingLevel
|
|
1288
|
+
});
|
|
1289
|
+
return { role, text };
|
|
1290
|
+
})
|
|
1291
|
+
);
|
|
1292
|
+
for (const r of roundResults) {
|
|
1293
|
+
if (r.status === "fulfilled") {
|
|
1294
|
+
entries.push(new MemoryEntry(r.value.role, round, r.value.text));
|
|
1295
|
+
blackboard += `
|
|
1296
|
+
[${r.value.role}] Round ${round}: ${r.value.text}
|
|
1297
|
+
`;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Consolidating findings...\n");
|
|
1302
|
+
const synthesis = await ask(
|
|
1303
|
+
`Topic: ${topic}
|
|
1304
|
+
|
|
1305
|
+
Blackboard findings:
|
|
1306
|
+
${blackboard}
|
|
1307
|
+
|
|
1308
|
+
Consolidate into a comprehensive, structured synthesis.`,
|
|
1309
|
+
{
|
|
1310
|
+
model: plannerModel,
|
|
1311
|
+
maxTokens: opts.maxTokens,
|
|
1312
|
+
thinkingLevel: "high",
|
|
1313
|
+
system: CONSOLIDATOR_SYSTEM
|
|
1314
|
+
}
|
|
1315
|
+
);
|
|
1316
|
+
const t1 = Date.now();
|
|
1317
|
+
const summary = entries.map(
|
|
1318
|
+
(e) => `[${e.role}] Round ${e.round}: ${e.content.slice(0, 150)}${e.content.length > 150 ? "..." : ""}`
|
|
1319
|
+
).join("\n");
|
|
1320
|
+
return new MemoryOutput(summary, synthesis, entries, t0, t1);
|
|
1321
|
+
}
|
|
1322
|
+
function makeMemory(opts = {}) {
|
|
1323
|
+
const merged = { ...defaults8, ...opts };
|
|
1324
|
+
const fn = ((pieces, ...args) => {
|
|
1325
|
+
if (!Array.isArray(pieces)) {
|
|
1326
|
+
return makeMemory({ ...merged, ...pieces });
|
|
1327
|
+
}
|
|
1328
|
+
return new PatternPromise((resolve, reject) => {
|
|
1329
|
+
execute8(pieces, args, merged).then(resolve, reject);
|
|
1330
|
+
});
|
|
1331
|
+
});
|
|
1332
|
+
let _quiet;
|
|
1333
|
+
Object.defineProperty(fn, "quiet", {
|
|
1334
|
+
get() {
|
|
1335
|
+
if (!_quiet) _quiet = makeMemory({ ...merged, quiet: true });
|
|
1336
|
+
return _quiet;
|
|
1337
|
+
},
|
|
1338
|
+
enumerable: true,
|
|
1339
|
+
configurable: true
|
|
1340
|
+
});
|
|
1341
|
+
return fn;
|
|
1342
|
+
}
|
|
1343
|
+
var \u039C = makeMemory();
|
|
1344
|
+
|
|
1345
|
+
// src/patterns/nu.ts
|
|
1346
|
+
var defaults9 = {
|
|
1347
|
+
maxTokens: 4096,
|
|
1348
|
+
thinkingLevel: "medium",
|
|
1349
|
+
minAgents: 2,
|
|
1350
|
+
maxAgents: 5
|
|
1351
|
+
};
|
|
1352
|
+
var NuRole = class {
|
|
1353
|
+
constructor(name, expertise, goal) {
|
|
1354
|
+
this.name = name;
|
|
1355
|
+
this.expertise = expertise;
|
|
1356
|
+
this.goal = goal;
|
|
1357
|
+
}
|
|
1358
|
+
name;
|
|
1359
|
+
expertise;
|
|
1360
|
+
goal;
|
|
1361
|
+
};
|
|
1362
|
+
var NuOutput = class extends PatternOutput {
|
|
1363
|
+
constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime) {
|
|
1364
|
+
super(text, startTime, endTime);
|
|
1365
|
+
this.negotiatedRoles = negotiatedRoles;
|
|
1366
|
+
this.workflow = workflow;
|
|
1367
|
+
this.workflowReasoning = workflowReasoning;
|
|
1368
|
+
this.roleResults = roleResults;
|
|
1369
|
+
this.synthesis = synthesis;
|
|
1370
|
+
}
|
|
1371
|
+
negotiatedRoles;
|
|
1372
|
+
workflow;
|
|
1373
|
+
workflowReasoning;
|
|
1374
|
+
roleResults;
|
|
1375
|
+
synthesis;
|
|
1376
|
+
};
|
|
1377
|
+
var NEGOTIATE_SYSTEM = `You are a team architect. Given a task, propose a team of specialized agents. Each role must have a distinct name, expertise, and goal.
|
|
1378
|
+
|
|
1379
|
+
Output format \u2014 one role per block, exactly as shown:
|
|
1380
|
+
|
|
1381
|
+
ROLE:
|
|
1382
|
+
NAME: Security Analyst
|
|
1383
|
+
EXPERTISE: (1 sentence describing domain knowledge)
|
|
1384
|
+
GOAL: (1 sentence describing what this role must accomplish)
|
|
1385
|
+
|
|
1386
|
+
ROLE:
|
|
1387
|
+
NAME: Performance Engineer
|
|
1388
|
+
EXPERTISE: (1 sentence)
|
|
1389
|
+
GOAL: (1 sentence)
|
|
1390
|
+
|
|
1391
|
+
Propose between {min} and {max} roles. Each role must be clearly distinct.
|
|
1392
|
+
Output only the role blocks \u2014 no preamble, no summary.`;
|
|
1393
|
+
var WORKFLOW_SYSTEM = `You are a workflow designer. Given a set of agent roles, determine the best execution strategy.
|
|
1394
|
+
|
|
1395
|
+
Rules:
|
|
1396
|
+
- sequential: roles depend on each other's outputs (output of A is input to B)
|
|
1397
|
+
- parallel: roles can work independently on different aspects
|
|
1398
|
+
- mixed: some roles are independent, some depend on others' outputs
|
|
1399
|
+
|
|
1400
|
+
Output exactly:
|
|
1401
|
+
WORKFLOW: (sequential | parallel | mixed)
|
|
1402
|
+
REASONING: (1-2 sentences explaining why)`;
|
|
1403
|
+
var EXECUTE_SYSTEM2 = (role) => `You are a ${role.name}. Expertise: ${role.expertise}. Goal: ${role.goal}. Complete your assigned task thoroughly and concisely. Output your findings directly \u2014 no meta-commentary.`;
|
|
1404
|
+
var SYNTHESIS_SYSTEM3 = `You are a delivery manager. Synthesize the contributions from all team members into a coherent, comprehensive final answer. Combine overlapping insights, resolve conflicts, and prioritize the most impactful findings.`;
|
|
1405
|
+
async function negotiateRoles(task, opts) {
|
|
1406
|
+
if (opts.roles && opts.roles.length > 0) return opts.roles;
|
|
1407
|
+
const min = opts.minAgents ?? 2;
|
|
1408
|
+
const max = opts.maxAgents ?? 5;
|
|
1409
|
+
const prompt = NEGOTIATE_SYSTEM.replace("{min}", String(min)).replace("{max}", String(max));
|
|
1410
|
+
const response = await ask(`Task: ${task}
|
|
1411
|
+
|
|
1412
|
+
${prompt}`, {
|
|
1413
|
+
model: opts.plannerModel ?? opts.model,
|
|
1414
|
+
maxTokens: 2048,
|
|
1415
|
+
thinkingLevel: "high"
|
|
1416
|
+
});
|
|
1417
|
+
const roles = [];
|
|
1418
|
+
const roleBlocks = response.split(/ROLE\s*:/i).filter((b) => b.trim());
|
|
1419
|
+
for (const block of roleBlocks) {
|
|
1420
|
+
const nameMatch = block.match(/NAME\s*:\s*(.+)/i);
|
|
1421
|
+
const expertiseMatch = block.match(/EXPERTISE\s*:\s*(.+)/i);
|
|
1422
|
+
const goalMatch = block.match(/GOAL\s*:\s*(.+)/i);
|
|
1423
|
+
if (nameMatch && expertiseMatch && goalMatch) {
|
|
1424
|
+
roles.push(new NuRole(nameMatch[1].trim(), expertiseMatch[1].trim(), goalMatch[1].trim()));
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
if (roles.length === 0) {
|
|
1428
|
+
const lines = response.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1429
|
+
const fallbackRoles = lines.slice(0, max).map((line, i) => {
|
|
1430
|
+
const clean = line.replace(/^[-*\d]+[.)\s]*/, "").slice(0, 60);
|
|
1431
|
+
return new NuRole(`Agent ${i + 1}`, clean, `Execute sub-task: ${clean}`);
|
|
1432
|
+
});
|
|
1433
|
+
return fallbackRoles.length > 0 ? fallbackRoles : [new NuRole("Generalist", "Broad domain knowledge", task)];
|
|
1434
|
+
}
|
|
1435
|
+
return roles.slice(0, max);
|
|
1436
|
+
}
|
|
1437
|
+
async function decideWorkflow(roles, task, opts) {
|
|
1438
|
+
if (roles.length <= 1)
|
|
1439
|
+
return { workflow: "parallel", reasoning: "Single role \u2014 no dependencies." };
|
|
1440
|
+
const rolesText = roles.map((r, i) => `${i + 1}. ${r.name}: ${r.goal}`).join("\n");
|
|
1441
|
+
const response = await ask(
|
|
1442
|
+
`Task: ${task}
|
|
1443
|
+
|
|
1444
|
+
Roles:
|
|
1445
|
+
${rolesText}
|
|
1446
|
+
|
|
1447
|
+
Determine the best execution strategy.`,
|
|
1448
|
+
{
|
|
1449
|
+
model: opts.plannerModel ?? opts.model,
|
|
1450
|
+
maxTokens: 512,
|
|
1451
|
+
thinkingLevel: "high",
|
|
1452
|
+
system: WORKFLOW_SYSTEM
|
|
1453
|
+
}
|
|
1454
|
+
);
|
|
1455
|
+
const wfMatch = response.match(/WORKFLOW\s*:\s*(.+)/i);
|
|
1456
|
+
const reasonMatch = response.match(/REASONING\s*:\s*(.+)/i);
|
|
1457
|
+
const workflowRaw = (wfMatch?.[1] ?? "parallel").trim().toLowerCase();
|
|
1458
|
+
const workflow = workflowRaw.startsWith("seq") ? "sequential" : workflowRaw.startsWith("mix") ? "mixed" : "parallel";
|
|
1459
|
+
return {
|
|
1460
|
+
workflow,
|
|
1461
|
+
reasoning: reasonMatch?.[1]?.trim() ?? "Auto-determined based on role dependencies."
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
async function executeRoles(roles, task, workflow, opts) {
|
|
1465
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
1466
|
+
const results = [];
|
|
1467
|
+
if (workflow === "sequential") {
|
|
1468
|
+
let context = task;
|
|
1469
|
+
for (const role of roles) {
|
|
1470
|
+
const output = await ask(context, {
|
|
1471
|
+
model: workerModel,
|
|
1472
|
+
maxTokens: opts.maxTokens,
|
|
1473
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1474
|
+
system: EXECUTE_SYSTEM2(role)
|
|
1475
|
+
});
|
|
1476
|
+
results.push({ role: role.name, output });
|
|
1477
|
+
context = `Previous output from ${role.name}:
|
|
1478
|
+
${output}
|
|
1479
|
+
|
|
1480
|
+
Continue with: ${task}`;
|
|
1481
|
+
}
|
|
1482
|
+
} else {
|
|
1483
|
+
const parallelResults = await Promise.allSettled(
|
|
1484
|
+
roles.map(
|
|
1485
|
+
(role) => ask(task, {
|
|
1486
|
+
model: workerModel,
|
|
1487
|
+
maxTokens: opts.maxTokens,
|
|
1488
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1489
|
+
system: EXECUTE_SYSTEM2(role)
|
|
1490
|
+
}).then((text) => ({ role: role.name, output: text })).catch((err) => ({ role: role.name, output: `(failed: ${String(err)})` }))
|
|
1491
|
+
)
|
|
1492
|
+
);
|
|
1493
|
+
for (const r of parallelResults) {
|
|
1494
|
+
if (r.status === "fulfilled") results.push(r.value);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
return results;
|
|
1498
|
+
}
|
|
1499
|
+
async function synthesize(task, results, opts) {
|
|
1500
|
+
const resultsText = results.map((r) => `--- ${r.role} ---
|
|
1501
|
+
${r.output}`).join("\n\n");
|
|
1502
|
+
return ask(
|
|
1503
|
+
`Original task:
|
|
1504
|
+
${task}
|
|
1505
|
+
|
|
1506
|
+
Team member outputs:
|
|
1507
|
+
${resultsText}
|
|
1508
|
+
|
|
1509
|
+
Synthesize a comprehensive final answer.`,
|
|
1510
|
+
{
|
|
1511
|
+
model: opts.plannerModel ?? opts.model,
|
|
1512
|
+
maxTokens: opts.maxTokens,
|
|
1513
|
+
thinkingLevel: "high",
|
|
1514
|
+
system: SYNTHESIS_SYSTEM3
|
|
1515
|
+
}
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
async function execute9(pieces, args, opts) {
|
|
1519
|
+
const task = build(pieces, args);
|
|
1520
|
+
const t0 = Date.now();
|
|
1521
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
1522
|
+
if (!opts.quiet) {
|
|
1523
|
+
process.stderr.write(
|
|
1524
|
+
`\u039D: Self-Organizing Teams \u2014 "${task.slice(0, 80)}${task.length > 80 ? "..." : ""}"
|
|
1525
|
+
`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Negotiating roles...\n");
|
|
1529
|
+
const roles = await negotiateRoles(task, { ...opts, plannerModel });
|
|
1530
|
+
if (!opts.quiet) {
|
|
1531
|
+
process.stderr.write(` \u2192 ${roles.length} role(s) proposed:
|
|
1532
|
+
`);
|
|
1533
|
+
for (const r of roles) {
|
|
1534
|
+
process.stderr.write(
|
|
1535
|
+
` ${r.name}: ${r.goal.slice(0, 60)}${r.goal.length > 60 ? "..." : ""}
|
|
1536
|
+
`
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Deciding workflow...\n");
|
|
1541
|
+
const { workflow, reasoning } = await decideWorkflow(roles, task, { ...opts, plannerModel });
|
|
1542
|
+
if (!opts.quiet) {
|
|
1543
|
+
process.stderr.write(` \u2192 Workflow: ${workflow}
|
|
1544
|
+
`);
|
|
1545
|
+
process.stderr.write(
|
|
1546
|
+
` \u2192 Reasoning: ${reasoning.slice(0, 80)}${reasoning.length > 80 ? "..." : ""}
|
|
1547
|
+
`
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Executing (${workflow})...
|
|
1551
|
+
`);
|
|
1552
|
+
const roleResults = await executeRoles(roles, task, workflow, opts);
|
|
1553
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing...\n");
|
|
1554
|
+
const synthesis = await synthesize(task, roleResults, { ...opts, plannerModel });
|
|
1555
|
+
const t1 = Date.now();
|
|
1556
|
+
const summary = [
|
|
1557
|
+
`Roles: ${roles.map((r) => r.name).join(", ")}`,
|
|
1558
|
+
`Workflow: ${workflow} (${reasoning})`,
|
|
1559
|
+
`Results: ${roleResults.length}/${roles.length} succeeded`,
|
|
1560
|
+
`Synthesis: ${synthesis}`
|
|
1561
|
+
].join("\n\n");
|
|
1562
|
+
return new NuOutput(summary, roles, workflow, reasoning, roleResults, synthesis, t0, t1);
|
|
1563
|
+
}
|
|
1564
|
+
function makeNu(opts = {}) {
|
|
1565
|
+
const merged = { ...defaults9, ...opts };
|
|
1566
|
+
const fn = ((pieces, ...args) => {
|
|
1567
|
+
if (!Array.isArray(pieces)) {
|
|
1568
|
+
return makeNu({ ...merged, ...pieces });
|
|
1569
|
+
}
|
|
1570
|
+
return new PatternPromise((resolve, reject) => {
|
|
1571
|
+
execute9(pieces, args, merged).then(resolve, reject);
|
|
1572
|
+
});
|
|
1573
|
+
});
|
|
1574
|
+
let _quiet;
|
|
1575
|
+
Object.defineProperty(fn, "quiet", {
|
|
1576
|
+
get() {
|
|
1577
|
+
if (!_quiet) _quiet = makeNu({ ...merged, quiet: true });
|
|
1578
|
+
return _quiet;
|
|
1579
|
+
},
|
|
1580
|
+
enumerable: true,
|
|
1581
|
+
configurable: true
|
|
1582
|
+
});
|
|
1583
|
+
return fn;
|
|
1584
|
+
}
|
|
1585
|
+
var \u039D = makeNu();
|
|
1586
|
+
|
|
1587
|
+
// src/patterns/orchestrator.ts
|
|
1588
|
+
var defaults10 = {
|
|
1589
|
+
maxTokens: 4096,
|
|
1590
|
+
thinkingLevel: "medium",
|
|
1591
|
+
workers: 3,
|
|
1592
|
+
concurrency: 3
|
|
1593
|
+
};
|
|
1594
|
+
var OrchestratorWorkerResult = class {
|
|
1595
|
+
constructor(task, output, success) {
|
|
1596
|
+
this.task = task;
|
|
1597
|
+
this.output = output;
|
|
1598
|
+
this.success = success;
|
|
1599
|
+
}
|
|
1600
|
+
task;
|
|
1601
|
+
output;
|
|
1602
|
+
success;
|
|
1603
|
+
};
|
|
1604
|
+
var OrchestratorOutput = class extends PatternOutput {
|
|
1605
|
+
constructor(text, plan, synthesis, workerResults, startTime, endTime) {
|
|
1606
|
+
super(text, startTime, endTime);
|
|
1607
|
+
this.plan = plan;
|
|
1608
|
+
this.synthesis = synthesis;
|
|
1609
|
+
this.workerResults = workerResults;
|
|
1610
|
+
}
|
|
1611
|
+
plan;
|
|
1612
|
+
synthesis;
|
|
1613
|
+
workerResults;
|
|
1614
|
+
};
|
|
1615
|
+
var PLANNER_SYSTEM = `You are a senior architect and project planner. Given a high-level request, create a detailed execution plan.
|
|
1616
|
+
|
|
1617
|
+
Output in this exact format:
|
|
1618
|
+
|
|
1619
|
+
PLAN SUMMARY:
|
|
1620
|
+
(one paragraph summarizing the approach)
|
|
1621
|
+
|
|
1622
|
+
SUB-TASKS:
|
|
1623
|
+
1. (specific, actionable sub-task)
|
|
1624
|
+
2. (specific, actionable sub-task)
|
|
1625
|
+
3. (specific, actionable sub-task)
|
|
1626
|
+
|
|
1627
|
+
Each sub-task must be self-contained and independently executable.
|
|
1628
|
+
Generate exactly {$workerCount} sub-tasks (adjust to the requested worker count).
|
|
1629
|
+
Focus on concrete actions, not abstractions.`;
|
|
1630
|
+
var WORKER_SYSTEM = `You are a task specialist. Complete your assigned sub-task thoroughly and concisely. Output your findings, code, or analysis directly \u2014 no meta-commentary.`;
|
|
1631
|
+
var SYNTHESIS_SYSTEM4 = `You are a delivery manager. Synthesize the worker results into a final, coherent deliverable that fulfills the original request. Combine, reconcile, and structure the outputs. Address any gaps or conflicts.`;
|
|
1632
|
+
async function execute10(pieces, args, opts) {
|
|
1633
|
+
const request = build(pieces, args);
|
|
1634
|
+
const t0 = Date.now();
|
|
1635
|
+
const workerCount = opts.workers ?? 3;
|
|
1636
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
1637
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
1638
|
+
if (!opts.quiet) {
|
|
1639
|
+
process.stderr.write(
|
|
1640
|
+
`\u03A9: Orchestrator \u2014 "${request.slice(0, 80)}${request.length > 80 ? "..." : ""}"
|
|
1641
|
+
`
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
|
|
1645
|
+
const planText = await ask(request, {
|
|
1646
|
+
model: plannerModel,
|
|
1647
|
+
maxTokens: opts.maxTokens,
|
|
1648
|
+
thinkingLevel: "high",
|
|
1649
|
+
system: PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount))
|
|
1650
|
+
});
|
|
1651
|
+
const subTasks = [];
|
|
1652
|
+
const taskLines = planText.split("\n");
|
|
1653
|
+
let inTasks = false;
|
|
1654
|
+
for (const line of taskLines) {
|
|
1655
|
+
if (line.match(/^SUB-TASKS:/i)) {
|
|
1656
|
+
inTasks = true;
|
|
1657
|
+
continue;
|
|
1658
|
+
}
|
|
1659
|
+
if (inTasks && line.match(/^\d+[.)]\s/)) {
|
|
1660
|
+
subTasks.push(line.replace(/^\d+[.)]\s+/, "").trim());
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
const tasks = subTasks.length > 0 ? subTasks.slice(0, workerCount) : [request];
|
|
1664
|
+
if (!opts.quiet) {
|
|
1665
|
+
process.stderr.write(` \u2192 ${tasks.length} sub-task(s) identified
|
|
1666
|
+
`);
|
|
1667
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
1668
|
+
const t = tasks[i];
|
|
1669
|
+
process.stderr.write(` [${i + 1}] ${t.slice(0, 60)}${t.length > 60 ? "..." : ""}
|
|
1670
|
+
`);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
const workerResults = [];
|
|
1674
|
+
const concurrency = opts.concurrency ?? 3;
|
|
1675
|
+
for (let i = 0; i < tasks.length; i += concurrency) {
|
|
1676
|
+
const batch = tasks.slice(i, i + concurrency);
|
|
1677
|
+
const batchResults = await Promise.allSettled(
|
|
1678
|
+
batch.map(
|
|
1679
|
+
(task) => ask(task, {
|
|
1680
|
+
model: workerModel,
|
|
1681
|
+
maxTokens: opts.maxTokens,
|
|
1682
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1683
|
+
system: WORKER_SYSTEM
|
|
1684
|
+
}).then((text) => new OrchestratorWorkerResult(task, text, true)).catch((err) => new OrchestratorWorkerResult(task, String(err), false))
|
|
1685
|
+
)
|
|
1686
|
+
);
|
|
1687
|
+
batchResults.forEach((r) => {
|
|
1688
|
+
if (r.status === "fulfilled") workerResults.push(r.value);
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
|
|
1692
|
+
const workerTexts = workerResults.map((wr, i) => `Task ${i + 1}: ${wr.task}
|
|
1693
|
+
Result: ${wr.output}`).join("\n\n");
|
|
1694
|
+
const synthesis = await ask(
|
|
1695
|
+
`Original request:
|
|
1696
|
+
${request}
|
|
1697
|
+
|
|
1698
|
+
Plan:
|
|
1699
|
+
${planText}
|
|
1700
|
+
|
|
1701
|
+
Worker results:
|
|
1702
|
+
${workerTexts}
|
|
1703
|
+
|
|
1704
|
+
Synthesize a final deliverable.`,
|
|
1705
|
+
{
|
|
1706
|
+
model: plannerModel,
|
|
1707
|
+
maxTokens: opts.maxTokens,
|
|
1708
|
+
thinkingLevel: "high",
|
|
1709
|
+
system: SYNTHESIS_SYSTEM4
|
|
1710
|
+
}
|
|
1711
|
+
);
|
|
1712
|
+
const t1 = Date.now();
|
|
1713
|
+
const summary = `Plan:
|
|
1714
|
+
${planText}
|
|
1715
|
+
|
|
1716
|
+
Workers: ${workerResults.filter((w) => w.success).length}/${workerResults.length} succeeded
|
|
1717
|
+
|
|
1718
|
+
Synthesis:
|
|
1719
|
+
${synthesis}`;
|
|
1720
|
+
return new OrchestratorOutput(summary, planText, synthesis, workerResults, t0, t1);
|
|
1721
|
+
}
|
|
1722
|
+
function makeOrchestrator(opts = {}) {
|
|
1723
|
+
const merged = { ...defaults10, ...opts };
|
|
1724
|
+
const fn = ((pieces, ...args) => {
|
|
1725
|
+
if (!Array.isArray(pieces)) {
|
|
1726
|
+
return makeOrchestrator({ ...merged, ...pieces });
|
|
1727
|
+
}
|
|
1728
|
+
return new PatternPromise((resolve, reject) => {
|
|
1729
|
+
execute10(pieces, args, merged).then(resolve, reject);
|
|
1730
|
+
});
|
|
1731
|
+
});
|
|
1732
|
+
let _quiet;
|
|
1733
|
+
Object.defineProperty(fn, "quiet", {
|
|
1734
|
+
get() {
|
|
1735
|
+
if (!_quiet) _quiet = makeOrchestrator({ ...merged, quiet: true });
|
|
1736
|
+
return _quiet;
|
|
1737
|
+
},
|
|
1738
|
+
enumerable: true,
|
|
1739
|
+
configurable: true
|
|
1740
|
+
});
|
|
1741
|
+
return fn;
|
|
1742
|
+
}
|
|
1743
|
+
var \u03A9 = makeOrchestrator();
|
|
1744
|
+
|
|
1745
|
+
// src/patterns/pipeline.ts
|
|
1746
|
+
var defaults11 = {
|
|
1747
|
+
maxTokens: 4096,
|
|
1748
|
+
thinkingLevel: "medium"
|
|
1749
|
+
};
|
|
1750
|
+
var PipelineStageResult = class {
|
|
1751
|
+
constructor(stage, output, index) {
|
|
1752
|
+
this.stage = stage;
|
|
1753
|
+
this.output = output;
|
|
1754
|
+
this.index = index;
|
|
1755
|
+
}
|
|
1756
|
+
stage;
|
|
1757
|
+
output;
|
|
1758
|
+
index;
|
|
1759
|
+
};
|
|
1760
|
+
var PipelineOutput = class extends PatternOutput {
|
|
1761
|
+
constructor(text, finalOutput, stages, startTime, endTime) {
|
|
1762
|
+
super(text, startTime, endTime);
|
|
1763
|
+
this.finalOutput = finalOutput;
|
|
1764
|
+
this.stages = stages;
|
|
1765
|
+
}
|
|
1766
|
+
finalOutput;
|
|
1767
|
+
stages;
|
|
1768
|
+
};
|
|
1769
|
+
function parseStages(template, explicitStages, separator) {
|
|
1770
|
+
if (explicitStages && explicitStages.length > 0) return explicitStages;
|
|
1771
|
+
const sep = separator ?? "\u2192";
|
|
1772
|
+
const altSep = sep === "\u2192" ? "->" : sep;
|
|
1773
|
+
const bySep = template.split(sep).flatMap((s) => s.split(altSep)).map((s) => s.trim()).filter(Boolean);
|
|
1774
|
+
if (bySep.length > 1) return bySep;
|
|
1775
|
+
const byPipe = template.split("|").map((s) => s.trim()).filter(Boolean);
|
|
1776
|
+
if (byPipe.length > 1) return byPipe;
|
|
1777
|
+
const byLine = template.split("\n").map(
|
|
1778
|
+
(s) => s.trim().replace(/^\d+[.)]\s*/, "").replace(/^[-*]\s*/, "")
|
|
1779
|
+
).filter(Boolean);
|
|
1780
|
+
if (byLine.length > 1) return byLine;
|
|
1781
|
+
return [template];
|
|
1782
|
+
}
|
|
1783
|
+
function generateStagePrompt(stage, previousOutput, isFirst) {
|
|
1784
|
+
if (isFirst) {
|
|
1785
|
+
return `Task: ${stage}
|
|
1786
|
+
|
|
1787
|
+
Execute the task above.`;
|
|
1788
|
+
}
|
|
1789
|
+
return `Previous stage output:
|
|
1790
|
+
${previousOutput}
|
|
1791
|
+
|
|
1792
|
+
Current stage: ${stage}
|
|
1793
|
+
|
|
1794
|
+
Process the previous output according to the current stage's instructions.`;
|
|
1795
|
+
}
|
|
1796
|
+
async function execute11(pieces, args, opts) {
|
|
1797
|
+
const template = build(pieces, args);
|
|
1798
|
+
const stages = parseStages(template, opts.stages, opts.separator);
|
|
1799
|
+
const t0 = Date.now();
|
|
1800
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
1801
|
+
if (!opts.quiet) {
|
|
1802
|
+
process.stderr.write(`\u039B: Pipeline \u2014 ${stages.length} stage(s)
|
|
1803
|
+
`);
|
|
1804
|
+
for (let i = 0; i < stages.length; i++) {
|
|
1805
|
+
process.stderr.write(` [${i + 1}] ${stages[i]}
|
|
1806
|
+
`);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const stageResults = [];
|
|
1810
|
+
let currentInput = "";
|
|
1811
|
+
for (let i = 0; i < stages.length; i++) {
|
|
1812
|
+
const stage = stages[i];
|
|
1813
|
+
const customPrompt = opts.stagePrompts?.[i];
|
|
1814
|
+
if (!opts.quiet)
|
|
1815
|
+
process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stage.slice(0, 50)}...
|
|
1816
|
+
`);
|
|
1817
|
+
const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
|
|
1818
|
+
const systemMessage = i === 0 ? `You are a specialist executing stage ${i + 1}: ${stage}. Focus only on this stage's output.` : `You are a specialist executing stage ${i + 1}: ${stage}. Process the previous stage's output according to your instructions. Maintain all important information from previous stages.`;
|
|
1819
|
+
const output = await ask(prompt, {
|
|
1820
|
+
model: workerModel,
|
|
1821
|
+
maxTokens: opts.maxTokens,
|
|
1822
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1823
|
+
system: systemMessage
|
|
1824
|
+
});
|
|
1825
|
+
stageResults.push(new PipelineStageResult(stage, output, i));
|
|
1826
|
+
currentInput = output;
|
|
1827
|
+
}
|
|
1828
|
+
const t1 = Date.now();
|
|
1829
|
+
const finalOutput = currentInput;
|
|
1830
|
+
const summary = stageResults.map(
|
|
1831
|
+
(sr) => `Stage ${sr.index + 1} (${sr.stage}):
|
|
1832
|
+
${sr.output.slice(0, 200)}${sr.output.length > 200 ? "..." : ""}`
|
|
1833
|
+
).join("\n\n");
|
|
1834
|
+
return new PipelineOutput(summary, finalOutput, stageResults, t0, t1);
|
|
1835
|
+
}
|
|
1836
|
+
function makePipeline(opts = {}) {
|
|
1837
|
+
const merged = { ...defaults11, ...opts };
|
|
1838
|
+
const fn = ((pieces, ...args) => {
|
|
1839
|
+
if (!Array.isArray(pieces)) {
|
|
1840
|
+
return makePipeline({ ...merged, ...pieces });
|
|
1841
|
+
}
|
|
1842
|
+
return new PatternPromise((resolve, reject) => {
|
|
1843
|
+
execute11(pieces, args, merged).then(resolve, reject);
|
|
1844
|
+
});
|
|
1845
|
+
});
|
|
1846
|
+
let _quiet;
|
|
1847
|
+
Object.defineProperty(fn, "quiet", {
|
|
1848
|
+
get() {
|
|
1849
|
+
if (!_quiet) _quiet = makePipeline({ ...merged, quiet: true });
|
|
1850
|
+
return _quiet;
|
|
1851
|
+
},
|
|
1852
|
+
enumerable: true,
|
|
1853
|
+
configurable: true
|
|
1854
|
+
});
|
|
1855
|
+
return fn;
|
|
1856
|
+
}
|
|
1857
|
+
var \u039B = makePipeline();
|
|
1858
|
+
|
|
1859
|
+
// src/patterns/ralph.ts
|
|
1860
|
+
import { createAgentSession } from "@earendil-works/pi-coding-agent";
|
|
1861
|
+
var defaults12 = {
|
|
1862
|
+
maxIterations: 5,
|
|
1863
|
+
useTools: true,
|
|
1864
|
+
maxAgentTurns: 10,
|
|
1865
|
+
thinkingLevel: "medium",
|
|
1866
|
+
maxTokens: 4096
|
|
1867
|
+
};
|
|
1868
|
+
var RalphOutput = class extends PatternOutput {
|
|
1869
|
+
constructor(text, iterationCount, completed, iterations, startTime, endTime) {
|
|
1870
|
+
super(text, startTime, endTime);
|
|
1871
|
+
this.iterationCount = iterationCount;
|
|
1872
|
+
this.completed = completed;
|
|
1873
|
+
this.iterations = iterations;
|
|
1874
|
+
}
|
|
1875
|
+
iterationCount;
|
|
1876
|
+
completed;
|
|
1877
|
+
iterations;
|
|
1878
|
+
};
|
|
1879
|
+
async function executeWithTools(goal, opts) {
|
|
1880
|
+
const agentModel = opts.model ? pickModel(opts.model) : void 0;
|
|
1881
|
+
if (opts.model && !agentModel) {
|
|
1882
|
+
throw new Error(
|
|
1883
|
+
`pizx/\u03A1: model not found: "${opts.model}". Run \`pi models\` to see available models.`
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
const { session } = await createAgentSession({
|
|
1887
|
+
tools: ["read", "bash", "edit", "write", "grep", "ls"],
|
|
1888
|
+
...agentModel ? { model: agentModel } : {}
|
|
1889
|
+
});
|
|
1890
|
+
try {
|
|
1891
|
+
await session.sendUserMessage(goal);
|
|
1892
|
+
const msgs = session.messages ?? [];
|
|
1893
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1894
|
+
if (msgs[i].role === "assistant") {
|
|
1895
|
+
const c = msgs[i].content;
|
|
1896
|
+
if (typeof c === "string") return c.trim();
|
|
1897
|
+
if (Array.isArray(c)) {
|
|
1898
|
+
const texts = c.filter(
|
|
1899
|
+
(x) => x.type === "text" && typeof x.text === "string"
|
|
1900
|
+
).map((x) => x.text);
|
|
1901
|
+
if (texts.length > 0) return texts.join("").trim();
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
return "(no assistant response)";
|
|
1906
|
+
} finally {
|
|
1907
|
+
session.dispose();
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
var ANALYSIS_SYSTEM2 = `You are a senior engineer. Analyze the current state and identify what needs to change to achieve the goal. Be specific \u2014 name files and code patterns. Keep it under 200 words.`;
|
|
1911
|
+
var PLAN_SYSTEM2 = `You are a precise coding architect. Generate a minimal, actionable implementation plan with specific file paths and changes. Keep it under 250 words.`;
|
|
1912
|
+
var REVIEW_SYSTEM = `You are a quality assurance reviewer. Review the changes that were just made. Determine:
|
|
1913
|
+
1. Was the plan fully implemented? Answer "FULLY" or "PARTIALLY"
|
|
1914
|
+
2. Are there any issues? (1 sentence)
|
|
1915
|
+
3. Should we iterate again? Answer "ITERATE" or "DONE"
|
|
1916
|
+
|
|
1917
|
+
Your final line MUST be either "FINAL: ITERATE" or "FINAL: DONE".`;
|
|
1918
|
+
async function execute12(pieces, args, opts) {
|
|
1919
|
+
const goal = build(pieces, args);
|
|
1920
|
+
const t0 = Date.now();
|
|
1921
|
+
const iterations = [];
|
|
1922
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
1923
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
1924
|
+
if (!opts.quiet) {
|
|
1925
|
+
process.stderr.write(`\u03A1: Ralph Loop \u2014 "${goal.slice(0, 80)}${goal.length > 80 ? "..." : ""}"
|
|
1926
|
+
`);
|
|
1927
|
+
}
|
|
1928
|
+
let currentGoal = goal;
|
|
1929
|
+
let iteration = 1;
|
|
1930
|
+
while (iteration <= (opts.maxIterations ?? 5)) {
|
|
1931
|
+
if (!opts.quiet) {
|
|
1932
|
+
process.stderr.write(`\u03A1: Iteration ${iteration}/${opts.maxIterations}
|
|
1933
|
+
`);
|
|
1934
|
+
}
|
|
1935
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Analyzing...\n");
|
|
1936
|
+
const analysis = await ask(currentGoal, {
|
|
1937
|
+
model: plannerModel,
|
|
1938
|
+
maxTokens: opts.maxTokens,
|
|
1939
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1940
|
+
system: ANALYSIS_SYSTEM2
|
|
1941
|
+
});
|
|
1942
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
|
|
1943
|
+
const plan = await ask(
|
|
1944
|
+
`Goal: ${currentGoal}
|
|
1945
|
+
|
|
1946
|
+
Analysis: ${analysis}
|
|
1947
|
+
|
|
1948
|
+
Generate an implementation plan.`,
|
|
1949
|
+
{
|
|
1950
|
+
model: plannerModel,
|
|
1951
|
+
maxTokens: opts.maxTokens,
|
|
1952
|
+
thinkingLevel: opts.thinkingLevel,
|
|
1953
|
+
system: PLAN_SYSTEM2
|
|
1954
|
+
}
|
|
1955
|
+
);
|
|
1956
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Executing...\n");
|
|
1957
|
+
const result = opts.useTools ? await executeWithTools(`Implement this plan:
|
|
1958
|
+
${plan}
|
|
1959
|
+
|
|
1960
|
+
Goal: ${currentGoal}`, {
|
|
1961
|
+
...opts,
|
|
1962
|
+
model: workerModel
|
|
1963
|
+
}) : await ask(`Implement this plan:
|
|
1964
|
+
${plan}
|
|
1965
|
+
|
|
1966
|
+
Goal: ${currentGoal}`, {
|
|
1967
|
+
model: workerModel,
|
|
1968
|
+
maxTokens: opts.maxTokens,
|
|
1969
|
+
thinkingLevel: opts.thinkingLevel
|
|
1970
|
+
});
|
|
1971
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Reviewing...\n");
|
|
1972
|
+
const review = await ask(`Plan:
|
|
1973
|
+
${plan}
|
|
1974
|
+
|
|
1975
|
+
Result:
|
|
1976
|
+
${result}
|
|
1977
|
+
|
|
1978
|
+
Review the implementation.`, {
|
|
1979
|
+
model: plannerModel,
|
|
1980
|
+
maxTokens: 1024,
|
|
1981
|
+
thinkingLevel: "high",
|
|
1982
|
+
system: REVIEW_SYSTEM
|
|
1983
|
+
});
|
|
1984
|
+
const shouldContinue = review.includes("ITERATE") && !review.includes("DONE");
|
|
1985
|
+
iterations.push({
|
|
1986
|
+
iteration,
|
|
1987
|
+
plan,
|
|
1988
|
+
result,
|
|
1989
|
+
review,
|
|
1990
|
+
shouldContinue
|
|
1991
|
+
});
|
|
1992
|
+
if (!shouldContinue) {
|
|
1993
|
+
if (!opts.quiet)
|
|
1994
|
+
process.stderr.write(`\u03A1: Quality threshold reached after ${iteration} iteration(s)
|
|
1995
|
+
`);
|
|
1996
|
+
break;
|
|
1997
|
+
}
|
|
1998
|
+
currentGoal = `Continue improving. Previous plan: ${plan}
|
|
1999
|
+
Review feedback: ${review}
|
|
2000
|
+
Original goal: ${goal}`;
|
|
2001
|
+
iteration++;
|
|
2002
|
+
}
|
|
2003
|
+
const t1 = Date.now();
|
|
2004
|
+
const summary = iterations.map(
|
|
2005
|
+
(i) => `Iteration ${i.iteration}:
|
|
2006
|
+
Plan: ${i.plan.slice(0, 100)}...
|
|
2007
|
+
Review: ${i.review.slice(0, 100)}...`
|
|
2008
|
+
).join("\n");
|
|
2009
|
+
return new RalphOutput(
|
|
2010
|
+
summary,
|
|
2011
|
+
iterations.length,
|
|
2012
|
+
iteration <= (opts.maxIterations ?? 5),
|
|
2013
|
+
iterations,
|
|
2014
|
+
t0,
|
|
2015
|
+
t1
|
|
2016
|
+
);
|
|
2017
|
+
}
|
|
2018
|
+
function makeRalph(opts = {}) {
|
|
2019
|
+
const merged = { ...defaults12, ...opts };
|
|
2020
|
+
const fn = ((pieces, ...args) => {
|
|
2021
|
+
if (!Array.isArray(pieces)) {
|
|
2022
|
+
return makeRalph({ ...merged, ...pieces });
|
|
2023
|
+
}
|
|
2024
|
+
return new PatternPromise((resolve, reject) => {
|
|
2025
|
+
execute12(pieces, args, merged).then(resolve, reject);
|
|
2026
|
+
});
|
|
2027
|
+
});
|
|
2028
|
+
let _quiet;
|
|
2029
|
+
Object.defineProperty(fn, "quiet", {
|
|
2030
|
+
get() {
|
|
2031
|
+
if (!_quiet) _quiet = makeRalph({ ...merged, quiet: true });
|
|
2032
|
+
return _quiet;
|
|
2033
|
+
},
|
|
2034
|
+
enumerable: true,
|
|
2035
|
+
configurable: true
|
|
2036
|
+
});
|
|
2037
|
+
return fn;
|
|
2038
|
+
}
|
|
2039
|
+
var \u03A1 = makeRalph();
|
|
2040
|
+
|
|
2041
|
+
// src/patterns/subagent.ts
|
|
2042
|
+
var defaults13 = {
|
|
2043
|
+
maxTokens: 4096,
|
|
2044
|
+
thinkingLevel: "medium",
|
|
2045
|
+
maxSubTasks: 4,
|
|
2046
|
+
concurrency: 4
|
|
2047
|
+
};
|
|
2048
|
+
var SubagentResult = class {
|
|
2049
|
+
constructor(subTask, text, success) {
|
|
2050
|
+
this.subTask = subTask;
|
|
2051
|
+
this.text = text;
|
|
2052
|
+
this.success = success;
|
|
2053
|
+
}
|
|
2054
|
+
subTask;
|
|
2055
|
+
text;
|
|
2056
|
+
success;
|
|
2057
|
+
};
|
|
2058
|
+
var SubagentOutput = class extends PatternOutput {
|
|
2059
|
+
constructor(text, synthesis, subResults, startTime, endTime) {
|
|
2060
|
+
super(text, startTime, endTime);
|
|
2061
|
+
this.synthesis = synthesis;
|
|
2062
|
+
this.subResults = subResults;
|
|
2063
|
+
}
|
|
2064
|
+
synthesis;
|
|
2065
|
+
subResults;
|
|
2066
|
+
};
|
|
2067
|
+
var DECOMPOSE_SYSTEM = `You are a task decomposition specialist. Break down complex tasks into independent sub-tasks that can be worked on in parallel. Output ONLY a JSON array of strings, each being a self-contained sub-task description. No markdown, no explanation.`;
|
|
2068
|
+
var SYNTHESIS_SYSTEM5 = `You are a synthesis specialist. Combine the results from multiple sub-agent analyses into a coherent, comprehensive answer. Identify patterns, conflicts, and gaps.`;
|
|
2069
|
+
async function decomposeTask(task, opts) {
|
|
2070
|
+
if (opts.subdomains && opts.subdomains.length > 0) return opts.subdomains;
|
|
2071
|
+
const result = await ask(
|
|
2072
|
+
`Decompose this task into ${opts.maxSubTasks ?? 4} independent sub-tasks that can be worked on in parallel:
|
|
2073
|
+
|
|
2074
|
+
${task}
|
|
2075
|
+
|
|
2076
|
+
Output a JSON array of strings.`,
|
|
2077
|
+
{
|
|
2078
|
+
model: opts.model,
|
|
2079
|
+
maxTokens: 1024,
|
|
2080
|
+
thinkingLevel: "medium",
|
|
2081
|
+
system: DECOMPOSE_SYSTEM
|
|
2082
|
+
}
|
|
2083
|
+
);
|
|
2084
|
+
try {
|
|
2085
|
+
const jsonMatch = result.match(/\[[\s\S]*\]/);
|
|
2086
|
+
if (jsonMatch) {
|
|
2087
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
2088
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
2089
|
+
return parsed.map(String).slice(0, opts.maxSubTasks ?? 4);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
} catch {
|
|
2093
|
+
}
|
|
2094
|
+
return result.split("\n").map(
|
|
2095
|
+
(l) => l.replace(/^\d+[.)]\s*/, "").replace(/^[-*]\s*/, "").trim()
|
|
2096
|
+
).filter(Boolean).slice(0, opts.maxSubTasks ?? 4);
|
|
2097
|
+
}
|
|
2098
|
+
var SUBAGENT_SYSTEM = `You are a domain specialist. Complete your assigned sub-task thoroughly. Output your findings clearly and concisely.`;
|
|
2099
|
+
async function execute13(pieces, args, opts) {
|
|
2100
|
+
const task = build(pieces, args);
|
|
2101
|
+
const t0 = Date.now();
|
|
2102
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
2103
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
2104
|
+
if (!opts.quiet) {
|
|
2105
|
+
process.stderr.write(
|
|
2106
|
+
`\u03A3: Subagent delegation \u2014 "${task.slice(0, 80)}${task.length > 80 ? "..." : ""}"
|
|
2107
|
+
`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Decomposing task into sub-tasks...\n");
|
|
2111
|
+
const subTasks = await decomposeTask(task, { ...opts, model: plannerModel });
|
|
2112
|
+
if (!opts.quiet) {
|
|
2113
|
+
process.stderr.write(` \u2192 ${subTasks.length} sub-task(s) identified:
|
|
2114
|
+
`);
|
|
2115
|
+
for (let i = 0; i < subTasks.length; i++) {
|
|
2116
|
+
const st = subTasks[i];
|
|
2117
|
+
process.stderr.write(` [${i + 1}] ${st.slice(0, 60)}${st.length > 60 ? "..." : ""}
|
|
2118
|
+
`);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
const subResults = [];
|
|
2122
|
+
const concurrency = opts.concurrency ?? 4;
|
|
2123
|
+
for (let i = 0; i < subTasks.length; i += concurrency) {
|
|
2124
|
+
const batch = subTasks.slice(i, i + concurrency);
|
|
2125
|
+
const batchResults = await Promise.allSettled(
|
|
2126
|
+
batch.map(
|
|
2127
|
+
(st) => ask(st, {
|
|
2128
|
+
model: workerModel,
|
|
2129
|
+
maxTokens: opts.maxTokens,
|
|
2130
|
+
thinkingLevel: opts.thinkingLevel,
|
|
2131
|
+
system: SUBAGENT_SYSTEM
|
|
2132
|
+
}).then((text) => new SubagentResult(st, text, true)).catch((err) => new SubagentResult(st, String(err), false))
|
|
2133
|
+
)
|
|
2134
|
+
);
|
|
2135
|
+
batchResults.forEach((r) => {
|
|
2136
|
+
if (r.status === "fulfilled") subResults.push(r.value);
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
|
|
2140
|
+
const subResultsText = subResults.map((sr, i) => `Sub-task ${i + 1}: ${sr.subTask}
|
|
2141
|
+
Result: ${sr.text}`).join("\n\n");
|
|
2142
|
+
const synthesis = await ask(
|
|
2143
|
+
`Original task:
|
|
2144
|
+
${task}
|
|
2145
|
+
|
|
2146
|
+
Sub-task results:
|
|
2147
|
+
${subResultsText}
|
|
2148
|
+
|
|
2149
|
+
Synthesize a comprehensive answer.`,
|
|
2150
|
+
{
|
|
2151
|
+
model: plannerModel,
|
|
2152
|
+
maxTokens: opts.maxTokens,
|
|
2153
|
+
thinkingLevel: opts.thinkingLevel,
|
|
2154
|
+
system: SYNTHESIS_SYSTEM5
|
|
2155
|
+
}
|
|
2156
|
+
);
|
|
2157
|
+
const t1 = Date.now();
|
|
2158
|
+
return new SubagentOutput(synthesis, synthesis, subResults, t0, t1);
|
|
2159
|
+
}
|
|
2160
|
+
function makeSubagent(opts = {}) {
|
|
2161
|
+
const merged = { ...defaults13, ...opts };
|
|
2162
|
+
const fn = ((pieces, ...args) => {
|
|
2163
|
+
if (!Array.isArray(pieces)) {
|
|
2164
|
+
return makeSubagent({ ...merged, ...pieces });
|
|
2165
|
+
}
|
|
2166
|
+
return new PatternPromise((resolve, reject) => {
|
|
2167
|
+
execute13(pieces, args, merged).then(resolve, reject);
|
|
2168
|
+
});
|
|
2169
|
+
});
|
|
2170
|
+
let _quiet;
|
|
2171
|
+
Object.defineProperty(fn, "quiet", {
|
|
2172
|
+
get() {
|
|
2173
|
+
if (!_quiet) _quiet = makeSubagent({ ...merged, quiet: true });
|
|
2174
|
+
return _quiet;
|
|
2175
|
+
},
|
|
2176
|
+
enumerable: true,
|
|
2177
|
+
configurable: true
|
|
2178
|
+
});
|
|
2179
|
+
return fn;
|
|
2180
|
+
}
|
|
2181
|
+
var \u03A3 = makeSubagent();
|
|
2182
|
+
|
|
2183
|
+
// src/patterns/tau.ts
|
|
2184
|
+
var defaults14 = {
|
|
2185
|
+
maxTokens: 4096,
|
|
2186
|
+
thinkingLevel: "medium",
|
|
2187
|
+
agents: 3,
|
|
2188
|
+
rounds: 1
|
|
2189
|
+
};
|
|
2190
|
+
var ToolMediatedEntry = class {
|
|
2191
|
+
constructor(agent, round, operation, key, content) {
|
|
2192
|
+
this.agent = agent;
|
|
2193
|
+
this.round = round;
|
|
2194
|
+
this.operation = operation;
|
|
2195
|
+
this.key = key;
|
|
2196
|
+
this.content = content;
|
|
2197
|
+
}
|
|
2198
|
+
agent;
|
|
2199
|
+
round;
|
|
2200
|
+
operation;
|
|
2201
|
+
key;
|
|
2202
|
+
content;
|
|
2203
|
+
};
|
|
2204
|
+
var TauOutput = class extends PatternOutput {
|
|
2205
|
+
constructor(text, entries, finalState, synthesis, startTime, endTime) {
|
|
2206
|
+
super(text, startTime, endTime);
|
|
2207
|
+
this.entries = entries;
|
|
2208
|
+
this.finalState = finalState;
|
|
2209
|
+
this.synthesis = synthesis;
|
|
2210
|
+
}
|
|
2211
|
+
entries;
|
|
2212
|
+
finalState;
|
|
2213
|
+
synthesis;
|
|
2214
|
+
};
|
|
2215
|
+
var SCHEMA_SYSTEM = `You are a coordination architect. Given a task, design a shared structured context for agent collaboration.
|
|
2216
|
+
|
|
2217
|
+
Output format exactly:
|
|
2218
|
+
KEYS: key1, key2, key3, key4
|
|
2219
|
+
|
|
2220
|
+
AGENT 1:
|
|
2221
|
+
ROLE: (one-word role name)
|
|
2222
|
+
ASSIGNED_KEYS: key1, key2
|
|
2223
|
+
|
|
2224
|
+
AGENT 2:
|
|
2225
|
+
ROLE: (one-word role name)
|
|
2226
|
+
ASSIGNED_KEYS: key3
|
|
2227
|
+
|
|
2228
|
+
Define exactly {agentCount} agents with distinct roles. Each agent is assigned 1-2 keys.
|
|
2229
|
+
Keys should be named categories relevant to the task (e.g., "Market_Size", "Competitors", "Risks").`;
|
|
2230
|
+
var WRITE_SYSTEM = (role, keys) => `You are a ${role}. Write your initial findings to your assigned keys in the shared context.
|
|
2231
|
+
|
|
2232
|
+
SHARED CONTEXT (current state \u2014 may be empty):
|
|
2233
|
+
{store}
|
|
2234
|
+
|
|
2235
|
+
Your assigned keys: ${keys}
|
|
2236
|
+
|
|
2237
|
+
Write initial, thorough findings to each of your keys. Output ONLY in this format:
|
|
2238
|
+
|
|
2239
|
+
KEY: key_name
|
|
2240
|
+
VALUE: your findings for this key
|
|
2241
|
+
|
|
2242
|
+
KEY: key_name
|
|
2243
|
+
VALUE: your findings for this key`;
|
|
2244
|
+
var UPDATE_SYSTEM = (role, keys) => `You are a ${role}. Review the current shared context and refine/update your entries.
|
|
2245
|
+
|
|
2246
|
+
SHARED CONTEXT (current state from all agents):
|
|
2247
|
+
{store}
|
|
2248
|
+
|
|
2249
|
+
Your assigned keys: ${keys}
|
|
2250
|
+
|
|
2251
|
+
Review what other agents wrote. Update your entries to:
|
|
2252
|
+
- Fill gaps others haven't covered
|
|
2253
|
+
- Add depth and specific details
|
|
2254
|
+
- Challenge or validate others' findings where relevant
|
|
2255
|
+
- Avoid repeating what's already well-covered
|
|
2256
|
+
|
|
2257
|
+
Output ONLY in this format:
|
|
2258
|
+
|
|
2259
|
+
KEY: key_name
|
|
2260
|
+
VALUE: your updated findings for this key`;
|
|
2261
|
+
var CONSOLIDATE_SYSTEM = `You are a research director. Consolidate the structured findings from all specialists into a comprehensive, well-organized synthesis. Combine overlapping insights, resolve contradictions, and prioritize the most impactful findings. Structure your output clearly.`;
|
|
2262
|
+
async function defineSchema(task, opts) {
|
|
2263
|
+
const agentCount = opts.agents ?? 3;
|
|
2264
|
+
const prompt = SCHEMA_SYSTEM.replace("{agentCount}", String(agentCount));
|
|
2265
|
+
const response = await ask(`Task: ${task}
|
|
2266
|
+
|
|
2267
|
+
${prompt}`, {
|
|
2268
|
+
model: opts.plannerModel ?? opts.model,
|
|
2269
|
+
maxTokens: 1024,
|
|
2270
|
+
thinkingLevel: "high"
|
|
2271
|
+
});
|
|
2272
|
+
const keysMatch = response.match(/KEYS\s*:\s*(.+)/i);
|
|
2273
|
+
const keys = keysMatch?.[1]?.split(",").map((k) => k.trim()).filter(Boolean) ?? ["Findings", "Risks", "Recommendations"];
|
|
2274
|
+
const agentRegex = /AGENT\s+\d+\s*:\s*\nROLE\s*:\s*(.+?)\nASSIGNED_KEYS\s*:\s*(.+?)(?:\n|$)/gi;
|
|
2275
|
+
const roles = [];
|
|
2276
|
+
const assignments = /* @__PURE__ */ new Map();
|
|
2277
|
+
let match;
|
|
2278
|
+
while ((match = agentRegex.exec(response)) !== null) {
|
|
2279
|
+
const role = match[1].trim();
|
|
2280
|
+
const agentKeys = match[2].split(",").map((k) => k.trim()).filter(Boolean);
|
|
2281
|
+
roles.push(role);
|
|
2282
|
+
assignments.set(role, agentKeys.length > 0 ? agentKeys : [keys[0] ?? "General"]);
|
|
2283
|
+
}
|
|
2284
|
+
if (roles.length === 0) {
|
|
2285
|
+
for (let i = 0; i < agentCount; i++) {
|
|
2286
|
+
const role = `Specialist ${i + 1}`;
|
|
2287
|
+
roles.push(role);
|
|
2288
|
+
const assignedKey = keys[i % keys.length] ?? `Key_${i + 1}`;
|
|
2289
|
+
assignments.set(role, [assignedKey]);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
return { keys, roles: roles.slice(0, agentCount), assignments };
|
|
2293
|
+
}
|
|
2294
|
+
function formatStore(store) {
|
|
2295
|
+
const entries = Object.entries(store).filter(([, v]) => v);
|
|
2296
|
+
if (entries.length === 0) return "(empty \u2014 you are the first contributor)";
|
|
2297
|
+
return entries.map(([k, v]) => `[${k}]: ${v}`).join("\n\n");
|
|
2298
|
+
}
|
|
2299
|
+
function mergeEntry(store, key, content) {
|
|
2300
|
+
if (store[key]) {
|
|
2301
|
+
store[key] += `
|
|
2302
|
+
|
|
2303
|
+
${content}`;
|
|
2304
|
+
} else {
|
|
2305
|
+
store[key] = content;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
async function executeRound(roles, assignments, store, round, opts) {
|
|
2309
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
2310
|
+
const isWrite = round === 1;
|
|
2311
|
+
const operation = isWrite ? "write" : "update";
|
|
2312
|
+
const roundResults = await Promise.allSettled(
|
|
2313
|
+
roles.map(async (role) => {
|
|
2314
|
+
const assignedKeys = assignments.get(role) ?? ["General"];
|
|
2315
|
+
const keysStr = assignedKeys.join(", ");
|
|
2316
|
+
const storeText = formatStore(store);
|
|
2317
|
+
const systemPrompt = isWrite ? WRITE_SYSTEM(role, keysStr).replace("{store}", storeText) : UPDATE_SYSTEM(role, keysStr).replace("{store}", storeText);
|
|
2318
|
+
const task = isWrite ? `Write your initial findings to your assigned keys: ${keysStr}` : `Review the shared context and update your entries for keys: ${keysStr}`;
|
|
2319
|
+
const response = await ask(task, {
|
|
2320
|
+
model: workerModel,
|
|
2321
|
+
maxTokens: opts.maxTokens,
|
|
2322
|
+
thinkingLevel: opts.thinkingLevel,
|
|
2323
|
+
system: systemPrompt
|
|
2324
|
+
});
|
|
2325
|
+
return { role, response };
|
|
2326
|
+
})
|
|
2327
|
+
);
|
|
2328
|
+
const entries = [];
|
|
2329
|
+
const newStore = { ...store };
|
|
2330
|
+
for (const r of roundResults) {
|
|
2331
|
+
if (r.status !== "fulfilled") continue;
|
|
2332
|
+
const { role, response } = r.value;
|
|
2333
|
+
const kvRegex = /KEY\s*:\s*(.+?)\nVALUE\s*:\s*([\s\S]*?)(?=\nKEY\s*:|\n*$)/gi;
|
|
2334
|
+
let kvMatch;
|
|
2335
|
+
let found = false;
|
|
2336
|
+
while ((kvMatch = kvRegex.exec(response)) !== null) {
|
|
2337
|
+
const key = kvMatch[1].trim();
|
|
2338
|
+
const value = kvMatch[2].trim();
|
|
2339
|
+
entries.push(new ToolMediatedEntry(role, round, operation, key, value));
|
|
2340
|
+
mergeEntry(newStore, key, value);
|
|
2341
|
+
found = true;
|
|
2342
|
+
}
|
|
2343
|
+
if (!found) {
|
|
2344
|
+
const fallbackKey = assignments.get(role)?.[0] ?? "General";
|
|
2345
|
+
const content = response.trim();
|
|
2346
|
+
entries.push(new ToolMediatedEntry(role, round, operation, fallbackKey, content));
|
|
2347
|
+
mergeEntry(newStore, fallbackKey, content);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
return { entries, store: newStore };
|
|
2351
|
+
}
|
|
2352
|
+
async function consolidateStore(task, store, opts) {
|
|
2353
|
+
const storeText = formatStore(store);
|
|
2354
|
+
return ask(
|
|
2355
|
+
`Original task: ${task}
|
|
2356
|
+
|
|
2357
|
+
Structured findings from all specialists:
|
|
2358
|
+
|
|
2359
|
+
${storeText}
|
|
2360
|
+
|
|
2361
|
+
Consolidate into a comprehensive, well-structured synthesis.`,
|
|
2362
|
+
{
|
|
2363
|
+
model: opts.plannerModel ?? opts.model,
|
|
2364
|
+
maxTokens: opts.maxTokens,
|
|
2365
|
+
thinkingLevel: "high",
|
|
2366
|
+
system: CONSOLIDATE_SYSTEM
|
|
2367
|
+
}
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
async function execute14(pieces, args, opts) {
|
|
2371
|
+
const task = build(pieces, args);
|
|
2372
|
+
const t0 = Date.now();
|
|
2373
|
+
const totalRounds = opts.rounds ?? 1;
|
|
2374
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
2375
|
+
if (!opts.quiet) {
|
|
2376
|
+
process.stderr.write(
|
|
2377
|
+
`\u03A4: Tool-Mediated Orchestration \u2014 "${task.slice(0, 80)}${task.length > 80 ? "..." : ""}"
|
|
2378
|
+
`
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Defining shared context schema...\n");
|
|
2382
|
+
const { keys, roles, assignments } = await defineSchema(task, { ...opts, plannerModel });
|
|
2383
|
+
if (!opts.quiet) {
|
|
2384
|
+
process.stderr.write(` \u2192 Schema: ${keys.join(", ")}
|
|
2385
|
+
`);
|
|
2386
|
+
process.stderr.write(` \u2192 ${roles.length} agent(s): ${roles.join(", ")}
|
|
2387
|
+
`);
|
|
2388
|
+
for (const [role, assignedKeys] of assignments) {
|
|
2389
|
+
process.stderr.write(` ${role} \u2192 ${assignedKeys.join(", ")}
|
|
2390
|
+
`);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
const allEntries = [];
|
|
2394
|
+
let store = {};
|
|
2395
|
+
for (let round = 1; round <= totalRounds; round++) {
|
|
2396
|
+
const label = round === 1 ? "Writing" : "Updating";
|
|
2397
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Round ${round}/${totalRounds}: ${label}...
|
|
2398
|
+
`);
|
|
2399
|
+
const { entries, store: updatedStore } = await executeRound(
|
|
2400
|
+
roles,
|
|
2401
|
+
assignments,
|
|
2402
|
+
store,
|
|
2403
|
+
round,
|
|
2404
|
+
opts
|
|
2405
|
+
);
|
|
2406
|
+
allEntries.push(...entries);
|
|
2407
|
+
store = updatedStore;
|
|
2408
|
+
}
|
|
2409
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Consolidating store...\n");
|
|
2410
|
+
const synthesis = await consolidateStore(task, store, { ...opts, plannerModel });
|
|
2411
|
+
const t1 = Date.now();
|
|
2412
|
+
const summary = [
|
|
2413
|
+
`Schema keys: ${keys.join(", ")}`,
|
|
2414
|
+
`Agents: ${roles.join(", ")}`,
|
|
2415
|
+
`Rounds: ${totalRounds}`,
|
|
2416
|
+
`Entries: ${allEntries.length}`,
|
|
2417
|
+
`Synthesis: ${synthesis}`
|
|
2418
|
+
].join("\n\n");
|
|
2419
|
+
return new TauOutput(summary, allEntries, store, synthesis, t0, t1);
|
|
2420
|
+
}
|
|
2421
|
+
function makeTau(opts = {}) {
|
|
2422
|
+
const merged = { ...defaults14, ...opts };
|
|
2423
|
+
const fn = ((pieces, ...args) => {
|
|
2424
|
+
if (!Array.isArray(pieces)) {
|
|
2425
|
+
return makeTau({ ...merged, ...pieces });
|
|
2426
|
+
}
|
|
2427
|
+
return new PatternPromise((resolve, reject) => {
|
|
2428
|
+
execute14(pieces, args, merged).then(resolve, reject);
|
|
2429
|
+
});
|
|
2430
|
+
});
|
|
2431
|
+
let _quiet;
|
|
2432
|
+
Object.defineProperty(fn, "quiet", {
|
|
2433
|
+
get() {
|
|
2434
|
+
if (!_quiet) _quiet = makeTau({ ...merged, quiet: true });
|
|
2435
|
+
return _quiet;
|
|
2436
|
+
},
|
|
2437
|
+
enumerable: true,
|
|
2438
|
+
configurable: true
|
|
2439
|
+
});
|
|
2440
|
+
return fn;
|
|
2441
|
+
}
|
|
2442
|
+
var \u03A4 = makeTau();
|
|
2443
|
+
|
|
2444
|
+
// src/patterns/thread.ts
|
|
2445
|
+
var defaults15 = {
|
|
2446
|
+
maxTokens: 4096,
|
|
2447
|
+
thinkingLevel: "medium",
|
|
2448
|
+
agents: 3,
|
|
2449
|
+
turns: 3
|
|
2450
|
+
};
|
|
2451
|
+
var ROLE_SETS4 = {
|
|
2452
|
+
2: ["Proposer \u2014 advocate the best approach", "Critic \u2014 identify weaknesses and gaps"],
|
|
2453
|
+
3: [
|
|
2454
|
+
"Proposer \u2014 suggest the best approach",
|
|
2455
|
+
"Critic \u2014 identify weaknesses, risks, and missing pieces",
|
|
2456
|
+
"Synthesizer \u2014 combine the best ideas into a practical plan"
|
|
2457
|
+
],
|
|
2458
|
+
4: [
|
|
2459
|
+
"Proposer \u2014 advocate a bold solution",
|
|
2460
|
+
"Critic \u2014 identify risks and weaknesses",
|
|
2461
|
+
"Pragmatist \u2014 focus on practical implementation",
|
|
2462
|
+
"Innovator \u2014 propose creative alternatives"
|
|
2463
|
+
],
|
|
2464
|
+
5: [
|
|
2465
|
+
"Proposer",
|
|
2466
|
+
"Critic",
|
|
2467
|
+
"Pragmatist",
|
|
2468
|
+
"Innovator",
|
|
2469
|
+
"Devil's Advocate \u2014 challenge every assumption"
|
|
2470
|
+
]
|
|
2471
|
+
};
|
|
2472
|
+
var ThreadMessage = class {
|
|
2473
|
+
constructor(role, turn, content) {
|
|
2474
|
+
this.role = role;
|
|
2475
|
+
this.turn = turn;
|
|
2476
|
+
this.content = content;
|
|
2477
|
+
}
|
|
2478
|
+
role;
|
|
2479
|
+
turn;
|
|
2480
|
+
content;
|
|
2481
|
+
};
|
|
2482
|
+
var ThreadOutput = class extends PatternOutput {
|
|
2483
|
+
constructor(text, conclusion, messages, startTime, endTime) {
|
|
2484
|
+
super(text, startTime, endTime);
|
|
2485
|
+
this.conclusion = conclusion;
|
|
2486
|
+
this.messages = messages;
|
|
2487
|
+
}
|
|
2488
|
+
conclusion;
|
|
2489
|
+
messages;
|
|
2490
|
+
};
|
|
2491
|
+
var THREAD_PROMPT = `You are an agent with the role: {role}.
|
|
2492
|
+
Engage in a multi-agent conversation about the topic.
|
|
2493
|
+
|
|
2494
|
+
The conversation so far:
|
|
2495
|
+
{thread}
|
|
2496
|
+
|
|
2497
|
+
Respond as your role. Be specific, build on or challenge what others have said.
|
|
2498
|
+
Keep your response under 200 words.`;
|
|
2499
|
+
var SYNTHESIS_SYSTEM6 = `You are a neutral facilitator. Synthesize the multi-agent conversation thread into a clear, actionable conclusion. Weigh the evidence, resolve conflicts, and present the best path forward.`;
|
|
2500
|
+
function buildThreadPrompt(role, thread) {
|
|
2501
|
+
return THREAD_PROMPT.replace("{role}", role).replace(
|
|
2502
|
+
"{thread}",
|
|
2503
|
+
thread || "(This is the first message in the thread.)"
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
async function execute15(pieces, args, opts) {
|
|
2507
|
+
const topic = build(pieces, args);
|
|
2508
|
+
const t0 = Date.now();
|
|
2509
|
+
const agentCount = opts.agents ?? 3;
|
|
2510
|
+
const maxTurns = opts.turns ?? 3;
|
|
2511
|
+
const roles = opts.roles ?? ROLE_SETS4[agentCount] ?? ROLE_SETS4[3] ?? [];
|
|
2512
|
+
const plannerModel = opts.plannerModel ?? opts.model;
|
|
2513
|
+
const workerModel = opts.workerModel ?? opts.model;
|
|
2514
|
+
if (!opts.quiet) {
|
|
2515
|
+
process.stderr.write(`\u0398: Thread \u2014 ${agentCount} agent(s), ${maxTurns} turn(s)
|
|
2516
|
+
`);
|
|
2517
|
+
process.stderr.write(` Topic: "${topic.slice(0, 80)}${topic.length > 80 ? "..." : ""}"
|
|
2518
|
+
`);
|
|
2519
|
+
}
|
|
2520
|
+
const messages = [];
|
|
2521
|
+
let thread = `Topic: ${topic}
|
|
2522
|
+
`;
|
|
2523
|
+
for (let turn = 1; turn <= maxTurns; turn++) {
|
|
2524
|
+
if (!opts.quiet) process.stderr.write(` \u2192 Turn ${turn}/${maxTurns}
|
|
2525
|
+
`);
|
|
2526
|
+
for (let a = 0; a < roles.length; a++) {
|
|
2527
|
+
const role = roles[a] ?? `Agent ${a + 1}`;
|
|
2528
|
+
const prompt = buildThreadPrompt(role, thread);
|
|
2529
|
+
const response = await ask(prompt, {
|
|
2530
|
+
model: workerModel,
|
|
2531
|
+
maxTokens: opts.maxTokens,
|
|
2532
|
+
thinkingLevel: opts.thinkingLevel
|
|
2533
|
+
});
|
|
2534
|
+
messages.push(new ThreadMessage(role, turn, response));
|
|
2535
|
+
thread += `
|
|
2536
|
+
[${role}] (Turn ${turn}): ${response}
|
|
2537
|
+
`;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing conclusion...\n");
|
|
2541
|
+
const conclusion = await ask(
|
|
2542
|
+
`Topic: ${topic}
|
|
2543
|
+
|
|
2544
|
+
Conversation thread:
|
|
2545
|
+
${thread}
|
|
2546
|
+
|
|
2547
|
+
Synthesize a clear, actionable conclusion.`,
|
|
2548
|
+
{
|
|
2549
|
+
model: plannerModel,
|
|
2550
|
+
maxTokens: opts.maxTokens,
|
|
2551
|
+
thinkingLevel: "high",
|
|
2552
|
+
system: SYNTHESIS_SYSTEM6
|
|
2553
|
+
}
|
|
2554
|
+
);
|
|
2555
|
+
const t1 = Date.now();
|
|
2556
|
+
const summary = messages.map(
|
|
2557
|
+
(m) => `[${m.role}] Turn ${m.turn}: ${m.content.slice(0, 150)}${m.content.length > 150 ? "..." : ""}`
|
|
2558
|
+
).join("\n");
|
|
2559
|
+
return new ThreadOutput(summary, conclusion, messages, t0, t1);
|
|
2560
|
+
}
|
|
2561
|
+
function makeThread(opts = {}) {
|
|
2562
|
+
const merged = { ...defaults15, ...opts };
|
|
2563
|
+
const fn = ((pieces, ...args) => {
|
|
2564
|
+
if (!Array.isArray(pieces)) {
|
|
2565
|
+
return makeThread({ ...merged, ...pieces });
|
|
2566
|
+
}
|
|
2567
|
+
return new PatternPromise((resolve, reject) => {
|
|
2568
|
+
execute15(pieces, args, merged).then(resolve, reject);
|
|
2569
|
+
});
|
|
2570
|
+
});
|
|
2571
|
+
let _quiet;
|
|
2572
|
+
Object.defineProperty(fn, "quiet", {
|
|
2573
|
+
get() {
|
|
2574
|
+
if (!_quiet) _quiet = makeThread({ ...merged, quiet: true });
|
|
2575
|
+
return _quiet;
|
|
2576
|
+
},
|
|
2577
|
+
enumerable: true,
|
|
2578
|
+
configurable: true
|
|
2579
|
+
});
|
|
2580
|
+
return fn;
|
|
2581
|
+
}
|
|
2582
|
+
var \u0398 = makeThread();
|
|
2583
|
+
|
|
2584
|
+
// src/pi.ts
|
|
2585
|
+
import {
|
|
2586
|
+
getEnvApiKey as getEnvApiKey2,
|
|
2587
|
+
getModels as getModels2,
|
|
2588
|
+
getProviders as getProviders2,
|
|
2589
|
+
streamSimple
|
|
2590
|
+
} from "@earendil-works/pi-ai";
|
|
2591
|
+
|
|
2592
|
+
// src/pi-output.ts
|
|
2593
|
+
var PiOutput = class {
|
|
2594
|
+
constructor(text, modelUsed, events = [], startTime = Date.now(), endTime = Date.now()) {
|
|
2595
|
+
this.text = text;
|
|
2596
|
+
this.modelUsed = modelUsed;
|
|
2597
|
+
this.events = events;
|
|
2598
|
+
this.startTime = startTime;
|
|
2599
|
+
this.endTime = endTime;
|
|
2600
|
+
}
|
|
2601
|
+
text;
|
|
2602
|
+
modelUsed;
|
|
2603
|
+
events;
|
|
2604
|
+
startTime;
|
|
2605
|
+
endTime;
|
|
2606
|
+
/** Duration in milliseconds */
|
|
2607
|
+
get duration() {
|
|
2608
|
+
return this.endTime - this.startTime;
|
|
2609
|
+
}
|
|
2610
|
+
toString() {
|
|
2611
|
+
return this.text;
|
|
2612
|
+
}
|
|
2613
|
+
valueOf() {
|
|
2614
|
+
return this.text;
|
|
2615
|
+
}
|
|
2616
|
+
[Symbol.toPrimitive]() {
|
|
2617
|
+
return this.text;
|
|
2618
|
+
}
|
|
2619
|
+
/** Number of characters in output */
|
|
2620
|
+
get length() {
|
|
2621
|
+
return this.text.length;
|
|
2622
|
+
}
|
|
2623
|
+
/** Number of lines in output */
|
|
2624
|
+
get lines() {
|
|
2625
|
+
return this.text.split("\n").length;
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2628
|
+
|
|
2629
|
+
// src/pi.ts
|
|
2630
|
+
var defaults16 = {
|
|
2631
|
+
thinkingLevel: "medium",
|
|
2632
|
+
quiet: false,
|
|
2633
|
+
maxTokens: 4096
|
|
2634
|
+
};
|
|
2635
|
+
var _piSettings2;
|
|
2636
|
+
function getPiDefaults2() {
|
|
2637
|
+
if (_piSettings2 === void 0) {
|
|
2638
|
+
_piSettings2 = isPiInstalled() ? loadPiSettings() : {};
|
|
2639
|
+
}
|
|
2640
|
+
return _piSettings2;
|
|
2641
|
+
}
|
|
2642
|
+
function allModels2() {
|
|
2643
|
+
const result = [];
|
|
2644
|
+
for (const p of getProviders2()) {
|
|
2645
|
+
const ms = getModels2(p);
|
|
2646
|
+
if (ms && ms.length > 0) result.push(...ms);
|
|
2647
|
+
}
|
|
2648
|
+
return result;
|
|
2649
|
+
}
|
|
2650
|
+
function getConfiguredProviders2() {
|
|
2651
|
+
return getProviders2().filter((p) => getEnvApiKey2(p) !== void 0);
|
|
2652
|
+
}
|
|
2653
|
+
function configuredModels2() {
|
|
2654
|
+
const configured = new Set(getConfiguredProviders2());
|
|
2655
|
+
return allModels2().filter((m) => configured.has(m.provider));
|
|
2656
|
+
}
|
|
2657
|
+
function findModelById2(id) {
|
|
2658
|
+
const all = allModels2();
|
|
2659
|
+
if (id.includes("/")) {
|
|
2660
|
+
const [provider, modelId] = id.split("/", 2);
|
|
2661
|
+
return all.find(
|
|
2662
|
+
(m) => m.provider === provider && (m.id === modelId || m.id.endsWith(`/${modelId}`))
|
|
2663
|
+
);
|
|
2664
|
+
}
|
|
2665
|
+
return all.find((m) => m.id === id || m.id.endsWith(`/${id}`));
|
|
2666
|
+
}
|
|
2667
|
+
function pickModel2(preferred) {
|
|
2668
|
+
if (preferred) {
|
|
2669
|
+
const hit = findModelById2(preferred);
|
|
2670
|
+
if (hit) return hit;
|
|
2671
|
+
}
|
|
2672
|
+
const settings = getPiDefaults2();
|
|
2673
|
+
if (settings.defaultModel) {
|
|
2674
|
+
const hit = findModelById2(settings.defaultModel);
|
|
2675
|
+
if (hit) return hit;
|
|
2676
|
+
}
|
|
2677
|
+
if (settings.defaultProvider) {
|
|
2678
|
+
const providerModels = getModels2(settings.defaultProvider);
|
|
2679
|
+
if (providerModels && providerModels.length > 0) {
|
|
2680
|
+
const configured = new Set(getConfiguredProviders2());
|
|
2681
|
+
if (configured.has(settings.defaultProvider)) {
|
|
2682
|
+
return providerModels[0];
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
const available = configuredModels2();
|
|
2687
|
+
if (available.length > 0) {
|
|
2688
|
+
const order2 = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
|
|
2689
|
+
for (const id of order2) {
|
|
2690
|
+
const m = available.find((m2) => m2.id.includes(id));
|
|
2691
|
+
if (m) return m;
|
|
2692
|
+
}
|
|
2693
|
+
return available[0];
|
|
2694
|
+
}
|
|
2695
|
+
const models = allModels2();
|
|
2696
|
+
if (models.length === 0) return void 0;
|
|
2697
|
+
const order = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
|
|
2698
|
+
for (const id of order) {
|
|
2699
|
+
const m = models.find((m2) => m2.id.includes(id));
|
|
2700
|
+
if (m) return m;
|
|
2701
|
+
}
|
|
2702
|
+
return models[0];
|
|
2703
|
+
}
|
|
2704
|
+
function build2(pieces, args) {
|
|
2705
|
+
let s = "";
|
|
2706
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
2707
|
+
s += pieces[i];
|
|
2708
|
+
if (i < args.length) s += String(args[i]);
|
|
2709
|
+
}
|
|
2710
|
+
return s;
|
|
2711
|
+
}
|
|
2712
|
+
function makeContext(pieces, args, opts) {
|
|
2713
|
+
return {
|
|
2714
|
+
systemPrompt: opts.system,
|
|
2715
|
+
messages: [
|
|
2716
|
+
{
|
|
2717
|
+
role: "user",
|
|
2718
|
+
content: build2(pieces, args),
|
|
2719
|
+
timestamp: Date.now()
|
|
2720
|
+
}
|
|
2721
|
+
]
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
function makeOpts(opts) {
|
|
2725
|
+
return {
|
|
2726
|
+
maxTokens: opts.maxTokens,
|
|
2727
|
+
reasoning: opts.thinkingLevel
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
async function run(pieces, args, opts) {
|
|
2731
|
+
const model = pickModel2(opts.model);
|
|
2732
|
+
if (!model) throw new Error("pizx/\u03C0: No AI models configured. Run `pi auth login` first.");
|
|
2733
|
+
const t0 = Date.now();
|
|
2734
|
+
let text = "";
|
|
2735
|
+
for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
|
|
2736
|
+
if (ev.type === "text_delta") {
|
|
2737
|
+
text += ev.delta;
|
|
2738
|
+
if (!opts.quiet) process.stdout.write(ev.delta);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
if (!opts.quiet && text) process.stdout.write("\n");
|
|
2742
|
+
return new PiOutput(text.trim(), model.id, [], t0, Date.now());
|
|
2743
|
+
}
|
|
2744
|
+
async function* runStream(pieces, args, opts) {
|
|
2745
|
+
const model = pickModel2(opts.model);
|
|
2746
|
+
if (!model) throw new Error("pizx/\u03C0: No AI models configured");
|
|
2747
|
+
for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
|
|
2748
|
+
if (ev.type === "text_delta") yield ev.delta;
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
var PiPromise = class extends Promise {
|
|
2752
|
+
_modelId = "";
|
|
2753
|
+
constructor(fn, modelId) {
|
|
2754
|
+
super(fn);
|
|
2755
|
+
if (modelId) this._modelId = modelId;
|
|
2756
|
+
}
|
|
2757
|
+
get modelUsed() {
|
|
2758
|
+
return this._modelId;
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
function makePi(opts = {}) {
|
|
2762
|
+
const merged = { ...defaults16, ...opts };
|
|
2763
|
+
const fn = ((pieces, ...args) => {
|
|
2764
|
+
if (!Array.isArray(pieces)) {
|
|
2765
|
+
return makePi({ ...merged, ...pieces });
|
|
2766
|
+
}
|
|
2767
|
+
return new PiPromise((resolve, reject) => {
|
|
2768
|
+
run(pieces, args, merged).then(resolve, reject);
|
|
2769
|
+
});
|
|
2770
|
+
});
|
|
2771
|
+
let _quiet;
|
|
2772
|
+
Object.defineProperty(fn, "quiet", {
|
|
2773
|
+
get() {
|
|
2774
|
+
if (!_quiet) _quiet = makePi({ ...merged, quiet: true });
|
|
2775
|
+
return _quiet;
|
|
2776
|
+
},
|
|
2777
|
+
enumerable: true,
|
|
2778
|
+
configurable: true
|
|
2779
|
+
});
|
|
2780
|
+
fn.stream = async function* stream_pi(pieces, ...args) {
|
|
2781
|
+
yield* runStream(pieces, args, merged);
|
|
2782
|
+
};
|
|
2783
|
+
return fn;
|
|
2784
|
+
}
|
|
2785
|
+
var \u03C0 = makePi();
|
|
2786
|
+
function configurePi(opts) {
|
|
2787
|
+
Object.assign(defaults16, opts);
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
// src/pi-agent.ts
|
|
2791
|
+
import { createAgentSession as createAgentSession2 } from "@earendil-works/pi-coding-agent";
|
|
2792
|
+
var _agentDefaults = {
|
|
2793
|
+
quiet: false,
|
|
2794
|
+
maxTurns: 10
|
|
2795
|
+
};
|
|
2796
|
+
var AgentOutput = class {
|
|
2797
|
+
constructor(text, turnCount = 0, startTime = Date.now(), endTime = Date.now()) {
|
|
2798
|
+
this.text = text;
|
|
2799
|
+
this.turnCount = turnCount;
|
|
2800
|
+
this.startTime = startTime;
|
|
2801
|
+
this.endTime = endTime;
|
|
2802
|
+
}
|
|
2803
|
+
text;
|
|
2804
|
+
turnCount;
|
|
2805
|
+
startTime;
|
|
2806
|
+
endTime;
|
|
2807
|
+
get duration() {
|
|
2808
|
+
return this.endTime - this.startTime;
|
|
2809
|
+
}
|
|
2810
|
+
toString() {
|
|
2811
|
+
return this.text;
|
|
2812
|
+
}
|
|
2813
|
+
valueOf() {
|
|
2814
|
+
return this.text;
|
|
2815
|
+
}
|
|
2816
|
+
[Symbol.toPrimitive]() {
|
|
2817
|
+
return this.text;
|
|
2818
|
+
}
|
|
2819
|
+
};
|
|
2820
|
+
var AgentPromise = class extends Promise {
|
|
2821
|
+
};
|
|
2822
|
+
var _sharedSession = null;
|
|
2823
|
+
async function getSession(opts) {
|
|
2824
|
+
if (_sharedSession && !opts.model) return _sharedSession;
|
|
2825
|
+
const result = await createAgentSession2({
|
|
2826
|
+
cwd: opts.cwd,
|
|
2827
|
+
thinkingLevel: opts.thinkingLevel,
|
|
2828
|
+
tools: opts.tools,
|
|
2829
|
+
excludeTools: opts.excludeTools
|
|
2830
|
+
});
|
|
2831
|
+
_sharedSession = result.session;
|
|
2832
|
+
return _sharedSession;
|
|
2833
|
+
}
|
|
2834
|
+
function build3(pieces, args) {
|
|
2835
|
+
let s = "";
|
|
2836
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
2837
|
+
s += pieces[i];
|
|
2838
|
+
if (i < args.length) s += String(args[i]);
|
|
2839
|
+
}
|
|
2840
|
+
return s.trim();
|
|
2841
|
+
}
|
|
2842
|
+
function getMessageText(msg) {
|
|
2843
|
+
const content = msg.content;
|
|
2844
|
+
if (typeof content === "string") return content;
|
|
2845
|
+
if (Array.isArray(content)) {
|
|
2846
|
+
return content.filter(
|
|
2847
|
+
(c) => c.type === "text" && typeof c.text === "string"
|
|
2848
|
+
).map((c) => c.text).join("");
|
|
2849
|
+
}
|
|
2850
|
+
return "";
|
|
2851
|
+
}
|
|
2852
|
+
function getLastAssistantText(session) {
|
|
2853
|
+
const messages = session.messages;
|
|
2854
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2855
|
+
if (messages[i].role === "assistant") {
|
|
2856
|
+
return getMessageText(messages[i]);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
return "";
|
|
2860
|
+
}
|
|
2861
|
+
async function execute16(pieces, args, opts) {
|
|
2862
|
+
const prompt = build3(pieces, args);
|
|
2863
|
+
const session = await getSession(opts);
|
|
2864
|
+
const t0 = Date.now();
|
|
2865
|
+
if (!opts.quiet) {
|
|
2866
|
+
process.stderr.write(`\u03A0: ${prompt.slice(0, 100)}${prompt.length > 100 ? "..." : ""}
|
|
2867
|
+
`);
|
|
2868
|
+
}
|
|
2869
|
+
try {
|
|
2870
|
+
await session.sendUserMessage(prompt);
|
|
2871
|
+
const t1 = Date.now();
|
|
2872
|
+
const text = getLastAssistantText(session);
|
|
2873
|
+
const turnCount = session.messages.filter(
|
|
2874
|
+
(m) => m.role === "assistant"
|
|
2875
|
+
).length;
|
|
2876
|
+
return new AgentOutput(text || "(no assistant response)", turnCount, t0, t1);
|
|
2877
|
+
} catch (err) {
|
|
2878
|
+
const t1 = Date.now();
|
|
2879
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2880
|
+
console.error("\u03A0 error:", err);
|
|
2881
|
+
return new AgentOutput(`\u03A0 error: ${msg}`, 0, t0, t1);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
function makeAgent(opts = {}) {
|
|
2885
|
+
const merged = { ..._agentDefaults, ...opts };
|
|
2886
|
+
const fn = ((pieces, ...args) => {
|
|
2887
|
+
if (!Array.isArray(pieces)) {
|
|
2888
|
+
return makeAgent({ ...merged, ...pieces });
|
|
2889
|
+
}
|
|
2890
|
+
return new AgentPromise((resolve, reject) => {
|
|
2891
|
+
execute16(pieces, args, merged).then(resolve, reject);
|
|
2892
|
+
});
|
|
2893
|
+
});
|
|
2894
|
+
let _quiet;
|
|
2895
|
+
Object.defineProperty(fn, "quiet", {
|
|
2896
|
+
get() {
|
|
2897
|
+
if (!_quiet) _quiet = makeAgent({ ...merged, quiet: true });
|
|
2898
|
+
return _quiet;
|
|
2899
|
+
},
|
|
2900
|
+
enumerable: true,
|
|
2901
|
+
configurable: true
|
|
2902
|
+
});
|
|
2903
|
+
return fn;
|
|
2904
|
+
}
|
|
2905
|
+
var \u03A0 = makeAgent();
|
|
2906
|
+
function configureAgent(opts) {
|
|
2907
|
+
Object.assign(_agentDefaults, opts);
|
|
2908
|
+
}
|
|
2909
|
+
async function closeAgent() {
|
|
2910
|
+
if (_sharedSession) {
|
|
2911
|
+
_sharedSession.dispose();
|
|
2912
|
+
_sharedSession = null;
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
export {
|
|
2916
|
+
AdaptiveOutput,
|
|
2917
|
+
AdaptiveStep,
|
|
2918
|
+
AgentOutput,
|
|
2919
|
+
AgentPromise,
|
|
2920
|
+
BroadcastOutput,
|
|
2921
|
+
BroadcastResponse,
|
|
2922
|
+
CritiqueOutput,
|
|
2923
|
+
CritiqueRound,
|
|
2924
|
+
DebateOutput,
|
|
2925
|
+
DebatePerspective,
|
|
2926
|
+
FleetMemberOutput,
|
|
2927
|
+
FleetOutput,
|
|
2928
|
+
GraphNodeResult,
|
|
2929
|
+
GraphOutput,
|
|
2930
|
+
MemoryEntry,
|
|
2931
|
+
MemoryOutput,
|
|
2932
|
+
OrchestratorOutput,
|
|
2933
|
+
OrchestratorWorkerResult,
|
|
2934
|
+
PatternOutput,
|
|
2935
|
+
PatternPromise,
|
|
2936
|
+
PiOutput,
|
|
2937
|
+
PiPromise,
|
|
2938
|
+
PipelineOutput,
|
|
2939
|
+
PipelineStageResult,
|
|
2940
|
+
RalphOutput,
|
|
2941
|
+
SubagentOutput,
|
|
2942
|
+
SubagentResult,
|
|
2943
|
+
ThreadMessage,
|
|
2944
|
+
ThreadOutput,
|
|
2945
|
+
closeAgent,
|
|
2946
|
+
configureAgent,
|
|
2947
|
+
configurePi,
|
|
2948
|
+
\u0391,
|
|
2949
|
+
\u0392,
|
|
2950
|
+
\u0393,
|
|
2951
|
+
\u0394,
|
|
2952
|
+
\u0398,
|
|
2953
|
+
\u039B,
|
|
2954
|
+
\u039C,
|
|
2955
|
+
\u03A0,
|
|
2956
|
+
\u03A1,
|
|
2957
|
+
\u03A3,
|
|
2958
|
+
\u03A6,
|
|
2959
|
+
\u03A8,
|
|
2960
|
+
\u03A9,
|
|
2961
|
+
\u03C0
|
|
2962
|
+
};
|
|
2963
|
+
//# sourceMappingURL=index.js.map
|