@tekmidian/pai 0.36.1 → 0.37.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.
Files changed (157) hide show
  1. package/dist/{auto-route-Byf8ENXj.mjs → auto-route-DM7GhJ8y.mjs} +2 -2
  2. package/dist/{auto-route-Byf8ENXj.mjs.map → auto-route-DM7GhJ8y.mjs.map} +1 -1
  3. package/dist/cli/index.mjs +5 -4
  4. package/dist/cli/index.mjs.map +1 -1
  5. package/dist/cli/program.d.mts.map +1 -1
  6. package/dist/cli/program.mjs +5 -4
  7. package/dist/{clusters-wZgTCYCB.mjs → clusters-Do4tEGyc.mjs} +1 -1
  8. package/dist/{clusters-wZgTCYCB.mjs.map → clusters-Do4tEGyc.mjs.map} +1 -1
  9. package/dist/{context-handover-cache-PtNvj_8D.mjs → context-handover-cache-BpUojjsi.mjs} +2 -2
  10. package/dist/{context-handover-cache-PtNvj_8D.mjs.map → context-handover-cache-BpUojjsi.mjs.map} +1 -1
  11. package/dist/daemon/index.mjs +8 -8
  12. package/dist/{daemon-BZ93KRBo.mjs → daemon-CGg1VCbA.mjs} +25 -25
  13. package/dist/{daemon-BZ93KRBo.mjs.map → daemon-CGg1VCbA.mjs.map} +1 -1
  14. package/dist/daemon-DsGGiIJM.mjs +20 -0
  15. package/dist/daemon-mcp/index.mjs +428 -1
  16. package/dist/daemon-mcp/index.mjs.map +1 -1
  17. package/dist/{detector--Gg5JRN5.mjs → detector-CMap-9vw.mjs} +1 -1
  18. package/dist/{detector--Gg5JRN5.mjs.map → detector-CMap-9vw.mjs.map} +1 -1
  19. package/dist/detector-DtLExmHN.mjs +5 -0
  20. package/dist/factory-BXzqRYVZ.mjs +3 -0
  21. package/dist/{factory-Bsp7xOpO.mjs → factory-DD2T33C9.mjs} +5 -5
  22. package/dist/{factory-Bsp7xOpO.mjs.map → factory-DD2T33C9.mjs.map} +1 -1
  23. package/dist/hooks/context-compression-hook.mjs +164 -14
  24. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  25. package/dist/hooks/initialize-session.mjs +6 -0
  26. package/dist/hooks/initialize-session.mjs.map +3 -3
  27. package/dist/hooks/load-core-context.mjs +6 -0
  28. package/dist/hooks/load-core-context.mjs.map +3 -3
  29. package/dist/hooks/load-project-context.mjs +6 -0
  30. package/dist/hooks/load-project-context.mjs.map +3 -3
  31. package/dist/hooks/post-compact-inject.mjs +48 -7
  32. package/dist/hooks/post-compact-inject.mjs.map +4 -4
  33. package/dist/hooks/route-agents-to-worker.mjs +456 -0
  34. package/dist/hooks/route-agents-to-worker.mjs.map +7 -0
  35. package/dist/hooks/status-line.mjs +400 -0
  36. package/dist/hooks/status-line.mjs.map +7 -0
  37. package/dist/hooks/stop-hook.mjs +6 -0
  38. package/dist/hooks/stop-hook.mjs.map +3 -3
  39. package/dist/hooks/subagent-stop-hook.mjs +6 -0
  40. package/dist/hooks/subagent-stop-hook.mjs.map +3 -3
  41. package/dist/hooks/sync-todo-to-md.mjs +6 -0
  42. package/dist/hooks/sync-todo-to-md.mjs.map +3 -3
  43. package/dist/hooks/update-tab-on-action.mjs +6 -0
  44. package/dist/hooks/update-tab-on-action.mjs.map +3 -3
  45. package/dist/hooks/update-tab-titles.mjs +6 -0
  46. package/dist/hooks/update-tab-titles.mjs.map +3 -3
  47. package/dist/hooks/worker-proxy.mjs +796 -0
  48. package/dist/hooks/worker-proxy.mjs.map +7 -0
  49. package/dist/hooks/worker-status-line.mjs +483 -0
  50. package/dist/hooks/worker-status-line.mjs.map +7 -0
  51. package/dist/{indexer-backend-nQZuEx6N.mjs → indexer-backend-Cox9BCo-.mjs} +1 -1
  52. package/dist/{indexer-backend-nQZuEx6N.mjs.map → indexer-backend-Cox9BCo-.mjs.map} +1 -1
  53. package/dist/{kg-entity-DbOMPdF9.mjs → kg-entity-r8duqhi9.mjs} +1 -1
  54. package/dist/{kg-entity-DbOMPdF9.mjs.map → kg-entity-r8duqhi9.mjs.map} +1 -1
  55. package/dist/{latent-ideas-Bn6A5-5P.mjs → latent-ideas-BC1oINZ-.mjs} +2 -2
  56. package/dist/{latent-ideas-Bn6A5-5P.mjs.map → latent-ideas-BC1oINZ-.mjs.map} +1 -1
  57. package/dist/{link-boost-fYjUnxCN.mjs → link-boost-QFLrJwD6.mjs} +1 -1
  58. package/dist/{link-boost-fYjUnxCN.mjs.map → link-boost-QFLrJwD6.mjs.map} +1 -1
  59. package/dist/{main-resolver-BAbhKpeX.mjs → main-resolver-DlaLOFBA.mjs} +1 -1
  60. package/dist/{main-resolver-BAbhKpeX.mjs.map → main-resolver-DlaLOFBA.mjs.map} +1 -1
  61. package/dist/{main-resolver-Dxh444GO.mjs → main-resolver-IhZo4pI0.mjs} +1 -1
  62. package/dist/{neighborhood-DpaEM991.mjs → neighborhood-D9MJ1c8f.mjs} +1 -1
  63. package/dist/{neighborhood-DpaEM991.mjs.map → neighborhood-D9MJ1c8f.mjs.map} +1 -1
  64. package/dist/{note-context-DrcY4cWm.mjs → note-context-d1wT_-GA.mjs} +1 -1
  65. package/dist/{note-context-DrcY4cWm.mjs.map → note-context-d1wT_-GA.mjs.map} +1 -1
  66. package/dist/{postgres-BVme6qX0.mjs → postgres--BjPtLa0.mjs} +1 -1
  67. package/dist/{postgres-BVme6qX0.mjs.map → postgres--BjPtLa0.mjs.map} +1 -1
  68. package/dist/{program-C-fUghPv.mjs → program-CEIHn_Ma.mjs} +693 -101
  69. package/dist/program-CEIHn_Ma.mjs.map +1 -0
  70. package/dist/providers-sXcK5bDZ.mjs +3067 -0
  71. package/dist/providers-sXcK5bDZ.mjs.map +1 -0
  72. package/dist/{query-feedback-C1T6kS18.mjs → query-feedback-BUJxgw5B.mjs} +1 -1
  73. package/dist/{query-feedback-C1T6kS18.mjs.map → query-feedback-BUJxgw5B.mjs.map} +1 -1
  74. package/dist/query-feedback-DhyLOe5S.mjs +3 -0
  75. package/dist/router-1zi8jiNF.mjs +3 -0
  76. package/dist/{router-CsDm7HvK.mjs → router-DK_sLsUL.mjs} +1 -1
  77. package/dist/{router-CsDm7HvK.mjs.map → router-DK_sLsUL.mjs.map} +1 -1
  78. package/dist/skills/Worker/SKILL.md +41 -0
  79. package/dist/{sources-D8ZdNfvK.mjs → sources-Bi7--33T.mjs} +1 -1
  80. package/dist/{sources-D8ZdNfvK.mjs.map → sources-Bi7--33T.mjs.map} +1 -1
  81. package/dist/{sqlite-D1IaR8Am.mjs → sqlite-DtaL1glm.mjs} +1 -1
  82. package/dist/{sqlite-D1IaR8Am.mjs.map → sqlite-DtaL1glm.mjs.map} +1 -1
  83. package/dist/{state-qtmrBWCm.mjs → state-8Hm9E4tW.mjs} +1 -1
  84. package/dist/{state-WaXhLr6R.mjs → state-BY2L6-vX.mjs} +1 -1
  85. package/dist/{state-WaXhLr6R.mjs.map → state-BY2L6-vX.mjs.map} +1 -1
  86. package/dist/{themes-XPkj_bfP.mjs → themes-BN0a2duq.mjs} +1 -1
  87. package/dist/{themes-XPkj_bfP.mjs.map → themes-BN0a2duq.mjs.map} +1 -1
  88. package/dist/{tools-DEt6YPfc.mjs → tools-CGPqpU3A.mjs} +1 -1
  89. package/dist/{tools-ceiy7ANX.mjs → tools-y2bJpKom.mjs} +14 -14
  90. package/dist/{tools-ceiy7ANX.mjs.map → tools-y2bJpKom.mjs.map} +1 -1
  91. package/dist/{trace-DfyGmMG_.mjs → trace-h23JCcFD.mjs} +1 -1
  92. package/dist/{trace-DfyGmMG_.mjs.map → trace-h23JCcFD.mjs.map} +1 -1
  93. package/dist/{vault-indexer-CFvlPUMB.mjs → vault-indexer-DgsPjMgs.mjs} +1 -1
  94. package/dist/{vault-indexer-CFvlPUMB.mjs.map → vault-indexer-DgsPjMgs.mjs.map} +1 -1
  95. package/dist/{work-queue-worker-B8W8_3Rn.mjs → work-queue-worker-Dva_v_pI.mjs} +4 -4
  96. package/dist/{work-queue-worker-B8W8_3Rn.mjs.map → work-queue-worker-Dva_v_pI.mjs.map} +1 -1
  97. package/dist/{work-queue-worker-HN2Ufg-L.mjs → work-queue-worker-gsKd2LJa.mjs} +4 -4
  98. package/dist/{zettelkasten-CvjmMghT.mjs → zettelkasten-vo7psPdT.mjs} +3 -3
  99. package/dist/{zettelkasten-CvjmMghT.mjs.map → zettelkasten-vo7psPdT.mjs.map} +1 -1
  100. package/docs/commands/README.md +28 -0
  101. package/docs/commands/backup.md +1 -1
  102. package/docs/commands/clear-names.md +1 -1
  103. package/docs/commands/daemon.md +1 -1
  104. package/docs/commands/db.md +1 -1
  105. package/docs/commands/end.md +1 -1
  106. package/docs/commands/help.md +1 -1
  107. package/docs/commands/identity.md +1 -1
  108. package/docs/commands/kg.md +1 -1
  109. package/docs/commands/mcp.md +1 -1
  110. package/docs/commands/memory.md +1 -1
  111. package/docs/commands/notify.md +1 -1
  112. package/docs/commands/observation.md +1 -1
  113. package/docs/commands/obsidian.md +1 -1
  114. package/docs/commands/pause.md +1 -1
  115. package/docs/commands/project.md +1 -1
  116. package/docs/commands/projects.md +1 -1
  117. package/docs/commands/registry.md +1 -1
  118. package/docs/commands/restore.md +1 -1
  119. package/docs/commands/session.md +1 -1
  120. package/docs/commands/sessions.md +1 -1
  121. package/docs/commands/setup.md +1 -1
  122. package/docs/commands/shell-init.md +1 -1
  123. package/docs/commands/skill.md +1 -1
  124. package/docs/commands/task.md +1 -1
  125. package/docs/commands/topic.md +1 -1
  126. package/docs/commands/update.md +1 -1
  127. package/docs/commands/worker.md +361 -0
  128. package/docs/commands/zettel.md +1 -1
  129. package/docs/worker.md +303 -0
  130. package/package.json +1 -1
  131. package/scripts/build-hooks.mjs +46 -1
  132. package/src/hooks/pre-compact.sh +1 -0
  133. package/src/hooks/session-autosave.sh +1 -0
  134. package/src/hooks/session-stop.sh +1 -0
  135. package/src/hooks/ts/lib/context-fill.test.ts +104 -0
  136. package/src/hooks/ts/lib/context-fill.ts +73 -0
  137. package/src/hooks/ts/lib/handover-evidence.ts +227 -0
  138. package/src/hooks/ts/lib/worker-session.test.ts +24 -0
  139. package/src/hooks/ts/lib/worker-session.ts +23 -0
  140. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +2 -0
  141. package/src/hooks/ts/post-tool-use/update-tab-on-action.ts +2 -0
  142. package/src/hooks/ts/pre-compact/context-compression-hook.ts +17 -0
  143. package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +128 -0
  144. package/src/hooks/ts/session-start/initialize-session.ts +2 -0
  145. package/src/hooks/ts/session-start/load-core-context.ts +2 -0
  146. package/src/hooks/ts/session-start/load-project-context.ts +2 -0
  147. package/src/hooks/ts/session-start/post-compact-inject.ts +13 -0
  148. package/src/hooks/ts/stop/stop-hook.ts +2 -0
  149. package/src/hooks/ts/subagent-stop/subagent-stop-hook.ts +2 -0
  150. package/src/hooks/ts/user-prompt/update-tab-titles.ts +2 -0
  151. package/statusline-command.sh +17 -0
  152. package/dist/daemon-DRdoA489.mjs +0 -20
  153. package/dist/detector-DGAk1iBR.mjs +0 -5
  154. package/dist/factory-CrokPMk2.mjs +0 -3
  155. package/dist/program-C-fUghPv.mjs.map +0 -1
  156. package/dist/query-feedback-BBMBp96K.mjs +0 -3
  157. package/dist/router-BMkOb62X.mjs +0 -3
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as PaiClient } from "../ipc-client-BmypMNYk.mjs";
3
3
  import { o as loadConfig } from "../config-B64vFg14.mjs";
