@topce/pizx 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -2,7 +2,15 @@
2
2
  export * from "zx";
3
3
 
4
4
  // src/patterns/types.ts
5
- import { completeSimple, getEnvApiKey, getModels, getProviders } from "@earendil-works/pi-ai";
5
+ import { createInterface } from "node:readline";
6
+ import { completeSimple } from "@earendil-works/pi-ai";
7
+
8
+ // src/model-picker.ts
9
+ import {
10
+ getEnvApiKey,
11
+ getModels,
12
+ getProviders
13
+ } from "@earendil-works/pi-ai";
6
14
 
7
15
  // src/load-pi-settings.ts
8
16
  import { existsSync, readFileSync } from "node:fs";
@@ -33,40 +41,7 @@ function loadPiSettings(agentDir) {
33
41
  }
34
42
  }
35
43
 
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
- }
44
+ // src/model-picker.ts
70
45
  var _piSettings;
71
46
  function getPiDefaults() {
72
47
  if (_piSettings === void 0) {
@@ -110,7 +85,8 @@ function pickModel(preferred) {
110
85
  if (hit) return hit;
111
86
  }
112
87
  if (settings.defaultProvider) {
113
- const providerModels = getModels(settings.defaultProvider);
88
+ const provider = settings.defaultProvider;
89
+ const providerModels = getModels(provider);
114
90
  if (providerModels && providerModels.length > 0) {
115
91
  const configured = new Set(getConfiguredProviders());
116
92
  if (configured.has(settings.defaultProvider)) {
@@ -136,9 +112,138 @@ function pickModel(preferred) {
136
112
  }
137
113
  return models[0];
138
114
  }
115
+
116
+ // src/patterns/types.ts
117
+ var PatternOutput = class {
118
+ constructor(text, startTime = Date.now(), endTime = Date.now()) {
119
+ this.text = text;
120
+ this.startTime = startTime;
121
+ this.endTime = endTime;
122
+ }
123
+ text;
124
+ startTime;
125
+ endTime;
126
+ /** Execution trace: one entry per LLM call within this pattern run. Populated by createPatternTag. */
127
+ trace = [];
128
+ /** Structured phase log: key phases during execution, populated by each pattern. */
129
+ phaseLog = [];
130
+ /** Duration in milliseconds */
131
+ get duration() {
132
+ return this.endTime - this.startTime;
133
+ }
134
+ /** Total input tokens across all calls */
135
+ get inputTokens() {
136
+ return this.trace.reduce((s, t) => s + t.inputTokens, 0);
137
+ }
138
+ /** Total output tokens across all calls */
139
+ get outputTokens() {
140
+ return this.trace.reduce((s, t) => s + t.outputTokens, 0);
141
+ }
142
+ /** Total tokens (input + output) across all calls */
143
+ get totalTokens() {
144
+ return this.trace.reduce((s, t) => s + t.totalTokens, 0);
145
+ }
146
+ /** Total cost in USD across all calls */
147
+ get totalCost() {
148
+ return this.trace.reduce((s, t) => s + t.cost, 0);
149
+ }
150
+ /** Number of LLM calls made during this pattern execution */
151
+ get callCount() {
152
+ return this.trace.length;
153
+ }
154
+ toString() {
155
+ return this.text;
156
+ }
157
+ valueOf() {
158
+ return this.text;
159
+ }
160
+ [Symbol.toPrimitive]() {
161
+ return this.text;
162
+ }
163
+ };
164
+ var PatternPromise = class extends Promise {
165
+ };
166
+ var _trace = null;
167
+ function beginTrace() {
168
+ _trace = [];
169
+ }
170
+ function collectTrace() {
171
+ const t = _trace ?? [];
172
+ _trace = null;
173
+ return t;
174
+ }
175
+ function pushTrace(entry) {
176
+ if (_trace) _trace.push(entry);
177
+ }
178
+ function createPatternTag(defaults17, execute17) {
179
+ function make(opts = {}) {
180
+ const merged = { ...defaults17, ...opts };
181
+ const fn = ((pieces, ...args) => {
182
+ if (!Array.isArray(pieces)) {
183
+ return make({ ...merged, ...pieces });
184
+ }
185
+ beginTrace();
186
+ return new PatternPromise((resolve, reject) => {
187
+ execute17(pieces, args, merged).then(
188
+ (output) => {
189
+ output.trace = collectTrace();
190
+ resolve(output);
191
+ },
192
+ (err) => {
193
+ collectTrace();
194
+ reject(err);
195
+ }
196
+ );
197
+ });
198
+ });
199
+ let _quiet;
200
+ Object.defineProperty(fn, "quiet", {
201
+ get() {
202
+ if (!_quiet) _quiet = make({ ...merged, quiet: true });
203
+ return _quiet;
204
+ },
205
+ enumerable: true,
206
+ configurable: true
207
+ });
208
+ return fn;
209
+ }
210
+ return make();
211
+ }
212
+ function build(pieces, args) {
213
+ let s = "";
214
+ for (let i = 0; i < pieces.length; i++) {
215
+ s += pieces[i];
216
+ if (i < args.length) s += String(args[i]);
217
+ }
218
+ return s.trim();
219
+ }
220
+ function mergeSystem(userSystem, patternSystem) {
221
+ if (!userSystem) return patternSystem;
222
+ return `${userSystem}
223
+
224
+ ${patternSystem}`;
225
+ }
226
+ async function confirmPhase(description, opts) {
227
+ if (!opts.confirm) return true;
228
+ if (!opts.quiet) {
229
+ process.stderr.write(`
230
+ \u2500\u2500 Confirm \u2500\u2500
231
+ ${description}
232
+ Proceed? [Y/n] `);
233
+ }
234
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
235
+ const answer = await new Promise((resolve) => {
236
+ rl.question("", (ans) => resolve(ans));
237
+ });
238
+ rl.close();
239
+ const trimmed = answer.trim().toLowerCase();
240
+ if (trimmed === "" || trimmed === "y" || trimmed === "yes") return true;
241
+ return false;
242
+ }
139
243
  async function ask(prompt, opts = {}) {
140
244
  const model = pickModel(opts.model);
141
245
  if (!model) throw new Error("pizx/patterns: No AI models configured. Run `pi auth login` first.");
246
+ const t0 = Date.now();
142
247
  const result = await completeSimple(
143
248
  model,
144
249
  {
@@ -147,12 +252,74 @@ async function ask(prompt, opts = {}) {
147
252
  },
148
253
  {
149
254
  maxTokens: opts.maxTokens ?? 4096,
150
- reasoning: opts.thinkingLevel ?? "medium"
255
+ reasoning: opts.thinkingLevel ?? "medium",
256
+ timeoutMs: opts.timeoutMs,
257
+ maxRetries: opts.maxRetries
151
258
  }
152
259
  );
260
+ const durationMs = Date.now() - t0;
261
+ if (!result.content || !Array.isArray(result.content)) {
262
+ throw new Error("pizx/patterns: Unexpected response format from AI model.");
263
+ }
153
264
  const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
265
+ if (_trace !== null) {
266
+ pushTrace({
267
+ call: _trace.length + 1,
268
+ modelId: result.model,
269
+ promptPreview: prompt.slice(0, 200),
270
+ outputPreview: text.slice(0, 200),
271
+ inputTokens: result.usage.input,
272
+ outputTokens: result.usage.output,
273
+ cacheReadTokens: result.usage.cacheRead,
274
+ cacheWriteTokens: result.usage.cacheWrite,
275
+ totalTokens: result.usage.totalTokens,
276
+ cost: result.usage.cost.total,
277
+ durationMs
278
+ });
279
+ }
154
280
  return text.trim();
155
281
  }
282
+ var QUALITY_REVIEW_SYSTEM = `You are a quality assurance reviewer. Evaluate the final deliverable against the original request.
283
+
284
+ Output format:
285
+ SCORE: 0.XX (quality score from 0.0 to 1.0)
286
+ ASSESSMENT: (1-2 sentences \u2014 is the output complete, consistent, and actionable?)
287
+ RECOMMENDATION: (1 sentence \u2014 what would improve this output?)`;
288
+ async function runQualityReview(originalRequest, finalOutput, opts) {
289
+ if (!opts.qualityCheck) return void 0;
290
+ const reviewText = await ask(
291
+ `Original request:
292
+ ${originalRequest}
293
+
294
+ Final deliverable:
295
+ ${finalOutput}
296
+
297
+ Evaluate the quality.`,
298
+ {
299
+ model: opts.plannerModel ?? opts.model,
300
+ maxTokens: 512,
301
+ thinkingLevel: "high",
302
+ timeoutMs: opts.timeoutMs,
303
+ maxRetries: opts.maxRetries,
304
+ system: QUALITY_REVIEW_SYSTEM
305
+ }
306
+ );
307
+ const scoreMatch = reviewText.match(/SCORE:\s*([\d.]+)/i);
308
+ const assessMatch = reviewText.match(/ASSESSMENT:\s*(.+)/i);
309
+ const recMatch = reviewText.match(/RECOMMENDATION:\s*(.+)/i);
310
+ const result = {
311
+ score: scoreMatch ? parseFloat(scoreMatch[1]) : 0.5,
312
+ assessment: assessMatch?.[1]?.trim() ?? "(no assessment)",
313
+ recommendation: recMatch?.[1]?.trim() ?? "(no recommendation)"
314
+ };
315
+ if (!opts.quiet) {
316
+ process.stderr.write(` Quality score: ${result.score.toFixed(2)}
317
+ `);
318
+ process.stderr.write(` ${result.assessment.slice(0, 80)}...
319
+ `);
320
+ }
321
+ return result;
322
+ }
156
323
 
157
324
  // src/patterns/adaptive.ts
158
325
  var defaults = {
@@ -216,10 +383,10 @@ async function execute(pieces, args, opts) {
216
383
  }
217
384
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
218
385
  const planText = await ask(goal, {
386
+ ...opts,
219
387
  model: plannerModel,
220
- maxTokens: opts.maxTokens,
221
388
  thinkingLevel: "high",
222
- system: PLAN_SYSTEM
389
+ system: mergeSystem(opts.system, PLAN_SYSTEM)
223
390
  });
224
391
  const planLines = planText.split("\n");
225
392
  const plannedSteps = [];
@@ -249,10 +416,9 @@ async function execute(pieces, args, opts) {
249
416
  process.stderr.write(` \u2192 Step ${executionStep}: ${currentStep.slice(0, 60)}...
250
417
  `);
251
418
  const result = await ask(currentStep, {
419
+ ...opts,
252
420
  model: workerModel,
253
- maxTokens: opts.maxTokens,
254
- thinkingLevel: opts.thinkingLevel,
255
- system: EXECUTE_SYSTEM
421
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM)
256
422
  });
257
423
  const evaluation = await ask(
258
424
  `Goal: ${goal}
@@ -261,10 +427,11 @@ Result: ${result}
261
427
 
262
428
  Evaluate the result.`,
263
429
  {
430
+ ...opts,
264
431
  model: plannerModel,
265
432
  maxTokens: 512,
266
433
  thinkingLevel: "high",
267
- system: EVALUATE_SYSTEM
434
+ system: mergeSystem(opts.system, EVALUATE_SYSTEM)
268
435
  }
269
436
  );
270
437
  const scoreMatch = evaluation.match(/SCORE:\s*([\d.]+)/i);
@@ -287,8 +454,7 @@ Evaluate the result.`,
287
454
  break;
288
455
  }
289
456
  const adaptUpper = adaptation.toUpperCase();
290
- if (adaptUpper.startsWith("REFINE")) {
291
- } else if (adaptUpper.startsWith("SKIP_NEXT")) {
457
+ if (adaptUpper.startsWith("SKIP_NEXT")) {
292
458
  stepIndex += 2;
293
459
  } else if (adaptUpper.startsWith("ADD")) {
294
460
  const newStep = adaptation.replace(/^ADD\s*/i, "");
@@ -307,36 +473,76 @@ Evaluate the result.`,
307
473
  ).join("\n\n");
308
474
  return new AdaptiveOutput(summary, finalResult, adaptiveSteps, executionStep, t0, t1);
309
475
  }
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();
476
+ var \u0391 = createPatternTag(defaults, execute);
332
477
 
333
- // src/patterns/broadcast.ts
334
- var defaults2 = {
335
- maxTokens: 4096,
336
- thinkingLevel: "medium",
337
- workers: 4
478
+ // src/patterns/role-sets.ts
479
+ var DEBATE_ROLE_SETS = {
480
+ 2: [
481
+ "Optimist \u2014 advocate for the most ambitious approach",
482
+ "Pessimist \u2014 identify risks and failure modes"
483
+ ],
484
+ 3: [
485
+ "Optimist \u2014 advocate the benefits and opportunities",
486
+ "Pessimist \u2014 identify risks, costs, and failure modes",
487
+ "Pragmatist \u2014 focus on practical trade-offs and implementation"
488
+ ],
489
+ 4: [
490
+ "Optimist \u2014 argue for the best-case potential",
491
+ "Pessimist \u2014 highlight worst-case risks and downsides",
492
+ "Pragmatist \u2014 balance pros/cons with practical constraints",
493
+ "Innovator \u2014 propose creative alternatives and novel approaches"
494
+ ],
495
+ 5: [
496
+ "Optimist",
497
+ "Pessimist",
498
+ "Pragmatist",
499
+ "Innovator",
500
+ "User Advocate \u2014 focus on end-user experience and accessibility"
501
+ ]
502
+ };
503
+ var MEMORY_ROLE_SETS = {
504
+ 2: ["Analyst \u2014 deep analysis of core aspects", "Reviewer \u2014 check for gaps and blind spots"],
505
+ 3: [
506
+ "Analyst \u2014 deep analysis of core aspects",
507
+ "Reviewer \u2014 check for gaps, edge cases, and blind spots",
508
+ "Strategist \u2014 connect findings to actionable insights"
509
+ ],
510
+ 4: [
511
+ "Analyst",
512
+ "Reviewer",
513
+ "Strategist",
514
+ "Innovator \u2014 propose novel angles and creative solutions"
515
+ ],
516
+ 5: [
517
+ "Analyst",
518
+ "Reviewer",
519
+ "Strategist",
520
+ "Innovator",
521
+ "Skeptic \u2014 challenge assumptions and stress-test conclusions"
522
+ ]
523
+ };
524
+ var THREAD_ROLE_SETS = {
525
+ 2: ["Proposer \u2014 advocate the best approach", "Critic \u2014 identify weaknesses and gaps"],
526
+ 3: [
527
+ "Proposer \u2014 suggest the best approach",
528
+ "Critic \u2014 identify weaknesses, risks, and missing pieces",
529
+ "Synthesizer \u2014 combine the best ideas into a practical plan"
530
+ ],
531
+ 4: [
532
+ "Proposer \u2014 advocate a bold solution",
533
+ "Critic \u2014 identify risks and weaknesses",
534
+ "Pragmatist \u2014 focus on practical implementation",
535
+ "Innovator \u2014 propose creative alternatives"
536
+ ],
537
+ 5: [
538
+ "Proposer",
539
+ "Critic",
540
+ "Pragmatist",
541
+ "Innovator",
542
+ "Devil's Advocate \u2014 challenge every assumption"
543
+ ]
338
544
  };
339
- var ROLE_SETS = {
545
+ var BROADCAST_ROLE_SETS = {
340
546
  2: [
341
547
  "Technical Expert \u2014 evaluate technical feasibility",
342
548
  "Business Expert \u2014 evaluate business viability"
@@ -360,6 +566,13 @@ var ROLE_SETS = {
360
566
  "Innovation Expert \u2014 suggest novel approaches and alternatives"
361
567
  ]
362
568
  };
569
+
570
+ // src/patterns/broadcast.ts
571
+ var defaults2 = {
572
+ maxTokens: 4096,
573
+ thinkingLevel: "medium",
574
+ workers: 4
575
+ };
363
576
  var BroadcastResponse = class {
364
577
  constructor(role, response, success, error) {
365
578
  this.role = role;
@@ -373,13 +586,15 @@ var BroadcastResponse = class {
373
586
  error;
374
587
  };
375
588
  var BroadcastOutput = class extends PatternOutput {
376
- constructor(text, synthesis, responses, startTime, endTime) {
589
+ constructor(text, synthesis, responses, startTime, endTime, qualityReview) {
377
590
  super(text, startTime, endTime);
378
591
  this.synthesis = synthesis;
379
592
  this.responses = responses;
593
+ this.qualityReview = qualityReview;
380
594
  }
381
595
  synthesis;
382
596
  responses;
597
+ qualityReview;
383
598
  };
384
599
  var WORKER_PROMPT = `You are a {role}.
385
600
 
@@ -394,7 +609,7 @@ async function execute2(pieces, args, opts) {
394
609
  const question = build(pieces, args);
395
610
  const t0 = Date.now();
396
611
  const workerCount = opts.workers ?? 4;
397
- const roles = opts.roles ?? ROLE_SETS[workerCount] ?? ROLE_SETS[4] ?? [];
612
+ const roles = opts.roles ?? BROADCAST_ROLE_SETS[workerCount] ?? BROADCAST_ROLE_SETS[4] ?? [];
398
613
  const plannerModel = opts.plannerModel ?? opts.model;
399
614
  const workerModel = opts.workerModel ?? opts.model;
400
615
  if (!opts.quiet) {
@@ -409,11 +624,7 @@ async function execute2(pieces, args, opts) {
409
624
  const broadcastResults = await Promise.allSettled(
410
625
  roles.map(async (role) => {
411
626
  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
- });
627
+ const text = await ask(prompt, { ...opts, model: workerModel });
417
628
  return new BroadcastResponse(role, text, true);
418
629
  })
419
630
  );
@@ -438,40 +649,21 @@ ${responsesText}
438
649
 
439
650
  Synthesize a cohesive, actionable recommendation.`,
440
651
  {
652
+ ...opts,
441
653
  model: plannerModel,
442
- maxTokens: opts.maxTokens,
443
654
  thinkingLevel: "high",
444
- system: SYNTHESIS_SYSTEM
655
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM)
445
656
  }
446
657
  );
658
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
659
+ const qualityReview = await runQualityReview(question, synthesis, opts);
447
660
  const t1 = Date.now();
448
661
  const summary = responses.map(
449
662
  (wr) => `[${wr.role}]: ${wr.response.slice(0, 150)}${wr.response.length > 150 ? "..." : ""}`
450
663
  ).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;
664
+ return new BroadcastOutput(summary, synthesis, responses, t0, t1, qualityReview);
473
665
  }
474
- var \u0392 = makeBroadcast();
666
+ var \u0392 = createPatternTag(defaults2, execute2);
475
667
 
476
668
  // src/patterns/chi.ts
477
669
  var defaults3 = {
@@ -491,15 +683,17 @@ var LearningInsight = class {
491
683
  confidence;
492
684
  };
493
685
  var ChiOutput = class extends PatternOutput {
494
- constructor(text, insights, summary, suggestedChanges, startTime, endTime) {
686
+ constructor(text, insights, summary, suggestedChanges, startTime, endTime, qualityReview) {
495
687
  super(text, startTime, endTime);
496
688
  this.insights = insights;
497
689
  this.summary = summary;
498
690
  this.suggestedChanges = suggestedChanges;
691
+ this.qualityReview = qualityReview;
499
692
  }
500
693
  insights;
501
694
  summary;
502
695
  suggestedChanges;
696
+ qualityReview;
503
697
  };
504
698
  var ANALYSIS_SYSTEM = `You are an agent team performance analyst. Review a multi-agent execution and extract structured learnings.
505
699
 
@@ -568,12 +762,13 @@ async function execute3(pieces, args, opts) {
568
762
  `);
569
763
  }
570
764
  const response = await ask(input, {
765
+ ...opts,
571
766
  model: plannerModel,
572
- maxTokens: opts.maxTokens,
573
- thinkingLevel: opts.thinkingLevel,
574
- system: ANALYSIS_SYSTEM
767
+ system: mergeSystem(opts.system, ANALYSIS_SYSTEM)
575
768
  });
576
769
  const { insights, summary, suggestedChanges } = parseInsights(response);
770
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
771
+ const qualityReview = await runQualityReview(input, response, opts);
577
772
  const t1 = Date.now();
578
773
  const text = [
579
774
  `Insights: ${insights.length} extracted`,
@@ -585,30 +780,9 @@ Summary: ${summary}`,
585
780
  `
586
781
  Changes: ${suggestedChanges}`
587
782
  ].join("\n");
588
- return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1);
783
+ return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1, qualityReview);
589
784
  }
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();
785
+ var \u03A7 = createPatternTag(defaults3, execute3);
612
786
 
613
787
  // src/patterns/critique.ts
614
788
  var defaults4 = {
@@ -663,12 +837,7 @@ async function execute4(pieces, args, opts) {
663
837
  for (let r = 0; r < rounds; r++) {
664
838
  if (r === 0) {
665
839
  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
- });
840
+ currentContent = await ask(prompt, { ...opts, model: workerModel, system: opts.system });
672
841
  } else {
673
842
  if (!opts.quiet) process.stderr.write(` \u2192 Improving (round ${r + 1})...
674
843
  `);
@@ -683,21 +852,15 @@ Content to improve:
683
852
  ${currentContent}
684
853
 
685
854
  Revise the content based on the critique.`,
686
- {
687
- model: workerModel,
688
- maxTokens: opts.maxTokens,
689
- thinkingLevel: opts.thinkingLevel,
690
- system: IMPROVE_SYSTEM
691
- }
855
+ { ...opts, model: workerModel, system: mergeSystem(opts.system, IMPROVE_SYSTEM) }
692
856
  );
693
857
  }
694
858
  if (!opts.quiet) process.stderr.write(` \u2192 Critiquing (round ${r + 1})...
695
859
  `);
696
860
  const critique = await ask(currentContent, {
861
+ ...opts,
697
862
  model: plannerModel,
698
- maxTokens: opts.maxTokens,
699
- thinkingLevel: opts.thinkingLevel,
700
- system: CRITIQUE_SYSTEM
863
+ system: mergeSystem(opts.system, CRITIQUE_SYSTEM)
701
864
  });
702
865
  critiqueRounds.push(new CritiqueRound(currentContent, critique, r));
703
866
  }
@@ -710,28 +873,7 @@ Critique: ${cr.critique.slice(0, 200)}${cr.critique.length > 200 ? "..." : ""}`
710
873
  ).join("\n\n");
711
874
  return new CritiqueOutput(summary, finalContent, critiqueRounds, t0, t1);
712
875
  }
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();
876
+ var \u03A8 = createPatternTag(defaults4, execute4);
735
877
 
736
878
  // src/patterns/debate.ts
737
879
  var defaults5 = {
@@ -740,30 +882,6 @@ var defaults5 = {
740
882
  perspectives: 3,
741
883
  rounds: 1
742
884
  };
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
885
  var DebatePerspective = class {
768
886
  constructor(role, argument, round = 1) {
769
887
  this.role = role;
@@ -775,15 +893,17 @@ var DebatePerspective = class {
775
893
  round;
776
894
  };
777
895
  var DebateOutput = class extends PatternOutput {
778
- constructor(text, conclusion, perspectives, rounds, startTime, endTime) {
896
+ constructor(text, conclusion, perspectives, rounds, startTime, endTime, qualityReview) {
779
897
  super(text, startTime, endTime);
780
898
  this.conclusion = conclusion;
781
899
  this.perspectives = perspectives;
782
900
  this.rounds = rounds;
901
+ this.qualityReview = qualityReview;
783
902
  }
784
903
  conclusion;
785
904
  perspectives;
786
905
  rounds;
906
+ qualityReview;
787
907
  };
788
908
  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
909
  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.`;
@@ -793,7 +913,7 @@ async function execute5(pieces, args, opts) {
793
913
  const t0 = Date.now();
794
914
  const count = opts.perspectives ?? 3;
795
915
  const totalRounds = opts.rounds ?? 1;
796
- const roles = opts.roles ?? ROLE_SETS2[count] ?? ROLE_SETS2[3] ?? [];
916
+ const roles = opts.roles ?? DEBATE_ROLE_SETS[count] ?? DEBATE_ROLE_SETS[3] ?? [];
797
917
  const plannerModel = opts.plannerModel ?? opts.model;
798
918
  const workerModel = opts.workerModel ?? opts.model;
799
919
  if (!opts.quiet) {
@@ -811,10 +931,9 @@ async function execute5(pieces, args, opts) {
811
931
  const round1Results = await Promise.allSettled(
812
932
  roles.map(
813
933
  (role) => ask(question, {
934
+ ...opts,
814
935
  model: workerModel,
815
- maxTokens: opts.maxTokens,
816
- thinkingLevel: opts.thinkingLevel,
817
- system: PERSPECTIVE_SYSTEM(role)
936
+ system: mergeSystem(opts.system, PERSPECTIVE_SYSTEM(role))
818
937
  }).then((text) => new DebatePerspective(role, text, 1))
819
938
  )
820
939
  );
@@ -848,10 +967,9 @@ ${othersText}
848
967
 
849
968
  Refine your position. Address the counter-arguments directly. Strengthen your argument with rebuttals.`;
850
969
  return ask(prompt, {
970
+ ...opts,
851
971
  model: workerModel,
852
- maxTokens: opts.maxTokens,
853
- thinkingLevel: opts.thinkingLevel,
854
- system: REBUTTAL_SYSTEM(role)
972
+ system: mergeSystem(opts.system, REBUTTAL_SYSTEM(role))
855
973
  }).then((text) => new DebatePerspective(role, text, round));
856
974
  })
857
975
  );
@@ -875,37 +993,31 @@ Refine your position. Address the counter-arguments directly. Strengthen your ar
875
993
 
876
994
  Synthesize a balanced conclusion from the full debate above. Weigh the evidence from all rounds.`,
877
995
  {
996
+ ...opts,
878
997
  model: plannerModel,
879
- maxTokens: opts.maxTokens,
880
998
  thinkingLevel: "high",
881
- system: SYNTHESIS_SYSTEM2
999
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM2)
882
1000
  }
883
1001
  );
1002
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1003
+ const qualityReview = await runQualityReview(question, conclusion, opts);
884
1004
  const t1 = Date.now();
885
- return new DebateOutput(conclusion, conclusion, allPerspectives, totalRounds, t0, t1);
1005
+ return new DebateOutput(
1006
+ conclusion,
1007
+ conclusion,
1008
+ allPerspectives,
1009
+ totalRounds,
1010
+ t0,
1011
+ t1,
1012
+ qualityReview
1013
+ );
886
1014
  }
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;
1015
+ var \u0394 = createPatternTag(defaults5, execute5);
1016
+
1017
+ // src/utils.ts
1018
+ function getErrorMessage(err) {
1019
+ return err instanceof Error ? err.message : String(err);
907
1020
  }
908
- var \u0394 = makeDebate();
909
1021
 
910
1022
  // src/patterns/fleet.ts
911
1023
  var defaults6 = {
@@ -926,11 +1038,13 @@ var FleetMemberOutput = class {
926
1038
  error;
927
1039
  };
928
1040
  var FleetOutput = class extends PatternOutput {
929
- constructor(text, members, startTime, endTime) {
1041
+ constructor(text, members, startTime, endTime, qualityReview) {
930
1042
  super(text, startTime, endTime);
931
1043
  this.members = members;
1044
+ this.qualityReview = qualityReview;
932
1045
  }
933
1046
  members;
1047
+ qualityReview;
934
1048
  /** Number of successful members */
935
1049
  get successCount() {
936
1050
  return this.members.filter((m) => m.success).length;
@@ -950,20 +1064,30 @@ function parseTasks(template, explicitTasks) {
950
1064
  if (lines.length > 1) return lines;
951
1065
  return [template];
952
1066
  }
1067
+ function describeTask(task) {
1068
+ if (typeof task === "function") return "(composed pattern)";
1069
+ return task;
1070
+ }
953
1071
  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
1072
  async function executeTask(task, opts, workerModel) {
1073
+ if (typeof task === "function") {
1074
+ try {
1075
+ const text = await task("");
1076
+ return new FleetMemberOutput("(composed pattern)", text, true);
1077
+ } catch (err) {
1078
+ return new FleetMemberOutput("(composed pattern)", "", false, getErrorMessage(err));
1079
+ }
1080
+ }
955
1081
  const model = workerModel ?? opts.model;
956
1082
  try {
957
1083
  const text = await ask(task, {
1084
+ ...opts,
958
1085
  model,
959
- maxTokens: opts.maxTokens,
960
- thinkingLevel: opts.thinkingLevel,
961
- system: opts.system ?? FLEET_SYSTEM
1086
+ system: mergeSystem(opts.system, FLEET_SYSTEM)
962
1087
  });
963
1088
  return new FleetMemberOutput(task, text, true);
964
1089
  } catch (err) {
965
- const msg = err instanceof Error ? err.message : String(err);
966
- return new FleetMemberOutput(task, "", false, msg);
1090
+ return new FleetMemberOutput(task, "", false, getErrorMessage(err));
967
1091
  }
968
1092
  }
969
1093
  async function execute6(pieces, args, opts) {
@@ -975,11 +1099,16 @@ async function execute6(pieces, args, opts) {
975
1099
  process.stderr.write(`\u03A6: Fleet executing ${tasks.length} task(s) in parallel
976
1100
  `);
977
1101
  for (let i = 0; i < tasks.length; i++) {
978
- const t = tasks[i];
1102
+ const t = describeTask(tasks[i]);
979
1103
  process.stderr.write(` [${i + 1}] ${t.slice(0, 60)}${t.length > 60 ? "..." : ""}
980
1104
  `);
981
1105
  }
982
1106
  }
1107
+ const taskSummary = `Execute ${tasks.length} fleet task(s)?
1108
+ ${tasks.map((t, i) => `${i + 1}. ${describeTask(t).slice(0, 80)}`).join("\n ")}`;
1109
+ if (!await confirmPhase(taskSummary, opts)) {
1110
+ throw new Error("pizx/\u03A6: Execution cancelled by user.");
1111
+ }
983
1112
  const results = [];
984
1113
  const concurrency = opts.concurrency ?? 5;
985
1114
  for (let i = 0; i < tasks.length; i += concurrency) {
@@ -991,7 +1120,9 @@ async function execute6(pieces, args, opts) {
991
1120
  if (r.status === "fulfilled") {
992
1121
  results.push(r.value);
993
1122
  } else {
994
- results.push(new FleetMemberOutput(batch[idx], "", false, r.reason?.toString()));
1123
+ results.push(
1124
+ new FleetMemberOutput(describeTask(batch[idx]), "", false, r.reason?.toString())
1125
+ );
995
1126
  }
996
1127
  });
997
1128
  }
@@ -1003,30 +1134,11 @@ async function execute6(pieces, args, opts) {
1003
1134
  const header = `Fleet Results: ${results.filter((r) => r.success).length}/${results.length} succeeded
1004
1135
 
1005
1136
  `;
1006
- return new FleetOutput(header + summary, results, t0, t1);
1137
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1138
+ const qualityReview = await runQualityReview(template, header + summary, opts);
1139
+ return new FleetOutput(header + summary, results, t0, t1, qualityReview);
1007
1140
  }
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();
1141
+ var \u03A6 = createPatternTag(defaults6, execute6);
1030
1142
 
1031
1143
  // src/patterns/graph.ts
1032
1144
  var defaults7 = {
@@ -1046,13 +1158,15 @@ var GraphNodeResult = class {
1046
1158
  success;
1047
1159
  };
1048
1160
  var GraphOutput = class extends PatternOutput {
1049
- constructor(text, finalOutput, nodeResults, startTime, endTime) {
1161
+ constructor(text, finalOutput, nodeResults, startTime, endTime, qualityReview) {
1050
1162
  super(text, startTime, endTime);
1051
1163
  this.finalOutput = finalOutput;
1052
1164
  this.nodeResults = nodeResults;
1165
+ this.qualityReview = qualityReview;
1053
1166
  }
1054
1167
  finalOutput;
1055
1168
  nodeResults;
1169
+ qualityReview;
1056
1170
  };
1057
1171
  function parseGraph(template, separator) {
1058
1172
  const sep = separator ?? "\u2192";
@@ -1148,10 +1262,9 @@ Your task: ${node.task}`;
1148
1262
  }
1149
1263
  }
1150
1264
  const text = await ask(context, {
1265
+ ...opts,
1151
1266
  model: workerModel,
1152
- maxTokens: opts.maxTokens,
1153
- thinkingLevel: opts.thinkingLevel,
1154
- system: NODE_SYSTEM
1267
+ system: mergeSystem(opts.system, NODE_SYSTEM)
1155
1268
  });
1156
1269
  return { nodeId, task: node.task, text, success: true };
1157
1270
  })
@@ -1169,34 +1282,15 @@ Your task: ${node.task}`;
1169
1282
  const lastBatch = batches[batches.length - 1] ?? [];
1170
1283
  const finalNodeResults = lastBatch.map((id) => results.get(id)).filter(Boolean);
1171
1284
  const finalOutput = finalNodeResults.length > 0 ? finalNodeResults.join("\n\n") : "";
1285
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1286
+ const qualityReview = await runQualityReview(template, finalOutput, opts);
1172
1287
  const summary = nodeResults.map(
1173
1288
  (nr) => `[${nr.nodeId}] ${nr.task.slice(0, 80)}...
1174
1289
  ${nr.output.slice(0, 200)}${nr.output.length > 200 ? "..." : ""}`
1175
1290
  ).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;
1291
+ return new GraphOutput(summary, finalOutput, nodeResults, t0, t1, qualityReview);
1198
1292
  }
1199
- var \u0393 = makeGraph();
1293
+ var \u0393 = createPatternTag(defaults7, execute7);
1200
1294
 
1201
1295
  // src/patterns/memory.ts
1202
1296
  var defaults8 = {
@@ -1205,27 +1299,6 @@ var defaults8 = {
1205
1299
  agents: 3,
1206
1300
  rounds: 1
1207
1301
  };
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
1302
  var MemoryEntry = class {
1230
1303
  constructor(role, round, content) {
1231
1304
  this.role = role;
@@ -1237,13 +1310,15 @@ var MemoryEntry = class {
1237
1310
  content;
1238
1311
  };
1239
1312
  var MemoryOutput = class extends PatternOutput {
1240
- constructor(text, synthesis, entries, startTime, endTime) {
1313
+ constructor(text, synthesis, entries, startTime, endTime, qualityReview) {
1241
1314
  super(text, startTime, endTime);
1242
1315
  this.synthesis = synthesis;
1243
1316
  this.entries = entries;
1317
+ this.qualityReview = qualityReview;
1244
1318
  }
1245
1319
  synthesis;
1246
1320
  entries;
1321
+ qualityReview;
1247
1322
  };
1248
1323
  var WRITER_PROMPT = `You are a specialist with role: {role}.
1249
1324
 
@@ -1264,7 +1339,7 @@ async function execute8(pieces, args, opts) {
1264
1339
  const t0 = Date.now();
1265
1340
  const agentCount = opts.agents ?? 3;
1266
1341
  const totalRounds = opts.rounds ?? 1;
1267
- const roles = opts.roles ?? ROLE_SETS3[agentCount] ?? ROLE_SETS3[3] ?? [];
1342
+ const roles = opts.roles ?? MEMORY_ROLE_SETS[agentCount] ?? MEMORY_ROLE_SETS[3] ?? [];
1268
1343
  const plannerModel = opts.plannerModel ?? opts.model;
1269
1344
  const workerModel = opts.workerModel ?? opts.model;
1270
1345
  if (!opts.quiet) {
@@ -1281,11 +1356,7 @@ async function execute8(pieces, args, opts) {
1281
1356
  const roundResults = await Promise.allSettled(
1282
1357
  roles.map(async (role) => {
1283
1358
  const prompt = buildWriterPrompt(role, topic, blackboard);
1284
- const text = await ask(prompt, {
1285
- model: workerModel,
1286
- maxTokens: opts.maxTokens,
1287
- thinkingLevel: opts.thinkingLevel
1288
- });
1359
+ const text = await ask(prompt, { ...opts, model: workerModel });
1289
1360
  return { role, text };
1290
1361
  })
1291
1362
  );
@@ -1307,40 +1378,21 @@ ${blackboard}
1307
1378
 
1308
1379
  Consolidate into a comprehensive, structured synthesis.`,
1309
1380
  {
1381
+ ...opts,
1310
1382
  model: plannerModel,
1311
- maxTokens: opts.maxTokens,
1312
1383
  thinkingLevel: "high",
1313
- system: CONSOLIDATOR_SYSTEM
1384
+ system: mergeSystem(opts.system, CONSOLIDATOR_SYSTEM)
1314
1385
  }
1315
1386
  );
1387
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1388
+ const qualityReview = await runQualityReview(topic, synthesis, opts);
1316
1389
  const t1 = Date.now();
1317
1390
  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;
1391
+ (e) => `[${e.role}] Round ${e.round}: ${e.content.slice(0, 150)}${e.content.length > 150 ? "..." : ""}`
1392
+ ).join("\n");
1393
+ return new MemoryOutput(summary, synthesis, entries, t0, t1, qualityReview);
1342
1394
  }
1343
- var \u039C = makeMemory();
1395
+ var \u039C = createPatternTag(defaults8, execute8);
1344
1396
 
1345
1397
  // src/patterns/nu.ts
1346
1398
  var defaults9 = {
@@ -1360,19 +1412,21 @@ var NuRole = class {
1360
1412
  goal;
1361
1413
  };
1362
1414
  var NuOutput = class extends PatternOutput {
1363
- constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime) {
1415
+ constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime, qualityReview) {
1364
1416
  super(text, startTime, endTime);
1365
1417
  this.negotiatedRoles = negotiatedRoles;
1366
1418
  this.workflow = workflow;
1367
1419
  this.workflowReasoning = workflowReasoning;
1368
1420
  this.roleResults = roleResults;
1369
1421
  this.synthesis = synthesis;
1422
+ this.qualityReview = qualityReview;
1370
1423
  }
1371
1424
  negotiatedRoles;
1372
1425
  workflow;
1373
1426
  workflowReasoning;
1374
1427
  roleResults;
1375
1428
  synthesis;
1429
+ qualityReview;
1376
1430
  };
1377
1431
  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
1432
 
@@ -1410,6 +1464,7 @@ async function negotiateRoles(task, opts) {
1410
1464
  const response = await ask(`Task: ${task}
1411
1465
 
1412
1466
  ${prompt}`, {
1467
+ ...opts,
1413
1468
  model: opts.plannerModel ?? opts.model,
1414
1469
  maxTokens: 2048,
1415
1470
  thinkingLevel: "high"
@@ -1446,10 +1501,11 @@ ${rolesText}
1446
1501
 
1447
1502
  Determine the best execution strategy.`,
1448
1503
  {
1504
+ ...opts,
1449
1505
  model: opts.plannerModel ?? opts.model,
1450
1506
  maxTokens: 512,
1451
1507
  thinkingLevel: "high",
1452
- system: WORKFLOW_SYSTEM
1508
+ system: mergeSystem(opts.system, WORKFLOW_SYSTEM)
1453
1509
  }
1454
1510
  );
1455
1511
  const wfMatch = response.match(/WORKFLOW\s*:\s*(.+)/i);
@@ -1468,10 +1524,9 @@ async function executeRoles(roles, task, workflow, opts) {
1468
1524
  let context = task;
1469
1525
  for (const role of roles) {
1470
1526
  const output = await ask(context, {
1527
+ ...opts,
1471
1528
  model: workerModel,
1472
- maxTokens: opts.maxTokens,
1473
- thinkingLevel: opts.thinkingLevel,
1474
- system: EXECUTE_SYSTEM2(role)
1529
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
1475
1530
  });
1476
1531
  results.push({ role: role.name, output });
1477
1532
  context = `Previous output from ${role.name}:
@@ -1483,10 +1538,9 @@ Continue with: ${task}`;
1483
1538
  const parallelResults = await Promise.allSettled(
1484
1539
  roles.map(
1485
1540
  (role) => ask(task, {
1541
+ ...opts,
1486
1542
  model: workerModel,
1487
- maxTokens: opts.maxTokens,
1488
- thinkingLevel: opts.thinkingLevel,
1489
- system: EXECUTE_SYSTEM2(role)
1543
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
1490
1544
  }).then((text) => ({ role: role.name, output: text })).catch((err) => ({ role: role.name, output: `(failed: ${String(err)})` }))
1491
1545
  )
1492
1546
  );
@@ -1508,10 +1562,10 @@ ${resultsText}
1508
1562
 
1509
1563
  Synthesize a comprehensive final answer.`,
1510
1564
  {
1565
+ ...opts,
1511
1566
  model: opts.plannerModel ?? opts.model,
1512
- maxTokens: opts.maxTokens,
1513
1567
  thinkingLevel: "high",
1514
- system: SYNTHESIS_SYSTEM3
1568
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM3)
1515
1569
  }
1516
1570
  );
1517
1571
  }
@@ -1552,6 +1606,8 @@ async function execute9(pieces, args, opts) {
1552
1606
  const roleResults = await executeRoles(roles, task, workflow, opts);
1553
1607
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing...\n");
1554
1608
  const synthesis = await synthesize(task, roleResults, { ...opts, plannerModel });
1609
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1610
+ const qualityReview = await runQualityReview(task, synthesis, opts);
1555
1611
  const t1 = Date.now();
1556
1612
  const summary = [
1557
1613
  `Roles: ${roles.map((r) => r.name).join(", ")}`,
@@ -1559,30 +1615,19 @@ async function execute9(pieces, args, opts) {
1559
1615
  `Results: ${roleResults.length}/${roles.length} succeeded`,
1560
1616
  `Synthesis: ${synthesis}`
1561
1617
  ].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;
1618
+ return new NuOutput(
1619
+ summary,
1620
+ roles,
1621
+ workflow,
1622
+ reasoning,
1623
+ roleResults,
1624
+ synthesis,
1625
+ t0,
1626
+ t1,
1627
+ qualityReview
1628
+ );
1584
1629
  }
1585
- var \u039D = makeNu();
1630
+ var \u039D = createPatternTag(defaults9, execute9);
1586
1631
 
1587
1632
  // src/patterns/orchestrator.ts
1588
1633
  var defaults10 = {
@@ -1602,15 +1647,17 @@ var OrchestratorWorkerResult = class {
1602
1647
  success;
1603
1648
  };
1604
1649
  var OrchestratorOutput = class extends PatternOutput {
1605
- constructor(text, plan, synthesis, workerResults, startTime, endTime) {
1650
+ constructor(text, plan, synthesis, workerResults, startTime, endTime, qualityReview) {
1606
1651
  super(text, startTime, endTime);
1607
1652
  this.plan = plan;
1608
1653
  this.synthesis = synthesis;
1609
1654
  this.workerResults = workerResults;
1655
+ this.qualityReview = qualityReview;
1610
1656
  }
1611
1657
  plan;
1612
1658
  synthesis;
1613
1659
  workerResults;
1660
+ qualityReview;
1614
1661
  };
1615
1662
  var PLANNER_SYSTEM = `You are a senior architect and project planner. Given a high-level request, create a detailed execution plan.
1616
1663
 
@@ -1633,6 +1680,7 @@ async function execute10(pieces, args, opts) {
1633
1680
  const request = build(pieces, args);
1634
1681
  const t0 = Date.now();
1635
1682
  const workerCount = opts.workers ?? 3;
1683
+ const phases = [];
1636
1684
  const plannerModel = opts.plannerModel ?? opts.model;
1637
1685
  const workerModel = opts.workerModel ?? opts.model;
1638
1686
  if (!opts.quiet) {
@@ -1642,11 +1690,18 @@ async function execute10(pieces, args, opts) {
1642
1690
  );
1643
1691
  }
1644
1692
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
1693
+ const planStart = Date.now();
1645
1694
  const planText = await ask(request, {
1695
+ ...opts,
1646
1696
  model: plannerModel,
1647
- maxTokens: opts.maxTokens,
1648
1697
  thinkingLevel: "high",
1649
- system: PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount))
1698
+ system: mergeSystem(opts.system, PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount)))
1699
+ });
1700
+ phases.push({
1701
+ phase: "plan",
1702
+ durationMs: Date.now() - planStart,
1703
+ description: `Generated plan with ${workerCount} workers`,
1704
+ modelUsed: plannerModel
1650
1705
  });
1651
1706
  const subTasks = [];
1652
1707
  const taskLines = planText.split("\n");
@@ -1670,27 +1725,37 @@ async function execute10(pieces, args, opts) {
1670
1725
  `);
1671
1726
  }
1672
1727
  }
1728
+ const planSummary = tasks.length > 0 ? `Execute ${tasks.length} sub-task(s) as planned?
1729
+ ${tasks.map((t, i) => `${i + 1}. ${t.slice(0, 80)}`).join("\n ")}` : `Execute the plan?`;
1730
+ if (!await confirmPhase(planSummary, opts)) {
1731
+ throw new Error("pizx/\u03A9: Execution cancelled by user.");
1732
+ }
1673
1733
  const workerResults = [];
1674
1734
  const concurrency = opts.concurrency ?? 3;
1735
+ const dispatchStart = Date.now();
1675
1736
  for (let i = 0; i < tasks.length; i += concurrency) {
1676
1737
  const batch = tasks.slice(i, i + concurrency);
1677
1738
  const batchResults = await Promise.allSettled(
1678
1739
  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))
1740
+ (task) => ask(task, { ...opts, model: workerModel, system: mergeSystem(opts.system, WORKER_SYSTEM) }).then((text) => new OrchestratorWorkerResult(task, text, true)).catch((err) => new OrchestratorWorkerResult(task, String(err), false))
1685
1741
  )
1686
1742
  );
1687
1743
  batchResults.forEach((r) => {
1688
1744
  if (r.status === "fulfilled") workerResults.push(r.value);
1689
1745
  });
1690
1746
  }
1747
+ const succeeded = workerResults.filter((w) => w.success).length;
1748
+ phases.push({
1749
+ phase: "dispatch",
1750
+ durationMs: Date.now() - dispatchStart,
1751
+ description: `Executed ${workerResults.length} worker(s), ${succeeded} succeeded`,
1752
+ modelUsed: workerModel,
1753
+ callCount: workerResults.length
1754
+ });
1691
1755
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
1692
1756
  const workerTexts = workerResults.map((wr, i) => `Task ${i + 1}: ${wr.task}
1693
1757
  Result: ${wr.output}`).join("\n\n");
1758
+ const synthStart = Date.now();
1694
1759
  const synthesis = await ask(
1695
1760
  `Original request:
1696
1761
  ${request}
@@ -1703,46 +1768,60 @@ ${workerTexts}
1703
1768
 
1704
1769
  Synthesize a final deliverable.`,
1705
1770
  {
1771
+ ...opts,
1706
1772
  model: plannerModel,
1707
- maxTokens: opts.maxTokens,
1708
1773
  thinkingLevel: "high",
1709
- system: SYNTHESIS_SYSTEM4
1774
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM4)
1710
1775
  }
1711
1776
  );
1777
+ phases.push({
1778
+ phase: "synthesize",
1779
+ durationMs: Date.now() - synthStart,
1780
+ description: "Synthesized worker results into final deliverable",
1781
+ modelUsed: plannerModel
1782
+ });
1783
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1784
+ const qualityStart = Date.now();
1785
+ const qualityReview = await runQualityReview(request, synthesis, opts);
1786
+ if (qualityReview) {
1787
+ phases.push({
1788
+ phase: "quality-review",
1789
+ durationMs: Date.now() - qualityStart,
1790
+ description: `Score: ${qualityReview.score.toFixed(2)} \u2014 ${qualityReview.assessment.slice(0, 60)}`,
1791
+ modelUsed: plannerModel
1792
+ });
1793
+ }
1712
1794
  const t1 = Date.now();
1795
+ const reviewSection = qualityReview ? `
1796
+
1797
+ Quality Review: ${qualityReview.score.toFixed(2)} \u2014 ${qualityReview.assessment}
1798
+ Recommendation: ${qualityReview.recommendation}` : "";
1713
1799
  const summary = `Plan:
1714
1800
  ${planText}
1715
1801
 
1716
1802
  Workers: ${workerResults.filter((w) => w.success).length}/${workerResults.length} succeeded
1717
1803
 
1718
1804
  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;
1805
+ ${synthesis}${reviewSection}`;
1806
+ const output = new OrchestratorOutput(
1807
+ summary,
1808
+ planText,
1809
+ synthesis,
1810
+ workerResults,
1811
+ t0,
1812
+ t1,
1813
+ qualityReview
1814
+ );
1815
+ output.phaseLog = phases;
1816
+ return output;
1742
1817
  }
1743
- var \u03A9 = makeOrchestrator();
1818
+ var \u03A9 = createPatternTag(defaults10, execute10);
1744
1819
 
1745
1820
  // src/patterns/pipeline.ts
1821
+ function describeStage(stage) {
1822
+ if (typeof stage === "function") return "(composed pattern)";
1823
+ return stage;
1824
+ }
1746
1825
  var defaults11 = {
1747
1826
  maxTokens: 4096,
1748
1827
  thinkingLevel: "medium"
@@ -1758,13 +1837,15 @@ var PipelineStageResult = class {
1758
1837
  index;
1759
1838
  };
1760
1839
  var PipelineOutput = class extends PatternOutput {
1761
- constructor(text, finalOutput, stages, startTime, endTime) {
1840
+ constructor(text, finalOutput, stages, startTime, endTime, qualityReview) {
1762
1841
  super(text, startTime, endTime);
1763
1842
  this.finalOutput = finalOutput;
1764
1843
  this.stages = stages;
1844
+ this.qualityReview = qualityReview;
1765
1845
  }
1766
1846
  finalOutput;
1767
1847
  stages;
1848
+ qualityReview;
1768
1849
  };
1769
1850
  function parseStages(template, explicitStages, separator) {
1770
1851
  if (explicitStages && explicitStages.length > 0) return explicitStages;
@@ -1806,55 +1887,46 @@ async function execute11(pieces, args, opts) {
1806
1887
  `);
1807
1888
  }
1808
1889
  }
1890
+ const stageSummary = `Run ${stages.length} pipeline stage(s)?
1891
+ ${stages.map((s, i) => `${i + 1}. ${describeStage(s)}`).join("\n ")}`;
1892
+ if (!await confirmPhase(stageSummary, opts)) {
1893
+ throw new Error("pizx/\u039B: Execution cancelled by user.");
1894
+ }
1809
1895
  const stageResults = [];
1810
1896
  let currentInput = "";
1811
1897
  for (let i = 0; i < stages.length; i++) {
1812
1898
  const stage = stages[i];
1899
+ const stageLabel = describeStage(stage);
1813
1900
  const customPrompt = opts.stagePrompts?.[i];
1814
1901
  if (!opts.quiet)
1815
- process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stage.slice(0, 50)}...
1902
+ process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stageLabel.slice(0, 50)}...
1816
1903
  `);
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));
1904
+ let output;
1905
+ if (typeof stage === "function") {
1906
+ output = await stage(currentInput);
1907
+ } else {
1908
+ const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
1909
+ 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.`;
1910
+ output = await ask(prompt, {
1911
+ ...opts,
1912
+ model: workerModel,
1913
+ system: mergeSystem(opts.system, systemMessage)
1914
+ });
1915
+ }
1916
+ stageResults.push(new PipelineStageResult(stageLabel, output, i));
1826
1917
  currentInput = output;
1827
1918
  }
1828
1919
  const t1 = Date.now();
1829
1920
  const finalOutput = currentInput;
1921
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1922
+ const qualityReview = await runQualityReview(template, finalOutput, opts);
1830
1923
  const summary = stageResults.map(
1831
1924
  (sr) => `Stage ${sr.index + 1} (${sr.stage}):
1832
1925
  ${sr.output.slice(0, 200)}${sr.output.length > 200 ? "..." : ""}`
1833
1926
  ).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;
1927
+ return new PipelineOutput(summary, finalOutput, stageResults, t0, t1, qualityReview);
1856
1928
  }
1857
- var \u039B = makePipeline();
1929
+ var \u039B = createPatternTag(defaults11, execute11);
1858
1930
 
1859
1931
  // src/patterns/ralph.ts
1860
1932
  import { createAgentSession } from "@earendil-works/pi-coding-agent";
@@ -1889,17 +1961,17 @@ async function executeWithTools(goal, opts) {
1889
1961
  });
1890
1962
  try {
1891
1963
  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
- }
1964
+ const messages = session.messages;
1965
+ for (let i = messages.length - 1; i >= 0; i--) {
1966
+ const msg = messages[i];
1967
+ if (msg?.role !== "assistant") continue;
1968
+ const c = "content" in msg ? msg.content : void 0;
1969
+ if (typeof c === "string") return c.trim();
1970
+ if (Array.isArray(c)) {
1971
+ const texts = c.filter(
1972
+ (x) => x.type === "text" && typeof x.text === "string"
1973
+ ).map((x) => x.text);
1974
+ if (texts.length > 0) return texts.join("").trim();
1903
1975
  }
1904
1976
  }
1905
1977
  return "(no assistant response)";
@@ -1934,10 +2006,9 @@ async function execute12(pieces, args, opts) {
1934
2006
  }
1935
2007
  if (!opts.quiet) process.stderr.write(" \u2192 Analyzing...\n");
1936
2008
  const analysis = await ask(currentGoal, {
2009
+ ...opts,
1937
2010
  model: plannerModel,
1938
- maxTokens: opts.maxTokens,
1939
- thinkingLevel: opts.thinkingLevel,
1940
- system: ANALYSIS_SYSTEM2
2011
+ system: mergeSystem(opts.system, ANALYSIS_SYSTEM2)
1941
2012
  });
1942
2013
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
1943
2014
  const plan = await ask(
@@ -1946,12 +2017,7 @@ async function execute12(pieces, args, opts) {
1946
2017
  Analysis: ${analysis}
1947
2018
 
1948
2019
  Generate an implementation plan.`,
1949
- {
1950
- model: plannerModel,
1951
- maxTokens: opts.maxTokens,
1952
- thinkingLevel: opts.thinkingLevel,
1953
- system: PLAN_SYSTEM2
1954
- }
2020
+ { ...opts, model: plannerModel, system: mergeSystem(opts.system, PLAN_SYSTEM2) }
1955
2021
  );
1956
2022
  if (!opts.quiet) process.stderr.write(" \u2192 Executing...\n");
1957
2023
  const result = opts.useTools ? await executeWithTools(`Implement this plan:
@@ -1964,9 +2030,8 @@ Goal: ${currentGoal}`, {
1964
2030
  ${plan}
1965
2031
 
1966
2032
  Goal: ${currentGoal}`, {
1967
- model: workerModel,
1968
- maxTokens: opts.maxTokens,
1969
- thinkingLevel: opts.thinkingLevel
2033
+ ...opts,
2034
+ model: workerModel
1970
2035
  });
1971
2036
  if (!opts.quiet) process.stderr.write(" \u2192 Reviewing...\n");
1972
2037
  const review = await ask(`Plan:
@@ -1976,10 +2041,11 @@ Result:
1976
2041
  ${result}
1977
2042
 
1978
2043
  Review the implementation.`, {
2044
+ ...opts,
1979
2045
  model: plannerModel,
1980
2046
  maxTokens: 1024,
1981
2047
  thinkingLevel: "high",
1982
- system: REVIEW_SYSTEM
2048
+ system: mergeSystem(opts.system, REVIEW_SYSTEM)
1983
2049
  });
1984
2050
  const shouldContinue = review.includes("ITERATE") && !review.includes("DONE");
1985
2051
  iterations.push({
@@ -2015,28 +2081,7 @@ Original goal: ${goal}`;
2015
2081
  t1
2016
2082
  );
2017
2083
  }
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();
2084
+ var \u03A1 = createPatternTag(defaults12, execute12);
2040
2085
 
2041
2086
  // src/patterns/subagent.ts
2042
2087
  var defaults13 = {
@@ -2056,13 +2101,15 @@ var SubagentResult = class {
2056
2101
  success;
2057
2102
  };
2058
2103
  var SubagentOutput = class extends PatternOutput {
2059
- constructor(text, synthesis, subResults, startTime, endTime) {
2104
+ constructor(text, synthesis, subResults, startTime, endTime, qualityReview) {
2060
2105
  super(text, startTime, endTime);
2061
2106
  this.synthesis = synthesis;
2062
2107
  this.subResults = subResults;
2108
+ this.qualityReview = qualityReview;
2063
2109
  }
2064
2110
  synthesis;
2065
2111
  subResults;
2112
+ qualityReview;
2066
2113
  };
2067
2114
  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
2115
  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.`;
@@ -2075,10 +2122,11 @@ ${task}
2075
2122
 
2076
2123
  Output a JSON array of strings.`,
2077
2124
  {
2125
+ ...opts,
2078
2126
  model: opts.model,
2079
2127
  maxTokens: 1024,
2080
2128
  thinkingLevel: "medium",
2081
- system: DECOMPOSE_SYSTEM
2129
+ system: mergeSystem(opts.system, DECOMPOSE_SYSTEM)
2082
2130
  }
2083
2131
  );
2084
2132
  try {
@@ -2099,6 +2147,7 @@ var SUBAGENT_SYSTEM = `You are a domain specialist. Complete your assigned sub-t
2099
2147
  async function execute13(pieces, args, opts) {
2100
2148
  const task = build(pieces, args);
2101
2149
  const t0 = Date.now();
2150
+ const phases = [];
2102
2151
  const plannerModel = opts.plannerModel ?? opts.model;
2103
2152
  const workerModel = opts.workerModel ?? opts.model;
2104
2153
  if (!opts.quiet) {
@@ -2108,7 +2157,14 @@ async function execute13(pieces, args, opts) {
2108
2157
  );
2109
2158
  }
2110
2159
  if (!opts.quiet) process.stderr.write(" \u2192 Decomposing task into sub-tasks...\n");
2160
+ const decomposeStart = Date.now();
2111
2161
  const subTasks = await decomposeTask(task, { ...opts, model: plannerModel });
2162
+ phases.push({
2163
+ phase: "decompose",
2164
+ durationMs: Date.now() - decomposeStart,
2165
+ description: `Decomposed into ${subTasks.length} sub-task(s)`,
2166
+ modelUsed: plannerModel
2167
+ });
2112
2168
  if (!opts.quiet) {
2113
2169
  process.stderr.write(` \u2192 ${subTasks.length} sub-task(s) identified:
2114
2170
  `);
@@ -2118,27 +2174,37 @@ async function execute13(pieces, args, opts) {
2118
2174
  `);
2119
2175
  }
2120
2176
  }
2177
+ const subTaskSummary = `Execute ${subTasks.length} sub-task(s)?
2178
+ ${subTasks.map((st, i) => `${i + 1}. ${st.slice(0, 80)}`).join("\n ")}`;
2179
+ if (!await confirmPhase(subTaskSummary, opts)) {
2180
+ throw new Error("pizx/\u03A3: Execution cancelled by user.");
2181
+ }
2121
2182
  const subResults = [];
2122
2183
  const concurrency = opts.concurrency ?? 4;
2184
+ const execStart = Date.now();
2123
2185
  for (let i = 0; i < subTasks.length; i += concurrency) {
2124
2186
  const batch = subTasks.slice(i, i + concurrency);
2125
2187
  const batchResults = await Promise.allSettled(
2126
2188
  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))
2189
+ (st) => ask(st, { ...opts, model: workerModel, system: mergeSystem(opts.system, SUBAGENT_SYSTEM) }).then((text) => new SubagentResult(st, text, true)).catch((err) => new SubagentResult(st, String(err), false))
2133
2190
  )
2134
2191
  );
2135
2192
  batchResults.forEach((r) => {
2136
2193
  if (r.status === "fulfilled") subResults.push(r.value);
2137
2194
  });
2138
2195
  }
2196
+ const succeeded = subResults.filter((r) => r.success).length;
2197
+ phases.push({
2198
+ phase: "execute",
2199
+ durationMs: Date.now() - execStart,
2200
+ description: `Executed ${subResults.length} sub-task(s), ${succeeded} succeeded`,
2201
+ modelUsed: workerModel,
2202
+ callCount: subResults.length
2203
+ });
2139
2204
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
2140
2205
  const subResultsText = subResults.map((sr, i) => `Sub-task ${i + 1}: ${sr.subTask}
2141
2206
  Result: ${sr.text}`).join("\n\n");
2207
+ const synthStart = Date.now();
2142
2208
  const synthesis = await ask(
2143
2209
  `Original task:
2144
2210
  ${task}
@@ -2147,38 +2213,31 @@ Sub-task results:
2147
2213
  ${subResultsText}
2148
2214
 
2149
2215
  Synthesize a comprehensive answer.`,
2150
- {
2151
- model: plannerModel,
2152
- maxTokens: opts.maxTokens,
2153
- thinkingLevel: opts.thinkingLevel,
2154
- system: SYNTHESIS_SYSTEM5
2155
- }
2216
+ { ...opts, model: plannerModel, system: mergeSystem(opts.system, SYNTHESIS_SYSTEM5) }
2156
2217
  );
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
2218
+ phases.push({
2219
+ phase: "synthesize",
2220
+ durationMs: Date.now() - synthStart,
2221
+ description: "Synthesized sub-agent results",
2222
+ modelUsed: plannerModel
2178
2223
  });
2179
- return fn;
2224
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2225
+ const qStart = Date.now();
2226
+ const qualityReview = await runQualityReview(task, synthesis, opts);
2227
+ if (qualityReview) {
2228
+ phases.push({
2229
+ phase: "quality-review",
2230
+ durationMs: Date.now() - qStart,
2231
+ description: `Score: ${qualityReview.score.toFixed(2)}`,
2232
+ modelUsed: plannerModel
2233
+ });
2234
+ }
2235
+ const t1 = Date.now();
2236
+ const output = new SubagentOutput(synthesis, synthesis, subResults, t0, t1, qualityReview);
2237
+ output.phaseLog = phases;
2238
+ return output;
2180
2239
  }
2181
- var \u03A3 = makeSubagent();
2240
+ var \u03A3 = createPatternTag(defaults13, execute13);
2182
2241
 
2183
2242
  // src/patterns/tau.ts
2184
2243
  var defaults14 = {
@@ -2202,15 +2261,17 @@ var ToolMediatedEntry = class {
2202
2261
  content;
2203
2262
  };
2204
2263
  var TauOutput = class extends PatternOutput {
2205
- constructor(text, entries, finalState, synthesis, startTime, endTime) {
2264
+ constructor(text, entries, finalState, synthesis, startTime, endTime, qualityReview) {
2206
2265
  super(text, startTime, endTime);
2207
2266
  this.entries = entries;
2208
2267
  this.finalState = finalState;
2209
2268
  this.synthesis = synthesis;
2269
+ this.qualityReview = qualityReview;
2210
2270
  }
2211
2271
  entries;
2212
2272
  finalState;
2213
2273
  synthesis;
2274
+ qualityReview;
2214
2275
  };
2215
2276
  var SCHEMA_SYSTEM = `You are a coordination architect. Given a task, design a shared structured context for agent collaboration.
2216
2277
 
@@ -2265,6 +2326,7 @@ async function defineSchema(task, opts) {
2265
2326
  const response = await ask(`Task: ${task}
2266
2327
 
2267
2328
  ${prompt}`, {
2329
+ ...opts,
2268
2330
  model: opts.plannerModel ?? opts.model,
2269
2331
  maxTokens: 1024,
2270
2332
  thinkingLevel: "high"
@@ -2317,10 +2379,9 @@ async function executeRound(roles, assignments, store, round, opts) {
2317
2379
  const systemPrompt = isWrite ? WRITE_SYSTEM(role, keysStr).replace("{store}", storeText) : UPDATE_SYSTEM(role, keysStr).replace("{store}", storeText);
2318
2380
  const task = isWrite ? `Write your initial findings to your assigned keys: ${keysStr}` : `Review the shared context and update your entries for keys: ${keysStr}`;
2319
2381
  const response = await ask(task, {
2382
+ ...opts,
2320
2383
  model: workerModel,
2321
- maxTokens: opts.maxTokens,
2322
- thinkingLevel: opts.thinkingLevel,
2323
- system: systemPrompt
2384
+ system: mergeSystem(opts.system, systemPrompt)
2324
2385
  });
2325
2386
  return { role, response };
2326
2387
  })
@@ -2360,10 +2421,10 @@ ${storeText}
2360
2421
 
2361
2422
  Consolidate into a comprehensive, well-structured synthesis.`,
2362
2423
  {
2424
+ ...opts,
2363
2425
  model: opts.plannerModel ?? opts.model,
2364
- maxTokens: opts.maxTokens,
2365
2426
  thinkingLevel: "high",
2366
- system: CONSOLIDATE_SYSTEM
2427
+ system: mergeSystem(opts.system, CONSOLIDATE_SYSTEM)
2367
2428
  }
2368
2429
  );
2369
2430
  }
@@ -2408,6 +2469,8 @@ async function execute14(pieces, args, opts) {
2408
2469
  }
2409
2470
  if (!opts.quiet) process.stderr.write(" \u2192 Consolidating store...\n");
2410
2471
  const synthesis = await consolidateStore(task, store, { ...opts, plannerModel });
2472
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2473
+ const qualityReview = await runQualityReview(task, synthesis, opts);
2411
2474
  const t1 = Date.now();
2412
2475
  const summary = [
2413
2476
  `Schema keys: ${keys.join(", ")}`,
@@ -2416,30 +2479,9 @@ async function execute14(pieces, args, opts) {
2416
2479
  `Entries: ${allEntries.length}`,
2417
2480
  `Synthesis: ${synthesis}`
2418
2481
  ].join("\n\n");
2419
- return new TauOutput(summary, allEntries, store, synthesis, t0, t1);
2482
+ return new TauOutput(summary, allEntries, store, synthesis, t0, t1, qualityReview);
2420
2483
  }
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();
2484
+ var \u03A4 = createPatternTag(defaults14, execute14);
2443
2485
 
2444
2486
  // src/patterns/thread.ts
2445
2487
  var defaults15 = {
@@ -2448,27 +2490,6 @@ var defaults15 = {
2448
2490
  agents: 3,
2449
2491
  turns: 3
2450
2492
  };
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
2493
  var ThreadMessage = class {
2473
2494
  constructor(role, turn, content) {
2474
2495
  this.role = role;
@@ -2480,13 +2501,15 @@ var ThreadMessage = class {
2480
2501
  content;
2481
2502
  };
2482
2503
  var ThreadOutput = class extends PatternOutput {
2483
- constructor(text, conclusion, messages, startTime, endTime) {
2504
+ constructor(text, conclusion, messages, startTime, endTime, qualityReview) {
2484
2505
  super(text, startTime, endTime);
2485
2506
  this.conclusion = conclusion;
2486
2507
  this.messages = messages;
2508
+ this.qualityReview = qualityReview;
2487
2509
  }
2488
2510
  conclusion;
2489
2511
  messages;
2512
+ qualityReview;
2490
2513
  };
2491
2514
  var THREAD_PROMPT = `You are an agent with the role: {role}.
2492
2515
  Engage in a multi-agent conversation about the topic.
@@ -2508,7 +2531,7 @@ async function execute15(pieces, args, opts) {
2508
2531
  const t0 = Date.now();
2509
2532
  const agentCount = opts.agents ?? 3;
2510
2533
  const maxTurns = opts.turns ?? 3;
2511
- const roles = opts.roles ?? ROLE_SETS4[agentCount] ?? ROLE_SETS4[3] ?? [];
2534
+ const roles = opts.roles ?? THREAD_ROLE_SETS[agentCount] ?? THREAD_ROLE_SETS[3] ?? [];
2512
2535
  const plannerModel = opts.plannerModel ?? opts.model;
2513
2536
  const workerModel = opts.workerModel ?? opts.model;
2514
2537
  if (!opts.quiet) {
@@ -2526,11 +2549,7 @@ async function execute15(pieces, args, opts) {
2526
2549
  for (let a = 0; a < roles.length; a++) {
2527
2550
  const role = roles[a] ?? `Agent ${a + 1}`;
2528
2551
  const prompt = buildThreadPrompt(role, thread);
2529
- const response = await ask(prompt, {
2530
- model: workerModel,
2531
- maxTokens: opts.maxTokens,
2532
- thinkingLevel: opts.thinkingLevel
2533
- });
2552
+ const response = await ask(prompt, { ...opts, model: workerModel });
2534
2553
  messages.push(new ThreadMessage(role, turn, response));
2535
2554
  thread += `
2536
2555
  [${role}] (Turn ${turn}): ${response}
@@ -2546,46 +2565,24 @@ ${thread}
2546
2565
 
2547
2566
  Synthesize a clear, actionable conclusion.`,
2548
2567
  {
2568
+ ...opts,
2549
2569
  model: plannerModel,
2550
- maxTokens: opts.maxTokens,
2551
2570
  thinkingLevel: "high",
2552
- system: SYNTHESIS_SYSTEM6
2571
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM6)
2553
2572
  }
2554
2573
  );
2574
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2575
+ const qualityReview = await runQualityReview(topic, conclusion, opts);
2555
2576
  const t1 = Date.now();
2556
2577
  const summary = messages.map(
2557
2578
  (m) => `[${m.role}] Turn ${m.turn}: ${m.content.slice(0, 150)}${m.content.length > 150 ? "..." : ""}`
2558
2579
  ).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;
2580
+ return new ThreadOutput(summary, conclusion, messages, t0, t1, qualityReview);
2581
2581
  }
2582
- var \u0398 = makeThread();
2582
+ var \u0398 = createPatternTag(defaults15, execute15);
2583
2583
 
2584
2584
  // src/pi.ts
2585
2585
  import {
2586
- getEnvApiKey as getEnvApiKey2,
2587
- getModels as getModels2,
2588
- getProviders as getProviders2,
2589
2586
  streamSimple
2590
2587
  } from "@earendil-works/pi-ai";
2591
2588
 
@@ -2603,10 +2600,28 @@ var PiOutput = class {
2603
2600
  events;
2604
2601
  startTime;
2605
2602
  endTime;
2603
+ /** Trace entry for this single LLM call. Populated by the π tag. */
2604
+ trace = [];
2606
2605
  /** Duration in milliseconds */
2607
2606
  get duration() {
2608
2607
  return this.endTime - this.startTime;
2609
2608
  }
2609
+ /** Total input tokens (convenience accessor) */
2610
+ get inputTokens() {
2611
+ return this.trace[0]?.inputTokens ?? 0;
2612
+ }
2613
+ /** Total output tokens (convenience accessor) */
2614
+ get outputTokens() {
2615
+ return this.trace[0]?.outputTokens ?? 0;
2616
+ }
2617
+ /** Total tokens (convenience accessor) */
2618
+ get totalTokens() {
2619
+ return this.trace[0]?.totalTokens ?? 0;
2620
+ }
2621
+ /** Total cost in USD (convenience accessor) */
2622
+ get totalCost() {
2623
+ return this.trace[0]?.cost ?? 0;
2624
+ }
2610
2625
  toString() {
2611
2626
  return this.text;
2612
2627
  }
@@ -2632,90 +2647,13 @@ var defaults16 = {
2632
2647
  quiet: false,
2633
2648
  maxTokens: 4096
2634
2649
  };
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
2650
  function makeContext(pieces, args, opts) {
2713
2651
  return {
2714
2652
  systemPrompt: opts.system,
2715
2653
  messages: [
2716
2654
  {
2717
2655
  role: "user",
2718
- content: build2(pieces, args),
2656
+ content: build(pieces, args),
2719
2657
  timestamp: Date.now()
2720
2658
  }
2721
2659
  ]
@@ -2724,25 +2662,51 @@ function makeContext(pieces, args, opts) {
2724
2662
  function makeOpts(opts) {
2725
2663
  return {
2726
2664
  maxTokens: opts.maxTokens,
2727
- reasoning: opts.thinkingLevel
2665
+ reasoning: opts.thinkingLevel,
2666
+ timeoutMs: opts.timeoutMs,
2667
+ maxRetries: opts.maxRetries
2728
2668
  };
2729
2669
  }
2730
2670
  async function run(pieces, args, opts) {
2731
- const model = pickModel2(opts.model);
2671
+ const model = pickModel(opts.model);
2732
2672
  if (!model) throw new Error("pizx/\u03C0: No AI models configured. Run `pi auth login` first.");
2733
2673
  const t0 = Date.now();
2734
2674
  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);
2675
+ let traceEntry;
2676
+ try {
2677
+ for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2678
+ if (ev.type === "text_delta") {
2679
+ text += ev.delta;
2680
+ if (!opts.quiet) process.stdout.write(ev.delta);
2681
+ } else if (ev.type === "done") {
2682
+ const msg = ev.message;
2683
+ if (msg?.usage) {
2684
+ traceEntry = {
2685
+ call: 1,
2686
+ modelId: model.id,
2687
+ promptPreview: build(pieces, args).slice(0, 200),
2688
+ outputPreview: text.slice(0, 200),
2689
+ inputTokens: msg.usage.input,
2690
+ outputTokens: msg.usage.output,
2691
+ cacheReadTokens: msg.usage.cacheRead,
2692
+ cacheWriteTokens: msg.usage.cacheWrite,
2693
+ totalTokens: msg.usage.totalTokens,
2694
+ cost: msg.usage.cost.total,
2695
+ durationMs: Date.now() - t0
2696
+ };
2697
+ }
2698
+ }
2739
2699
  }
2700
+ } catch (err) {
2701
+ throw new Error(`pizx/\u03C0: AI generation failed: ${getErrorMessage(err)}`);
2740
2702
  }
2741
2703
  if (!opts.quiet && text) process.stdout.write("\n");
2742
- return new PiOutput(text.trim(), model.id, [], t0, Date.now());
2704
+ const output = new PiOutput(text.trim(), model.id, [], t0, Date.now());
2705
+ if (traceEntry) output.trace = [traceEntry];
2706
+ return output;
2743
2707
  }
2744
2708
  async function* runStream(pieces, args, opts) {
2745
- const model = pickModel2(opts.model);
2709
+ const model = pickModel(opts.model);
2746
2710
  if (!model) throw new Error("pizx/\u03C0: No AI models configured");
2747
2711
  for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2748
2712
  if (ev.type === "text_delta") yield ev.delta;
@@ -2822,22 +2786,18 @@ var AgentPromise = class extends Promise {
2822
2786
  var _sharedSession = null;
2823
2787
  async function getSession(opts) {
2824
2788
  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]);
2789
+ try {
2790
+ const result = await createAgentSession2({
2791
+ cwd: opts.cwd,
2792
+ thinkingLevel: opts.thinkingLevel,
2793
+ tools: opts.tools,
2794
+ excludeTools: opts.excludeTools
2795
+ });
2796
+ _sharedSession = result.session;
2797
+ return _sharedSession;
2798
+ } catch (err) {
2799
+ throw new Error(`pizx/\u03A0: Failed to create agent session: ${getErrorMessage(err)}`);
2839
2800
  }
2840
- return s.trim();
2841
2801
  }
2842
2802
  function getMessageText(msg) {
2843
2803
  const content = msg.content;
@@ -2859,7 +2819,7 @@ function getLastAssistantText(session) {
2859
2819
  return "";
2860
2820
  }
2861
2821
  async function execute16(pieces, args, opts) {
2862
- const prompt = build3(pieces, args);
2822
+ const prompt = build(pieces, args);
2863
2823
  const session = await getSession(opts);
2864
2824
  const t0 = Date.now();
2865
2825
  if (!opts.quiet) {
@@ -2870,15 +2830,12 @@ async function execute16(pieces, args, opts) {
2870
2830
  await session.sendUserMessage(prompt);
2871
2831
  const t1 = Date.now();
2872
2832
  const text = getLastAssistantText(session);
2873
- const turnCount = session.messages.filter(
2874
- (m) => m.role === "assistant"
2875
- ).length;
2833
+ const turnCount = session.messages.filter((m) => m.role === "assistant").length;
2876
2834
  return new AgentOutput(text || "(no assistant response)", turnCount, t0, t1);
2877
2835
  } catch (err) {
2878
2836
  const t1 = Date.now();
2879
- const msg = err instanceof Error ? err.message : String(err);
2880
2837
  console.error("\u03A0 error:", err);
2881
- return new AgentOutput(`\u03A0 error: ${msg}`, 0, t0, t1);
2838
+ return new AgentOutput(`\u03A0 error: ${getErrorMessage(err)}`, 0, t0, t1);
2882
2839
  }
2883
2840
  }
2884
2841
  function makeAgent(opts = {}) {
@@ -2945,6 +2902,7 @@ export {
2945
2902
  closeAgent,
2946
2903
  configureAgent,
2947
2904
  configurePi,
2905
+ createPatternTag,
2948
2906
  \u0391,
2949
2907
  \u0392,
2950
2908
  \u0393,