@tekmidian/pai 0.38.0 → 0.39.1

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.
Files changed (84) hide show
  1. package/dist/{auto-route-DM7GhJ8y.mjs → auto-route-BlWOWT4z.mjs} +2 -2
  2. package/dist/{auto-route-DM7GhJ8y.mjs.map → auto-route-BlWOWT4z.mjs.map} +1 -1
  3. package/dist/{providers-DUshcB-d.mjs → chain-CqlgL7uH.mjs} +734 -1410
  4. package/dist/chain-CqlgL7uH.mjs.map +1 -0
  5. package/dist/cli/index.mjs +4 -3
  6. package/dist/cli/index.mjs.map +1 -1
  7. package/dist/cli/program.mjs +4 -3
  8. package/dist/{clusters-Do4tEGyc.mjs → clusters-wlK0w41E.mjs} +1 -1
  9. package/dist/{clusters-Do4tEGyc.mjs.map → clusters-wlK0w41E.mjs.map} +1 -1
  10. package/dist/daemon/index.mjs +7 -7
  11. package/dist/{daemon-CGg1VCbA.mjs → daemon-CTkN_Vn8.mjs} +25 -25
  12. package/dist/{daemon-CGg1VCbA.mjs.map → daemon-CTkN_Vn8.mjs.map} +1 -1
  13. package/dist/{daemon-DsGGiIJM.mjs → daemon-CmHHmKpg.mjs} +7 -7
  14. package/dist/daemon-mcp/index.mjs +106 -3
  15. package/dist/daemon-mcp/index.mjs.map +1 -1
  16. package/dist/{detector-CMap-9vw.mjs → detector-Bwk_4Pk4.mjs} +1 -1
  17. package/dist/{detector-CMap-9vw.mjs.map → detector-Bwk_4Pk4.mjs.map} +1 -1
  18. package/dist/detector-DO730Zq0.mjs +5 -0
  19. package/dist/{factory-DD2T33C9.mjs → factory-A9x-T2Kg.mjs} +5 -5
  20. package/dist/{factory-DD2T33C9.mjs.map → factory-A9x-T2Kg.mjs.map} +1 -1
  21. package/dist/factory-BD-np0Vd.mjs +3 -0
  22. package/dist/fallback-DqYhiACU.mjs +1675 -0
  23. package/dist/fallback-DqYhiACU.mjs.map +1 -0
  24. package/dist/hooks/route-agents-to-worker.mjs +73 -8
  25. package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
  26. package/dist/hooks/worker-proxy.mjs +71 -6
  27. package/dist/hooks/worker-proxy.mjs.map +2 -2
  28. package/dist/hooks/worker-status-line.mjs +137 -11
  29. package/dist/hooks/worker-status-line.mjs.map +4 -4
  30. package/dist/{indexer-backend-Cox9BCo-.mjs → indexer-backend-TG64CCQC.mjs} +1 -1
  31. package/dist/{indexer-backend-Cox9BCo-.mjs.map → indexer-backend-TG64CCQC.mjs.map} +1 -1
  32. package/dist/{latent-ideas-BC1oINZ-.mjs → latent-ideas-B7wq75Pt.mjs} +2 -2
  33. package/dist/{latent-ideas-BC1oINZ-.mjs.map → latent-ideas-B7wq75Pt.mjs.map} +1 -1
  34. package/dist/{link-boost-QFLrJwD6.mjs → link-boost-HkG7JWZR.mjs} +1 -1
  35. package/dist/{link-boost-QFLrJwD6.mjs.map → link-boost-HkG7JWZR.mjs.map} +1 -1
  36. package/dist/{neighborhood-D9MJ1c8f.mjs → neighborhood-lThN-MaQ.mjs} +1 -1
  37. package/dist/{neighborhood-D9MJ1c8f.mjs.map → neighborhood-lThN-MaQ.mjs.map} +1 -1
  38. package/dist/{note-context-d1wT_-GA.mjs → note-context-b6k0mAKi.mjs} +1 -1
  39. package/dist/{note-context-d1wT_-GA.mjs.map → note-context-b6k0mAKi.mjs.map} +1 -1
  40. package/dist/planner-DGV8VFib.mjs +243 -0
  41. package/dist/planner-DGV8VFib.mjs.map +1 -0
  42. package/dist/{postgres--BjPtLa0.mjs → postgres-mW1n7Vi1.mjs} +1 -1
  43. package/dist/{postgres--BjPtLa0.mjs.map → postgres-mW1n7Vi1.mjs.map} +1 -1
  44. package/dist/{program-Y0hAiVy8.mjs → program-B-QJXCRb.mjs} +95 -19
  45. package/dist/{program-Y0hAiVy8.mjs.map → program-B-QJXCRb.mjs.map} +1 -1
  46. package/dist/{query-feedback-BUJxgw5B.mjs → query-feedback-BV4CcxqS.mjs} +1 -1
  47. package/dist/{query-feedback-BUJxgw5B.mjs.map → query-feedback-BV4CcxqS.mjs.map} +1 -1
  48. package/dist/query-feedback-DSVyHtrG.mjs +3 -0
  49. package/dist/router-Bk77E7hj.mjs +3 -0
  50. package/dist/{router-DK_sLsUL.mjs → router-DcHKnEPa.mjs} +1 -1
  51. package/dist/{router-DK_sLsUL.mjs.map → router-DcHKnEPa.mjs.map} +1 -1
  52. package/dist/skills/Worker/SKILL.md +13 -0
  53. package/dist/{sources-Bi7--33T.mjs → sources-kLnQsNrW.mjs} +1 -1
  54. package/dist/{sources-Bi7--33T.mjs.map → sources-kLnQsNrW.mjs.map} +1 -1
  55. package/dist/{sqlite-DtaL1glm.mjs → sqlite-BenGr3UP.mjs} +1 -1
  56. package/dist/{sqlite-DtaL1glm.mjs.map → sqlite-BenGr3UP.mjs.map} +1 -1
  57. package/dist/{state-BY2L6-vX.mjs → state-CAeyOdfq.mjs} +1 -1
  58. package/dist/{state-BY2L6-vX.mjs.map → state-CAeyOdfq.mjs.map} +1 -1
  59. package/dist/{state-8Hm9E4tW.mjs → state-Ca9F_sZc.mjs} +1 -1
  60. package/dist/{themes-BN0a2duq.mjs → themes-BI4GMRP9.mjs} +1 -1
  61. package/dist/{themes-BN0a2duq.mjs.map → themes-BI4GMRP9.mjs.map} +1 -1
  62. package/dist/{tools-CGPqpU3A.mjs → tools-Bp7hj6OW.mjs} +1 -1
  63. package/dist/{tools-y2bJpKom.mjs → tools-DGcB3o_S.mjs} +13 -13
  64. package/dist/{tools-y2bJpKom.mjs.map → tools-DGcB3o_S.mjs.map} +1 -1
  65. package/dist/{trace-h23JCcFD.mjs → trace-bobARFEX.mjs} +1 -1
  66. package/dist/{trace-h23JCcFD.mjs.map → trace-bobARFEX.mjs.map} +1 -1
  67. package/dist/{vault-indexer-DgsPjMgs.mjs → vault-indexer-C3OfXTNF.mjs} +1 -1
  68. package/dist/{vault-indexer-DgsPjMgs.mjs.map → vault-indexer-C3OfXTNF.mjs.map} +1 -1
  69. package/dist/{work-queue-worker-gsKd2LJa.mjs → work-queue-worker-DW8lz-Oo.mjs} +3 -3
  70. package/dist/{work-queue-worker-Dva_v_pI.mjs → work-queue-worker-R7UGIag_.mjs} +3 -3
  71. package/dist/{work-queue-worker-Dva_v_pI.mjs.map → work-queue-worker-R7UGIag_.mjs.map} +1 -1
  72. package/dist/{zettelkasten-vo7psPdT.mjs → zettelkasten-m5QPtb-L.mjs} +3 -3
  73. package/dist/{zettelkasten-vo7psPdT.mjs.map → zettelkasten-m5QPtb-L.mjs.map} +1 -1
  74. package/docs/commands/README.md +5 -0
  75. package/docs/commands/worker.md +74 -0
  76. package/docs/worker.md +132 -1
  77. package/package.json +1 -1
  78. package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +2 -2
  79. package/dist/detector-DtLExmHN.mjs +0 -5
  80. package/dist/factory-BXzqRYVZ.mjs +0 -3
  81. package/dist/providers-DUshcB-d.mjs.map +0 -1
  82. package/dist/query-feedback-DhyLOe5S.mjs +0 -3
  83. package/dist/router-1zi8jiNF.mjs +0 -3
  84. /package/dist/{main-resolver-IhZo4pI0.mjs → main-resolver-D6IImXvF.mjs} +0 -0