4
+ import { C as loadStatus, D as workersLogDir, E as ledgerPath, a as setRole, c as useProvider, d as replayOutput, i as setProviderEnabled, k as readWorkersSection, m as testProvider, n as describeProviders, o as setWorkersEnabled, p as runWorker, r as removeProvider, s as unsetRole, t as addProvider, u as psOutput, v as sayToWorker, w as ledgerSummary } from "../providers-sXcK5bDZ.mjs";
4
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
7
 
@@ -1406,6 +1407,97 @@ function handleIntersectionResults(result, left, right) {
1406
1407
  result.value = merged.data;
1407
1408
  return result;
1408
1409
  }
1410
+ const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
1411
+ $ZodType.init(inst, def);
1412
+ inst._zod.parse = (payload, ctx) => {
1413
+ const input = payload.value;
1414
+ if (!isPlainObject(input)) {
1415
+ payload.issues.push({
1416
+ expected: "record",
1417
+ code: "invalid_type",
1418
+ input,
1419
+ inst
1420
+ });
1421
+ return payload;
1422
+ }
1423
+ const proms = [];
1424
+ const values = def.keyType._zod.values;
1425
+ if (values) {
1426
+ payload.value = {};
1427
+ const recordKeys = /* @__PURE__ */ new Set();
1428
+ for (const key of values) if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
1429
+ recordKeys.add(typeof key === "number" ? key.toString() : key);
1430
+ const result = def.valueType._zod.run({
1431
+ value: input[key],
1432
+ issues: []
1433
+ }, ctx);
1434
+ if (result instanceof Promise) proms.push(result.then((result) => {
1435
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
1436
+ payload.value[key] = result.value;
1437
+ }));
1438
+ else {
1439
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
1440
+ payload.value[key] = result.value;
1441
+ }
1442
+ }
1443
+ let unrecognized;
1444
+ for (const key in input) if (!recordKeys.has(key)) {
1445
+ unrecognized = unrecognized ?? [];
1446
+ unrecognized.push(key);
1447
+ }
1448
+ if (unrecognized && unrecognized.length > 0) payload.issues.push({
1449
+ code: "unrecognized_keys",
1450
+ input,
1451
+ inst,
1452
+ keys: unrecognized
1453
+ });
1454
+ } else {
1455
+ payload.value = {};
1456
+ for (const key of Reflect.ownKeys(input)) {
1457
+ if (key === "__proto__") continue;
1458
+ let keyResult = def.keyType._zod.run({
1459
+ value: key,
1460
+ issues: []
1461
+ }, ctx);
1462
+ if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
1463
+ if (typeof key === "string" && number$1.test(key) && keyResult.issues.length) {
1464
+ const retryResult = def.keyType._zod.run({
1465
+ value: Number(key),
1466
+ issues: []
1467
+ }, ctx);
1468
+ if (retryResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
1469
+ if (retryResult.issues.length === 0) keyResult = retryResult;
1470
+ }
1471
+ if (keyResult.issues.length) {
1472
+ if (def.mode === "loose") payload.value[key] = input[key];
1473
+ else payload.issues.push({
1474
+ code: "invalid_key",
1475
+ origin: "record",
1476
+ issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
1477
+ input: key,
1478
+ path: [key],
1479
+ inst
1480
+ });
1481
+ continue;
1482
+ }
1483
+ const result = def.valueType._zod.run({
1484
+ value: input[key],
1485
+ issues: []
1486
+ }, ctx);
1487
+ if (result instanceof Promise) proms.push(result.then((result) => {
1488
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
1489
+ payload.value[keyResult.value] = result.value;
1490
+ }));
1491
+ else {
1492
+ if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
1493
+ payload.value[keyResult.value] = result.value;
1494
+ }
1495
+ }
1496
+ }
1497
+ if (proms.length) return Promise.all(proms).then(() => payload);
1498
+ return payload;
1499
+ };
1500
+ });
1409
1501
  const $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
1410
1502
  $ZodType.init(inst, def);
1411
1503
  const values = getEnumValues(def.entries);
@@ -2614,6 +2706,39 @@ const intersectionProcessor = (schema, ctx, json, params) => {
2614
2706
  const isSimpleIntersection = (val) => "allOf" in val && Object.keys(val).length === 1;
2615
2707
  json.allOf = [...isSimpleIntersection(a) ? a.allOf : [a], ...isSimpleIntersection(b) ? b.allOf : [b]];
2616
2708
  };
2709
+ const recordProcessor = (schema, ctx, _json, params) => {
2710
+ const json = _json;
2711
+ const def = schema._zod.def;
2712
+ json.type = "object";
2713
+ const keyType = def.keyType;
2714
+ const patterns = keyType._zod.bag?.patterns;
2715
+ if (def.mode === "loose" && patterns && patterns.size > 0) {
2716
+ const valueSchema = process$1(def.valueType, ctx, {
2717
+ ...params,
2718
+ path: [
2719
+ ...params.path,
2720
+ "patternProperties",
2721
+ "*"
2722
+ ]
2723
+ });
2724
+ json.patternProperties = {};
2725
+ for (const pattern of patterns) json.patternProperties[pattern.source] = valueSchema;
2726
+ } else {
2727
+ if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") json.propertyNames = process$1(def.keyType, ctx, {
2728
+ ...params,
2729
+ path: [...params.path, "propertyNames"]
2730
+ });
2731
+ json.additionalProperties = process$1(def.valueType, ctx, {
2732
+ ...params,
2733
+ path: [...params.path, "additionalProperties"]
2734
+ });
2735
+ }
2736
+ const keyValues = keyType._zod.values;
2737
+ if (keyValues) {
2738
+ const validKeyValues = [...keyValues].filter((v) => typeof v === "string" || typeof v === "number");
2739
+ if (validKeyValues.length > 0) json.required = validKeyValues;
2740
+ }
2741
+ };
2617
2742
  const nullableProcessor = (schema, ctx, json, params) => {
2618
2743
  const def = schema._zod.def;
2619
2744
  const inner = process$1(def.innerType, ctx, params);
@@ -3047,6 +3172,21 @@ function intersection(left, right) {
3047
3172
  right
3048
3173
  });