@@ -150,16 +150,25 @@ var DEFAULT_ROUTING2 = {
150
150
  cooldownMinutes: 30,
151
151
  retryOnQuota: true
152
152
  };
153
+ var DEFAULT_TREE = {
154
+ maxDepth: 2,
155
+ maxChildren: 4
156
+ };
157
+ var DEFAULT_MCP_SETS = {
158
+ desktop: ["clickr"]
159
+ };
153
160
  function defaultWorkersConfig() {
154
161
  return {
155
162
  enabled: false,
156
163
  active: null,
157
164
  providers: {},
158
165
  classes: {},
159
- mcpSets: {},
166
+ mcpSets: { ...DEFAULT_MCP_SETS },
160
167
  pane: { ...DEFAULT_PANE },
161
168
  logDir: DEFAULT_LOG_DIR,
162
- routing: { ...DEFAULT_ROUTING2, order: [] }
169
+ routing: { ...DEFAULT_ROUTING2, order: [] },
170
+ tree: { ...DEFAULT_TREE },
171
+ fallback: null
163
172
  };
164
173
  }
165
174
  var WorkersConfigError = class extends Error {
@@ -330,7 +339,7 @@ function parseWorkersConfig(raw) {
330
339
  }
331
340
  }
332
341
  }
333
- const mcpSets = {};
342
+ const mcpSets = { ...DEFAULT_MCP_SETS };
334
343
  if (w.mcpSets !== void 0) {
335
344
  if (typeof w.mcpSets !== "object" || w.mcpSets === null || Array.isArray(w.mcpSets)) {
336
345
  bad(".mcpSets", "must be an object of set name \u2192 [server names]");
@@ -381,9 +390,63 @@ function parseWorkersConfig(raw) {
381
390
  retryOnQuota: r.retryOnQuota === void 0 ? DEFAULT_ROUTING2.retryOnQuota : r.retryOnQuota === true
382
391
  };
383
392
  }
393
+ let tree = { ...DEFAULT_TREE };
394
+ if (w.tree !== void 0) {
395
+ if (typeof w.tree !== "object" || w.tree === null || Array.isArray(w.tree)) {
396
+ bad(".tree", "must be an object");
397
+ }
398
+ const t = w.tree;
399
+ if (t.maxDepth !== void 0) {
400
+ if (typeof t.maxDepth !== "number" || !Number.isInteger(t.maxDepth) || t.maxDepth < 0) {
401
+ bad(".tree.maxDepth", "must be a non-negative integer");
402
+ }
403
+ }
404
+ if (t.maxChildren !== void 0) {
405
+ if (typeof t.maxChildren !== "number" || !Number.isInteger(t.maxChildren) || t.maxChildren < 1) {
406
+ bad(".tree.maxChildren", "must be a positive integer");
407
+ }
408
+ }
409
+ tree = {
410
+ maxDepth: t.maxDepth === void 0 ? DEFAULT_TREE.maxDepth : t.maxDepth,
411
+ maxChildren: t.maxChildren === void 0 ? DEFAULT_TREE.maxChildren : t.maxChildren
412
+ };
413
+ }
384
414
  const active = w.active === void 0 || w.active === null ? null : str(w.active);
385
415
  if (active !== null && active !== "auto" && !(active in providers)) {
386
416
  }
417
+ let fallback = null;
418
+ if (w.fallback !== void 0 && w.fallback !== null) {
419
+ if (typeof w.fallback !== "object" || Array.isArray(w.fallback)) {
420
+ bad(".fallback", "must be an object (written by `pai worker fallback on`)");
421
+ }
422
+ const f = w.fallback;
423
+ const provider = str(f.provider);
424
+ if (!provider) bad(".fallback.provider", "is required");
425
+ const savedRaw = f.saved;
426
+ if (typeof savedRaw !== "object" || savedRaw === null || Array.isArray(savedRaw)) {
427
+ bad(".fallback.saved", "must be an object");
428
+ }
429
+ const s = savedRaw;
430
+ if (typeof s.env !== "object" || s.env === null || Array.isArray(s.env)) {
431
+ bad(".fallback.saved.env", "must be an object of env key \u2192 previous value or null");
432
+ }
433
+ const env = {};
434
+ for (const [k, v] of Object.entries(s.env)) {
435
+ env[k] = v === null || typeof v === "string" ? v : null;
436
+ }
437
+ if (s.model !== null && s.model !== void 0 && typeof s.model !== "string") {
438
+ bad(".fallback.saved.model", "must be a string or null");
439
+ }
440
+ fallback = {
441
+ provider,
442
+ saved: {
443
+ env,
444
+ model: typeof s.model === "string" ? s.model : null,
445
+ envExisted: s.envExisted === true
446
+ },
447
+ on: str(f.on) || (/* @__PURE__ */ new Date(0)).toISOString()
448
+ };
449
+ }
387
450
  return {
388
451
  enabled: w.enabled === void 0 ? d.enabled : w.enabled === true,
389
452
  active,
@@ -392,11 +455,13 @@ function parseWorkersConfig(raw) {
392
455
  mcpSets,
393
456
  pane,
394
457
  logDir: str(w.logDir) || d.logDir,
395
- routing
458
+ routing,
459
+ tree,
460
+ fallback
396
461
  };
397
462
  }
398
- function readWorkersSection() {
399
- const raw = readJsonStrict(CONFIG_FILE, "~/.config/pai/config.json");
463
+ function readWorkersSection(path = CONFIG_FILE) {
464
+ const raw = readJsonStrict(path, "~/.config/pai/config.json");
400
465
  return { raw, workers: parseWorkersConfig(raw.workers) };
401
466
  }
402
467
  function expandHome(p) {
@@ -468,7 +533,44 @@ function workerInScope(worker, term2) {
468
533
  return tabKey(worker.term) === tabKey(term2) && tabKey(term2) !== "";
469
534
  }
470
535
 
536
+ // src/workers/handoff.ts
537
+ import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "node:fs";
538
+ import { dirname, join as join5 } from "node:path";
539
+ function inboxPath(logDir, id) {
540
+ return join5(logDir, `${id}.inbox.jsonl`);
541
+ }
542
+ function readInbox(logDir, id) {
543
+ const path = inboxPath(logDir, id);
544
+ if (!existsSync3(path)) return [];
545
+ const out = [];
546
+ for (const line of readFileSync3(path, "utf8").split("\n")) {
547
+ if (!line.trim()) continue;
548
+ try {
549
+ out.push(JSON.parse(line));
550
+ } catch {
551
+ }
552
+ }
553
+ return out;
554
+ }
555
+
556
+ // src/workers/tree.ts
557
+ function workerDepth(statuses, id) {
558
+ const byId = new Map(statuses.map((s) => [s.id, s]));
559
+ let depth = 0;
560
+ let cur = byId.get(id);
561
+ const seen = /* @__PURE__ */ new Set([id]);
562
+ while (cur?.parent && !seen.has(cur.parent)) {
563
+ seen.add(cur.parent);
564
+ const next = byId.get(cur.parent);
565
+ if (!next) break;
566
+ depth += 1;
567
+ cur = next;
568
+ }
569
+ return depth;
570
+ }
571
+
471
572
  // src/workers/report.ts
573
+ var OPERATOR_MARK = "[operator]";
472
574
  var WORKER_CONTRACT_PROMPT = [
473
575
  "You are a headless implementation worker, run non-interactively by an orchestrating session.",
474
576
  "No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.",
@@ -476,7 +578,20 @@ var WORKER_CONTRACT_PROMPT = [
476
578
  '{"changed":[{"path":"\u2026","summary":"\u2026"}],"commands":["\u2026"],"checks":[{"name":"\u2026","ok":true,"detail":"\u2026"}],"open":["\u2026"],"notes":"one line"}',
477
579
  "changed: files you touched (path + one-line summary). commands: the commands that verify the work.",
478
580
  "checks: each with ok true/false and the evidence in detail. open: what you could not finish, if anything.",
479
- "notes: one line, the headline a reviewer reads first."
581
+ "notes: one line, the headline a reviewer reads first.",
582
+ "",
583
+ `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,
584
+ "run (the prompt of a resumed run is the operator's too). Answer it FIRST, in one or two plain lines \u2014",
585
+ "a question gets its answer, an instruction gets one line naming what will change \u2014 then continue the",
586
+ "task you were on.",
587
+ "",
588
+ "Sub-workers: you may start your own workers with `pai worker run --class <class> -p '<prompt>'`",
589
+ "(Bash tool). Your children run on their own provider and report back to you automatically when",
590
+ "they finish. Send a handoff UP instead of doing work yourself when a cheaper provider would",
591
+ "suffice, the task is out of your scope, or a decision is needed:",
592
+ '`pai worker handoff \'{"kind":"proposal","text":"\u2026","data":{\u2026}}\'` (kinds: proposal,',
593
+ "question, blocker; results are sent for you when you finish). Sibling workers are not a",
594
+ "coordination path \u2014 there is no sideways channel; everything goes up to your parent."
480
595
  ].join("\n");
481
596
 
482
597
  // src/workers/render.ts
@@ -504,9 +619,9 @@ function contextMeter(c, s, minPct = 0) {
504
619
  if (pct > 70) return c("yellow", label);
505
620
  return label;
506
621
  }
507
- function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(true)) {
622
+ function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(true), all = mine, inbox = {}) {
508
623
  if (!mine.length) return "";
509
- const running = mine.filter((s) => s.state === "running" && alive(s.pid));
624
+ const running = mine.filter((s) => s.state === "running" && alive(s.pid)).sort((a, b) => workerDepth(all, a.id) - workerDepth(all, b.id));
510
625
  const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
511
626
  const doneToday = mine.filter((s) => s.state !== "running" && s.started.startsWith(today));
512
627
  const ok = doneToday.filter((s) => s.state === "done").length;
@@ -516,7 +631,10 @@ function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(
516
631
  let head = `${providerTag} \u25B6${running.length}`;
517
632
  const parts = running.slice(0, 3).map((s) => {
518
633
  const meter = contextMeter(c, s, 60);
519
- return `${s.id.slice(-4)} ${s.label.slice(0, 26)} ${ageOf(s.started, now)} \xB7 ${s.last.slice(0, 30)}` + (meter ? ` ${meter}` : "");
634
+ const depth = workerDepth(all, s.id);
635
+ const lead = depth > 0 ? " ".repeat(depth - 1) + "\u21B3 " : "";
636
+ const box = inbox[s.id] ? ` \u25C6${inbox[s.id]}` : "";
637
+ return `${lead}${s.id.slice(-4)} ${s.label.slice(0, 26)} ${ageOf(s.started, now)} \xB7 ${s.last.slice(0, 30)}${box}` + (meter ? ` ${meter}` : "");
520
638
  });
521
639
  if (parts.length) head += " " + parts.join(" | ");
522
640
  if (doneToday.length) head += ` \u2713${ok} \u2717${bad2} today`;
@@ -524,6 +642,14 @@ function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(
524
642
  }
525
643
 
526
644
  // src/workers/viewer.ts
645
+ function inboxCounts(logDir, statuses) {
646
+ const out = {};
647
+ for (const s of statuses) {
648
+ const n = readInbox(logDir, s.id).length;
649
+ if (n) out[s.id] = n;
650
+ }
651
+ return out;
652
+ }
527
653
  function statusLineOutput(logDir, term2, cwd2, now = /* @__PURE__ */ new Date()) {
528
654
  const statuses = loadStatuses(logDir);
529
655
  const mine = statuses.filter((s) => {
@@ -531,7 +657,7 @@ function statusLineOutput(logDir, term2, cwd2, now = /* @__PURE__ */ new Date())
531
657
  const sameDir = cwd2 && s.cwd.startsWith(cwd2);
532
658
  return sameScope || !term2 && sameDir;
533
659
  });
534
- return renderStatusLine(mine, now);
660
+ return renderStatusLine(mine, now, makeColor(false), statuses, inboxCounts(logDir, mine));
535
661
  }
536
662
 
537
663
  // src/workers/standalone/status-line.ts