3049
3174
  }
3175
+ const ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
3176
+ $ZodRecord.init(inst, def);
3177
+ ZodType.init(inst, def);
3178
+ inst._zod.processJSONSchema = (ctx, json, params) => recordProcessor(inst, ctx, json, params);
3179
+ inst.keyType = def.keyType;
3180
+ inst.valueType = def.valueType;
3181
+ });
3182
+ function record(keyType, valueType, params) {
3183
+ return new ZodRecord({
3184
+ type: "record",
3185
+ keyType,
3186
+ valueType,
3187
+ ...normalizeParams(params)
3188
+ });
3189
+ }
3050
3190
  const ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
3051
3191
  $ZodEnum.init(inst, def);
3052
3192
  ZodType.init(inst, def);
@@ -3299,6 +3439,7 @@ Fetch the full skill instructions with: prompts/get { name: "<skill-name>" }
3299
3439
  | what's emerging, find patterns in vault | vault-emerge |
3300
3440
  | find orphans, orphaned notes | vault-orphans |
3301
3441
  | trace idea, how did X evolve | vault-trace |
3442
+ | run a worker, check workers, worker providers | worker |
3302
3443
 
3303
3444
  ### Reference Resources
3304
3445
 
@@ -4377,6 +4518,48 @@ Otherwise the open items live only in a session note nobody re-reads, which is t
4377
4518
  `
4378
4519
  };
4379
4520
 
4521
+ //#endregion
4522
+ //#region src/daemon-mcp/prompts/worker.ts
4523
+ const worker = {
4524
+ description: "Run, watch and route subagent workers on configured providers",
4525
+ content: `## Worker Skill
4526
+
4527
+ USE WHEN delegating subagent work, checking on running workers, or managing worker providers.
4528
+
4529
+ ### Delegating
4530
+
4531
+ The Agent tool is denied when workers are on — delegate with Bash instead:
4532
+
4533
+ \`\`\`
4534
+ pai worker run --label "short task label" --role research \\
4535
+ -p '<full, self-contained task spec>' \\
4536
+ --allowedTools 'Read,Edit,Write,Bash,Grep,Glob' --output-format json
4537
+ \`\`\`
4538
+
4539
+ - Always run in background (run_in_background: true) and with a timeout.
4540
+ - Roles: implement (default quality), research (web: add WebSearch,WebFetch), spotcheck (cheap/fast model).
4541
+ - The answer is in the \`result\` field of the JSON it prints. Review the diff yourself.
4542
+ - \`--no-pane\` suppresses the iTerm follow pane; panes open automatically otherwise.
4543
+ - Panes run under the \`pai-worker\` dynamic profile (default profile's font
4544
+ family at \`workers.pane.fontSize\`, default 13); \`pai worker pane <id> --check\`
4545
+ reports the pane plus that profile's path, existence and font.
4546
+
4547
+ ### Watching
4548
+
4549
+ - \`worker_ps\` — running + last finished workers.
4550
+ - \`worker_replay\` with id — the transcript of one worker.
4551
+ - CLI equivalents: \`pai worker ps\`, \`pai worker follow <id>\`.
4552
+
4553
+ ### Routing
4554
+
4555
+ - \`worker_status\` — on/off, active provider, providers, run tally.
4556
+ - \`worker_providers\` — list/add/remove/use/enable/disable/test. Adding needs
4557
+ name, base_url, model and a key (key file path, or a raw key which is
4558
+ parked in ~/.config/pai/keys/<name>, mode 0600).
4559
+ - \`worker_roles\` — list/set/unset role → provider[/fast].
4560
+ - \`worker_toggle\` — off makes Agent subagents run on Anthropic again.`
4561
+ };
4562
+
4380
4563
  //#endregion
4381
4564
  //#region src/daemon-mcp/resources/aesthetic.ts
4382
4565
  const aesthetic = {
@@ -5261,7 +5444,8 @@ async function startShim() {
5261
5444
  "whisper": whisper,
5262
5445
  "consolidate": consolidate,
5263
5446
  "advisor": advisor,
5264
- "tasks": tasks
5447
+ "tasks": tasks,
5448
+ "worker": worker
5265
5449
  };
5266
5450
  for (const [promptName, skill] of Object.entries(SKILL_PROMPTS)) server.prompt(promptName, skill.description, () => ({ messages: [{
5267
5451
  role: "user",
@@ -5521,6 +5705,249 @@ async function startShim() {
5521
5705
  top_k: number().int().min(1).max(100).optional().describe("Maximum KG triples to return after re-ranking. Default: 20."),
5522
5706
  tenant_id: string().optional().describe("Tenant ID for entity lookup scoping. Default: 'default'.")
5523
5707
  }, async (args) => proxyTool("memory_kg_search", args));
5708
+ const workerText = (s) => ({ content: [{
5709
+ type: "text",
5710
+ text: s
5711
+ }] });
5712
+ const workerError = (e) => ({
5713
+ content: [{
5714
+ type: "text",
5715
+ text: e instanceof Error ? e.message : String(e)
5716
+ }],
5717
+ isError: true
5718
+ });
5719
+ server.tool("worker_status", [
5720
+ "Show the worker system state: on/off, active provider, configured providers,",
5721
+ "roles, and today's run tally from the ledger.",
5722
+ "",
5723
+ "Use this before delegating with `pai worker run` to see what routing will choose."
5724
+ ].join("\n"), {}, async () => {
5725
+ try {
5726
+ const { workers } = readWorkersSection();
5727
+ const s = ledgerSummary(ledgerPath(workersLogDir(workers)), "all", 10);
5728
+ const tally = s ? [`runs ${s.scope}: ${s.started} started, ${s.endedOk} ok, ${s.endedFailed} failed`, `agent hook: ${s.denied} denied, ${s.allowed} allowed, ${s.reroutes} reroutes`] : ["no runs recorded yet"];
5729
+ return workerText([
5730
+ `workers: ${workers.enabled ? "on" : "off"}`,
5731
+ `active provider: ${workers.active ?? "(none)"}`,
5732
+ ...describeProviders(workers),
5733
+ "",
5734
+ ...tally
5735
+ ].join("\n"));
5736
+ } catch (e) {
5737
+ return workerError(e);
5738
+ }
5739
+ });
5740
+ server.tool("worker_providers", [
5741
+ "Manage worker providers (list, add, remove, use, enable, disable, test).",
5742
+ "",
5743
+ "action=list (default) shows providers, roles and routing.",
5744
+ "action=add needs name, model — and either key_file or key, plus:",
5745
+ " base_url (anthropic protocol) or upstream_url (protocol=openai, runs",
5746
+ " through the local PAI proxy). A raw key is written to",
5747
+ " ~/.config/pai/keys/<name> (mode 0600); only the path lands in the config.",
5748
+ " engine=codex runs the Codex CLI instead of Claude Code.",
5749
+ "action=test runs a one-word pong probe through the provider",
5750
+ " (reports 'codex not installed' when that engine's CLI is missing)."
5751
+ ].join("\n"), {
5752
+ action: _enum([
5753
+ "list",
5754
+ "add",
5755
+ "remove",
5756
+ "use",
5757
+ "enable",
5758
+ "disable",
5759
+ "test"
5760
+ ]).optional().describe("Default: list."),
5761
+ name: string().optional().describe("Provider name (required for every action but list)."),
5762
+ base_url: string().optional().describe("Anthropic-compatible API base URL (add)."),
5763
+ upstream_url: string().optional().describe("Chat Completions base URL (add, protocol=openai)."),
5764
+ protocol: _enum(["anthropic", "openai"]).optional().describe("Protocol (add). Default: anthropic."),
5765
+ engine: _enum(["claude", "codex"]).optional().describe("Runner engine (add). Default: claude."),
5766
+ context_window: number().int().positive().optional().describe("Context window for the meter (add). Default: 200000."),
5767
+ key_file: string().optional().describe("File holding the API token, 0600 (add)."),
5768
+ key: string().optional().describe("Raw API token (add) — parked in ~/.config/pai/keys/<name>."),
5769
+ model: string().optional().describe("Default model id (add)."),
5770
+ fast_model: string().optional().describe("Cheaper model for spotchecks (add, optional)."),
5771
+ env: record(string(), string()).optional().describe("Extra env for runs (add, optional)."),
5772
+ note: string().optional().describe("Human note shown in listings (add, optional)."),
5773
+ quota_probe: string().optional().describe("URL whose JSON first number is the quota percent (add, optional).")
5774
+ }, async (args) => {
5775
+ try {
5776
+ const action = args.action ?? "list";
5777
+ if (action === "list") return workerText(describeProviders(readWorkersSection().workers).join("\n"));
5778
+ if (!args.name) return workerError(/* @__PURE__ */ new Error("name is required for this action"));
5779
+ if (action === "add") {
5780
+ if (!args.model) return workerError(/* @__PURE__ */ new Error("add needs model"));
5781
+ if (args.protocol !== "openai" && !args.base_url) return workerError(/* @__PURE__ */ new Error("add needs base_url (only protocol=openai goes without it)"));
5782
+ const workers = addProvider({
5783
+ name: args.name,
5784
+ baseUrl: args.base_url ?? "",
5785
+ keyFile: args.key_file ?? null,
5786
+ ...args.key !== void 0 ? { key: args.key } : {},
5787
+ model: args.model,
5788
+ ...args.fast_model ? { fastModel: args.fast_model } : {},
5789
+ env: args.env ?? {},
5790
+ ...args.note ? { note: args.note } : {},
5791
+ ...args.protocol ? { protocol: args.protocol } : {},
5792
+ ...args.upstream_url ? { upstreamUrl: args.upstream_url } : {},
5793
+ ...args.engine ? { engine: args.engine } : {},
5794
+ ...args.context_window ? { contextWindow: args.context_window } : {},
5795
+ ...args.quota_probe ? { quotaProbe: args.quota_probe } : {}
5796
+ });
5797
+ return workerText([`provider ${args.name} added; active: ${workers.active ?? "(none)"}; workers ${workers.enabled ? "on" : "off"}`, ...describeProviders(workers)].join("\n"));
5798
+ }
5799
+ if (action === "remove") {
5800
+ removeProvider(args.name);
5801
+ return workerText(`provider ${args.name} removed`);
5802
+ }
5803
+ if (action === "use") {
5804
+ useProvider(args.name);
5805
+ return workerText(`active provider: ${args.name}`);
5806
+ }
5807
+ if (action === "enable" || action === "disable") {
5808
+ setProviderEnabled(args.name, action === "enable");
5809
+ return workerText(`provider ${args.name} ${action}d`);
5810
+ }
5811
+ const { workers } = readWorkersSection();
5812
+ const p = workers.providers[args.name];
5813
+ if (!p) return workerError(/* @__PURE__ */ new Error(`no provider named "${args.name}"`));
5814
+ const r = await testProvider(args.name, p, workersLogDir(workers));
5815
+ if (r.skipped) return workerText(`${r.provider} ${r.model} ${r.skipped}\nreply: ${r.result.slice(0, 200)}`);
5816
+ return workerText(`${r.provider} ${r.model} ${(r.latencyMs / 1e3).toFixed(1)}s\nreply: ${r.result.slice(0, 200)}\n${r.ok ? "OK" : "FAILED"}`);
5817
+ } catch (e) {
5818
+ return workerError(e);
5819
+ }
5820
+ });
5821
+ server.tool("worker_roles", [
5822
+ "Manage worker roles (list, set, unset).",
5823
+ "",
5824
+ "A role maps a task class to a provider, optionally its fast model:",
5825
+ "implement=glm, research=glm, spotcheck=glm/fast.",
5826
+ "Runs pick the provider via --role first, then the active provider."
5827
+ ].join("\n"), {
5828
+ action: _enum([
5829
+ "list",
5830
+ "set",
5831
+ "unset"
5832
+ ]).optional().describe("Default: list."),
5833
+ role: string().optional().describe("Role name (set/unset)."),
5834
+ target: string().optional().describe("Provider or provider/fast (set).")
5835
+ }, async (args) => {
5836
+ try {
5837
+ const action = args.action ?? "list";
5838
+ if (action === "list") {
5839
+ const { workers } = readWorkersSection();
5840
+ const entries = Object.entries(workers.roles);
5841
+ const text = (t) => typeof t === "string" ? t : typeof t === "object" && t !== null ? `${t.provider ?? "?"}${t.mcp?.length ? ` +mcp(${t.mcp.join(",")})` : ""}` : String(t);
5842
+ return workerText(entries.length ? entries.map(([r, t]) => `${r}: ${text(t)}`).join("\n") : "no roles set");
5843
+ }
5844
+ if (!args.role) return workerError(/* @__PURE__ */ new Error("role is required for this action"));
5845
+ if (action === "set") {
5846
+ if (!args.target) return workerError(/* @__PURE__ */ new Error("set needs target provider[/fast]"));
5847
+ setRole(args.role, args.target);
5848
+ return workerText(`role ${args.role} → ${args.target}`);
5849
+ }
5850
+ unsetRole(args.role);
5851
+ return workerText(`role ${args.role} removed`);
5852
+ } catch (e) {
5853
+ return workerError(e);
5854
+ }
5855
+ });
5856
+ server.tool("worker_toggle", [
5857
+ "Turn worker routing on or off.",
5858
+ "",
5859
+ "off: the Agent-tool hook stops denying, subagents run on Anthropic.",
5860
+ "on: subagents are denied and rewritten to `pai worker run`."
5861
+ ].join("\n"), { enabled: boolean().describe("true = route subagents to workers, false = Anthropic.") }, async (args) => {
5862
+ try {
5863
+ const workers = setWorkersEnabled(args.enabled);
5864
+ return workerText(`workers ${workers.enabled ? "on" : "off"} — Agent subagents ${workers.enabled ? "denied and rewritten to `pai worker run`" : "run on Anthropic"}`);
5865
+ } catch (e) {
5866
+ return workerError(e);
5867
+ }
5868
+ });
5869
+ server.tool("worker_ps", [
5870
+ "List workers: running (id, provider, age, turns, current tool) and the last finished.",
5871
+ "",
5872
+ "Use this to check on delegated `pai worker run` calls; worker_replay shows",
5873
+ "the transcript of one worker."
5874
+ ].join("\n"), { all: boolean().optional().describe("Show workers of all sessions (default: this one's).") }, async (args) => {
5875
+ try {
5876
+ const { workers } = readWorkersSection();
5877
+ return workerText(psOutput(workersLogDir(workers), args.all === true, {}, false));
5878
+ } catch (e) {
5879
+ return workerError(e);
5880
+ }
5881
+ });
5882
+ server.tool("worker_replay", [
5883
+ "Replay the transcript of one worker: tool calls, short outputs, result.",
5884
+ "",
5885
+ "Plain text, no colors. Use tail to cap the output."
5886
+ ].join("\n"), {
5887
+ id: string().describe("Worker id (as shown by worker_ps)."),
5888
+ tail: number().int().min(1).max(2e3).optional().describe("Last N rendered lines. Default: all.")
5889
+ }, async (args) => {
5890
+ try {
5891
+ const { workers } = readWorkersSection();
5892
+ return workerText(replayOutput(workersLogDir(workers), args.id, false, args.tail));
5893
+ } catch (e) {
5894
+ return workerError(e);
5895
+ }
5896
+ });
5897
+ server.tool("worker_say", [
5898
+ "Send one message to a RUNNING worker: it lands on the worker's open stdin",
5899
+ "as a user message, mid-run, without breaking its stream.",
5900
+ "",
5901
+ "Fails with an explanation when the worker already finished — then use",
5902
+ "worker_resume (or `pai worker resume <id> \"text\"`) instead."
5903
+ ].join("\n"), {
5904
+ id: string().describe("Worker id (as shown by worker_ps)."),
5905
+ text: string().min(1).describe("The message to send (one line).")
5906
+ }, async (args) => {
5907
+ try {
5908
+ const { workers } = readWorkersSection();
5909
+ await sayToWorker(workersLogDir(workers), args.id, args.text);
5910
+ return workerText(`sent to ${args.id}`);
5911
+ } catch (e) {
5912
+ return workerError(e);
5913
+ }
5914
+ });
5915
+ server.tool("worker_resume", [
5916
+ "Continue a FINISHED worker with a follow-up message: claude --resume on the",
5917
+ "same provider, same Claude session, context intact.",
5918
+ "",
5919
+ "Returns the new worker id. Not for running workers — say to those instead."
5920
+ ].join("\n"), {
5921
+ id: string().describe("Worker id of the finished run (as shown by worker_ps)."),
5922
+ text: string().min(1).describe("The follow-up message.")
5923
+ }, async (args) => {
5924
+ try {
5925
+ const { workers } = readWorkersSection();
5926
+ const old = loadStatus(workersLogDir(workers), args.id);
5927
+ if (!old) return workerError(/* @__PURE__ */ new Error(`no worker named "${args.id}"`));
5928
+ if (old.state === "running") return workerError(/* @__PURE__ */ new Error(`worker ${args.id} is still running — send messages with worker_say`));
5929
+ if (!old.claudeSession) return workerError(/* @__PURE__ */ new Error(`worker ${args.id} recorded no Claude session id — it predates resume support or ran through an engine that does not expose one`));
5930
+ let newId = "";
5931
+ const rc = await runWorker({
5932
+ providerFlag: old.provider,
5933
+ modelFlag: old.model,
5934
+ label: `↩ ${old.label}`,
5935
+ noPane: true,
5936
+ claudeArgs: [
5937
+ "--resume",
5938
+ old.claudeSession,
5939
+ "-p",
5940
+ args.text
5941
+ ],
5942
+ onWorkerStart: (wid) => {
5943
+ newId = wid;
5944
+ }
5945
+ });
5946
+ return workerText(`resumed ${args.id} as ${newId || "(unknown id)"} — rc=${rc}\ncheck on it with worker_ps / worker_replay`);
5947
+ } catch (e) {
5948
+ return workerError(e);
5949
+ }
5950
+ });
5524
5951
  const transport = new StdioServerTransport();
5525
5952
  await server.connect(transport);
5526
5953
  }