@tekmidian/pai 0.39.0 → 0.40.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 (126) hide show
  1. package/README.md +41 -0
  2. package/dist/{auto-route-BlWOWT4z.mjs → auto-route-BwzmlW3W.mjs} +2 -2
  3. package/dist/{auto-route-BlWOWT4z.mjs.map → auto-route-BwzmlW3W.mjs.map} +1 -1
  4. package/dist/{chain-CTtHligI.mjs → chain-DhVHVnmT.mjs} +263 -791
  5. package/dist/chain-DhVHVnmT.mjs.map +1 -0
  6. package/dist/cli/index.mjs +10 -8
  7. package/dist/cli/index.mjs.map +1 -1
  8. package/dist/cli/program.mjs +10 -8
  9. package/dist/{clusters-wlK0w41E.mjs → clusters-DRHhfli9.mjs} +1 -1
  10. package/dist/{clusters-wlK0w41E.mjs.map → clusters-DRHhfli9.mjs.map} +1 -1
  11. package/dist/{config-B64vFg14.mjs → config-D1G9IFpn.mjs} +50 -3
  12. package/dist/config-D1G9IFpn.mjs.map +1 -0
  13. package/dist/{config-C_ErGddD.mjs → config-DD52BPB-.mjs} +1 -1
  14. package/dist/config-Dl8lT4Lu.mjs +527 -0
  15. package/dist/config-Dl8lT4Lu.mjs.map +1 -0
  16. package/dist/{context-handover-cache-BpUojjsi.mjs → context-handover-cache-C6_rs7JT.mjs} +112 -24
  17. package/dist/context-handover-cache-C6_rs7JT.mjs.map +1 -0
  18. package/dist/daemon/index.mjs +12 -10
  19. package/dist/daemon/index.mjs.map +1 -1
  20. package/dist/daemon-BSL9sXxm.mjs +22 -0
  21. package/dist/{daemon-CTkN_Vn8.mjs → daemon-CpdVUnl_.mjs} +26 -26
  22. package/dist/{daemon-CTkN_Vn8.mjs.map → daemon-CpdVUnl_.mjs.map} +1 -1
  23. package/dist/daemon-mcp/index.mjs +127 -6
  24. package/dist/daemon-mcp/index.mjs.map +1 -1
  25. package/dist/detector-BLVOCiJ_.mjs +5 -0
  26. package/dist/{detector-Bwk_4Pk4.mjs → detector-smpCm7ku.mjs} +1 -1
  27. package/dist/{detector-Bwk_4Pk4.mjs.map → detector-smpCm7ku.mjs.map} +1 -1
  28. package/dist/{factory-A9x-T2Kg.mjs → factory-Bc5EyN54.mjs} +7 -7
  29. package/dist/{factory-A9x-T2Kg.mjs.map → factory-Bc5EyN54.mjs.map} +1 -1
  30. package/dist/factory-C7ua_3ik.mjs +3 -0
  31. package/dist/{providers-FYkZjn_C.mjs → fallback-CdXv-Np0.mjs} +411 -51
  32. package/dist/fallback-CdXv-Np0.mjs.map +1 -0
  33. package/dist/hooks/context-compression-hook.mjs +136 -22
  34. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  35. package/dist/hooks/post-compact-inject.mjs +98 -7
  36. package/dist/hooks/post-compact-inject.mjs.map +4 -4
  37. package/dist/hooks/route-agents-to-worker.mjs +75 -6
  38. package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
  39. package/dist/hooks/worker-proxy.mjs +75 -6
  40. package/dist/hooks/worker-proxy.mjs.map +2 -2
  41. package/dist/hooks/worker-status-line.mjs +149 -90
  42. package/dist/hooks/worker-status-line.mjs.map +4 -4
  43. package/dist/{indexer-backend-TG64CCQC.mjs → indexer-backend-rohVVKrA.mjs} +1 -1
  44. package/dist/{indexer-backend-TG64CCQC.mjs.map → indexer-backend-rohVVKrA.mjs.map} +1 -1
  45. package/dist/{ipc-client-BmypMNYk.mjs → ipc-client-C7AVpsyv.mjs} +2 -2
  46. package/dist/{ipc-client-BmypMNYk.mjs.map → ipc-client-C7AVpsyv.mjs.map} +1 -1
  47. package/dist/{kg-entity-r8duqhi9.mjs → kg-entity-DbOMPdF9.mjs} +1 -1
  48. package/dist/{kg-entity-r8duqhi9.mjs.map → kg-entity-DbOMPdF9.mjs.map} +1 -1
  49. package/dist/{latent-ideas-B7wq75Pt.mjs → latent-ideas-iNp5s55R.mjs} +2 -2
  50. package/dist/{latent-ideas-B7wq75Pt.mjs.map → latent-ideas-iNp5s55R.mjs.map} +1 -1
  51. package/dist/{link-boost-HkG7JWZR.mjs → link-boost-NNzADHX_.mjs} +1 -1
  52. package/dist/{link-boost-HkG7JWZR.mjs.map → link-boost-NNzADHX_.mjs.map} +1 -1
  53. package/dist/{main-resolver-DlaLOFBA.mjs → main-resolver-CM1IHbuu.mjs} +78 -75
  54. package/dist/main-resolver-CM1IHbuu.mjs.map +1 -0
  55. package/dist/main-resolver-E5WlilhM.mjs +6 -0
  56. package/dist/{neighborhood-lThN-MaQ.mjs → neighborhood-AGL7oXQ2.mjs} +1 -1
  57. package/dist/{neighborhood-lThN-MaQ.mjs.map → neighborhood-AGL7oXQ2.mjs.map} +1 -1
  58. package/dist/{note-context-b6k0mAKi.mjs → note-context-BHwYpSFf.mjs} +1 -1
  59. package/dist/{note-context-b6k0mAKi.mjs.map → note-context-BHwYpSFf.mjs.map} +1 -1
  60. package/dist/{planner-BDI7bE9B.mjs → planner-Cm3g6fWH.mjs} +5 -3
  61. package/dist/{planner-BDI7bE9B.mjs.map → planner-Cm3g6fWH.mjs.map} +1 -1
  62. package/dist/{postgres-mW1n7Vi1.mjs → postgres-Dwxe290H.mjs} +1 -1
  63. package/dist/{postgres-mW1n7Vi1.mjs.map → postgres-Dwxe290H.mjs.map} +1 -1
  64. package/dist/{program-DXBwJV7h.mjs → program-AR0qlYRg.mjs} +334 -78
  65. package/dist/program-AR0qlYRg.mjs.map +1 -0
  66. package/dist/query-feedback-CGcEiGtY.mjs +3 -0
  67. package/dist/{query-feedback-BV4CcxqS.mjs → query-feedback-DZfjI34w.mjs} +1 -1
  68. package/dist/{query-feedback-BV4CcxqS.mjs.map → query-feedback-DZfjI34w.mjs.map} +1 -1
  69. package/dist/router-CAtyVQud.mjs +3 -0
  70. package/dist/{router-DcHKnEPa.mjs → router-DHracTUC.mjs} +1 -1
  71. package/dist/{router-DcHKnEPa.mjs.map → router-DHracTUC.mjs.map} +1 -1
  72. package/dist/server-BOuAOj9b.mjs +386 -0
  73. package/dist/server-BOuAOj9b.mjs.map +1 -0
  74. package/dist/skills/Worker/SKILL.md +19 -1
  75. package/dist/{sources-kLnQsNrW.mjs → sources-ju6AA7lL.mjs} +1 -1
  76. package/dist/{sources-kLnQsNrW.mjs.map → sources-ju6AA7lL.mjs.map} +1 -1
  77. package/dist/{sqlite-BenGr3UP.mjs → sqlite-kFZeWCJ2.mjs} +1 -1
  78. package/dist/{sqlite-BenGr3UP.mjs.map → sqlite-kFZeWCJ2.mjs.map} +1 -1
  79. package/dist/{state-Ca9F_sZc.mjs → state-DH_mp5Xe.mjs} +1 -1
  80. package/dist/{state-CAeyOdfq.mjs → state-ahFAbD8b.mjs} +1 -1
  81. package/dist/{state-CAeyOdfq.mjs.map → state-ahFAbD8b.mjs.map} +1 -1
  82. package/dist/{themes-BI4GMRP9.mjs → themes-DqhNUUQk.mjs} +1 -1
  83. package/dist/{themes-BI4GMRP9.mjs.map → themes-DqhNUUQk.mjs.map} +1 -1
  84. package/dist/{tools-DGcB3o_S.mjs → tools-CMlHa9lZ.mjs} +14 -14
  85. package/dist/{tools-DGcB3o_S.mjs.map → tools-CMlHa9lZ.mjs.map} +1 -1
  86. package/dist/{tools-Bp7hj6OW.mjs → tools-COna9vZR.mjs} +1 -1
  87. package/dist/{trace-bobARFEX.mjs → trace-DTP6k4B_.mjs} +1 -1
  88. package/dist/{trace-bobARFEX.mjs.map → trace-DTP6k4B_.mjs.map} +1 -1
  89. package/dist/{vault-indexer-C3OfXTNF.mjs → vault-indexer-YMH22mLU.mjs} +1 -1
  90. package/dist/{vault-indexer-C3OfXTNF.mjs.map → vault-indexer-YMH22mLU.mjs.map} +1 -1
  91. package/dist/{work-queue-worker-R7UGIag_.mjs → work-queue-worker-B3nl8yyn.mjs} +23 -26
  92. package/dist/work-queue-worker-B3nl8yyn.mjs.map +1 -0
  93. package/dist/work-queue-worker-BM1GcAAq.mjs +14 -0
  94. package/dist/{zettelkasten-m5QPtb-L.mjs → zettelkasten-DDvKRm2V.mjs} +3 -3
  95. package/dist/{zettelkasten-m5QPtb-L.mjs.map → zettelkasten-DDvKRm2V.mjs.map} +1 -1
  96. package/docs/commands/README.md +5 -2
  97. package/docs/commands/worker.md +66 -5
  98. package/docs/images/provider-abstraction-live.png +0 -0
  99. package/docs/images/workers.png +0 -0
  100. package/docs/launcher-independence.md +70 -0
  101. package/docs/provider-abstraction.md +87 -0
  102. package/docs/provider-independence-details.md +330 -0
  103. package/docs/provider-independence.md +75 -0
  104. package/docs/worker.md +72 -11
  105. package/package.json +1 -1
  106. package/scripts/build-hooks.mjs +6 -1
  107. package/scripts/build-skill-stubs.mjs +17 -4
  108. package/src/hooks/ts/lib/context-fill.test.ts +158 -4
  109. package/src/hooks/ts/lib/context-fill.ts +102 -35
  110. package/statusline-command.sh +120 -21
  111. package/dist/chain-CTtHligI.mjs.map +0 -1
  112. package/dist/config-B64vFg14.mjs.map +0 -1
  113. package/dist/context-handover-cache-BpUojjsi.mjs.map +0 -1
  114. package/dist/daemon-CmHHmKpg.mjs +0 -20
  115. package/dist/detector-DO730Zq0.mjs +0 -5
  116. package/dist/factory-BD-np0Vd.mjs +0 -3
  117. package/dist/main-resolver-D6IImXvF.mjs +0 -4
  118. package/dist/main-resolver-DlaLOFBA.mjs.map +0 -1
  119. package/dist/program-DXBwJV7h.mjs.map +0 -1
  120. package/dist/providers-FYkZjn_C.mjs.map +0 -1
  121. package/dist/query-feedback-DSVyHtrG.mjs +0 -3
  122. package/dist/router-Bk77E7hj.mjs +0 -3
  123. package/dist/runtime-paths-rni52zHX.mjs +0 -50
  124. package/dist/runtime-paths-rni52zHX.mjs.map +0 -1
  125. package/dist/work-queue-worker-DW8lz-Oo.mjs +0 -11
  126. package/dist/work-queue-worker-R7UGIag_.mjs.map +0 -1
@@ -7,15 +7,16 @@ import { i as indexProject, n as indexAll, t as embedChunks } from "./sync-BWbe8
7
7
  import { t as STOP_WORDS } from "./stop-words-Hfu8u22w.mjs";
8
8
  import { a as searchMemory, i as populateSlugs } from "./search-Rpk1cSBC.mjs";
9
9
  import { n as formatDetection, r as formatDetectionJson, t as detectProject } from "./detect-CdaA48EI.mjs";
10
- import { _ as findMovedProjects, a as findLatestNote, c as extractAndStoreTriples, f as resolveHome, g as claudeProjectsDir, h as upsertSession, i as applyContinue, l as cmdScan, m as upsertProject, o as findNotesDir$1, p as saveScanConfig, r as appendCheckpointToNote, s as readBodyFile, t as readContextHandoverCache, u as loadScanConfig, v as scanTranscriptFolders, y as transcriptFiles } from "./context-handover-cache-BpUojjsi.mjs";
11
- import { a as schedulerLogPath, i as paiSocketPath, n as daemonLogPath, r as daemonPidPath } from "./runtime-paths-rni52zHX.mjs";
12
- import { t as PaiClient } from "./ipc-client-BmypMNYk.mjs";
13
- import { a as expandHome, c as UNROUTED, i as ensureConfigDir, n as CONFIG_FILE$2, o as loadConfig, s as OWNER_LABEL_PREFIX, t as CONFIG_DIR } from "./config-B64vFg14.mjs";
14
- import { r as humanDuration, t as createStorageBackend } from "./factory-A9x-T2Kg.mjs";
15
- import { B as workersLogDir, H as PROVIDER_TAGS, I as parseRunnerArgs, J as readWorkersSection, R as eventsPath, U as WorkersConfigError, X as readJsonStrict, Z as writeJsonAtomic, a as testProvider, b as openPaneForWorker, c as stopProxy, f as discardWorker, g as sayToWorker, i as runWorker, j as loadStatus, l as describeMcp, m as handoffFromInside, o as DEFAULT_PROXY_PORT, p as mergeWorker, s as ensureProxyRunning, t as runChain, v as checkPaneForWorker, y as openFollowPane, z as ledgerPath } from "./chain-CTtHligI.mjs";
16
- import { s as kgQuery } from "./kg-entity-r8duqhi9.mjs";
17
- import { _ as scanSessions, a as renderDedupedSessions, c as probeResume, d as callAiBroker, f as fetchLiveSessions, g as resolveSessionByNameOrId, h as fmtAge, i as normalizeName$2, l as restoreTopLevel, m as sendToSession, o as hasConversation, p as revealItermSession, r as buildDeduped, s as launchInDir, t as cmdMain, u as printExitDir } from "./main-resolver-DlaLOFBA.mjs";
18
- import { a as setClass, c as unsetClass, d as followWorkers, f as psOutput, i as removeProvider, l as updateProvider, m as statusLineOutput, n as classTargetText, o as setProviderEnabled, p as replayOutput, r as describeProviders, s as setWorkersEnabled, t as addProvider, u as useProvider } from "./providers-FYkZjn_C.mjs";
10
+ import { _ as claudeProjectsDir, a as findLatestNote, b as transcriptFiles, c as extractAndStoreTriples, d as loadScanConfig, g as upsertSession, h as upsertProject, i as applyContinue, m as saveScanConfig, o as findNotesDir$1, p as resolveHome, r as appendCheckpointToNote, s as readBodyFile, t as readContextHandoverCache, u as cmdScan, v as findMovedProjects, y as scanTranscriptFolders } from "./context-handover-cache-C6_rs7JT.mjs";
11
+ import { a as expandHome, c as UNROUTED, d as daemonLogPath, f as daemonPidPath, i as ensureConfigDir, m as schedulerLogPath, n as CONFIG_FILE$2, o as loadConfig, p as paiSocketPath, s as OWNER_LABEL_PREFIX, t as CONFIG_DIR } from "./config-D1G9IFpn.mjs";
12
+ import { t as PaiClient } from "./ipc-client-C7AVpsyv.mjs";
13
+ import { r as humanDuration, t as createStorageBackend } from "./factory-Bc5EyN54.mjs";
14
+ import { _ as DEFAULT_CONTEXT_WINDOW, a as WorkersConfigError, b as readJsonStrict, l as isModelCapability, m as readWorkersSection, r as PROVIDER_TAGS, v as contextWindowFromModelId, x as writeJsonAtomic, y as stripModelVariant } from "./config-Dl8lT4Lu.mjs";
15
+ import { s as kgQuery } from "./kg-entity-DbOMPdF9.mjs";
16
+ import { _ as workersLogDir, f as eventsPath, n as ensureProxyRunning, p as ledgerPath, r as stopProxy, t as DEFAULT_PROXY_PORT } from "./server-BOuAOj9b.mjs";
17
+ import { _ as scanSessions, a as renderDedupedSessions, c as probeResume, d as callAiBroker, f as fetchLiveSessions, g as resolveSessionByNameOrId, h as fmtAge, i as normalizeName$2, l as restoreTopLevel, m as sendToSession, o as hasConversation, p as revealItermSession, r as buildDeduped, s as launchInDir, t as cmdMain, u as printExitDir } from "./main-resolver-CM1IHbuu.mjs";
18
+ import { D as loadStatus, M as parseRunnerArgs, S as UNLABELED, _ as openPaneForWorker, a as testProvider, d as handoffFromInside, g as openFollowPane, h as checkPaneForWorker, i as runWorker, l as discardWorker, o as describeMcp, p as sayToWorker, s as parseWorkerReport, t as runChain, u as mergeWorker } from "./chain-DhVHVnmT.mjs";
19
+ import { S as statusLineOutput, _ as updateProvider, a as addProvider, b as psOutput, c as describeProviders, d as resolveProviderName, f as setClass, g as unsetClass, h as setWorkersEnabled, i as fallbackStatusText, l as modelPrefsText, m as setProviderModel, n as fallbackOn, o as classTargetText, p as setProviderEnabled, r as fallbackStatus, s as describeModels, t as fallbackOff, u as removeProvider, v as useProvider, x as replayOutput, y as followWorkers } from "./fallback-CdXv-Np0.mjs";
19
20
  import { appendFileSync, chmodSync, copyFileSync, createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
20
21
  import { homedir, platform, tmpdir } from "node:os";
21
22
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
@@ -1890,7 +1891,7 @@ function cmdUnregister(db, slug, opts = {}) {
1890
1891
  //#region src/cli/commands/project/projects-index.ts
1891
1892
  function registerProjectsCommands(projectsCmd, getDb) {
1892
1893
  projectsCmd.command("list", { isDefault: true }).description("Show the unified deduped listing (same as `pai`).\nUse --all to include cold / zero-session / archived projects.").option("--all", "Show all entries including cold / zero-session / archived projects").option("-n, --n <count>", "Max rows to show", "20").action(async (opts) => {
1893
- const { cmdMain } = await import("./main-resolver-D6IImXvF.mjs");
1894
+ const { cmdMain } = await import("./main-resolver-E5WlilhM.mjs");
1894
1895
  await cmdMain(getDb(), void 0, void 0, opts);
1895
1896
  });
1896
1897
  projectsCmd.command("list-raw").description("Raw registry listing with slug, path, status, last_active columns").option("--all", "Include archived projects (default: active only)").option("--status <status>", "Filter by status: active | archived").option("--tag <tag>", "Filter by tag").option("--type <type>", "Filter by type").action((opts) => {
@@ -2962,9 +2963,9 @@ function registerStatsCommands(memoryCmd, getDb) {
2962
2963
  //#region src/cli/commands/memory/sources-cmd.ts
2963
2964
  function registerSourcesCommand(memoryCmd) {
2964
2965
  memoryCmd.command("sources").description("Show what the indexer has taken in: composition by source, which roots\ncontent enters from, the heaviest single files, and how many chunks are\nrewritten per day (which distinguishes a finite backlog from a treadmill).").option("--limit <n>", "Rows per section (default 8)", "8").action(async (opts) => {
2965
- const { createStorageBackend } = await import("./factory-BD-np0Vd.mjs");
2966
- const { loadConfig } = await import("./config-C_ErGddD.mjs");
2967
- const { cmdMemorySources } = await import("./sources-kLnQsNrW.mjs");
2966
+ const { createStorageBackend } = await import("./factory-C7ua_3ik.mjs");
2967
+ const { loadConfig } = await import("./config-DD52BPB-.mjs");
2968
+ const { cmdMemorySources } = await import("./sources-ju6AA7lL.mjs");
2968
2969
  let backend;
2969
2970
  try {
2970
2971
  backend = await createStorageBackend(loadConfig());
@@ -3651,8 +3652,8 @@ function cmdLogs(opts) {
3651
3652
  }
3652
3653
  function registerDaemonCommands(daemonCmd) {
3653
3654
  daemonCmd.command("serve").description("Start the PAI daemon in the foreground").action(async () => {
3654
- const { serve } = await import("./daemon-CmHHmKpg.mjs");
3655
- const { loadConfig: lc, ensureConfigDir } = await import("./config-C_ErGddD.mjs");
3655
+ const { serve } = await import("./daemon-BSL9sXxm.mjs");
3656
+ const { loadConfig: lc, ensureConfigDir } = await import("./config-DD52BPB-.mjs");
3656
3657
  ensureConfigDir();
3657
3658
  await serve(lc());
3658
3659
  });
@@ -7229,7 +7230,7 @@ async function cmdExplore(note, opts) {
7229
7230
  const depth = parseInt(opts.depth ?? "3", 10);
7230
7231
  const direction = opts.direction ?? "both";
7231
7232
  const mode = opts.mode ?? "all";
7232
- const { zettelExplore } = await import("./zettelkasten-m5QPtb-L.mjs");
7233
+ const { zettelExplore } = await import("./zettelkasten-DDvKRm2V.mjs");
7233
7234
  const result = zettelExplore(getFedDb(), {
7234
7235
  startNote: note,
7235
7236
  depth,
@@ -7294,7 +7295,7 @@ async function cmdHealth(opts) {
7294
7295
  const projectPath = opts.project;
7295
7296
  const recentDays = parseInt(opts.days ?? "30", 10);
7296
7297
  const includeTypes = opts.include ? opts.include.split(",").map((s) => s.trim()) : void 0;
7297
- const { zettelHealth } = await import("./zettelkasten-m5QPtb-L.mjs");
7298
+ const { zettelHealth } = await import("./zettelkasten-DDvKRm2V.mjs");
7298
7299
  const result = zettelHealth(getFedDb(), {
7299
7300
  scope,
7300
7301
  projectPath,
@@ -7365,7 +7366,7 @@ async function cmdSurprise(note, opts) {
7365
7366
  const limit = parseInt(opts.limit ?? "10", 10);
7366
7367
  const minSimilarity = parseFloat(opts.minSimilarity ?? "0.3");
7367
7368
  const minGraphDistance = parseInt(opts.minDistance ?? "3", 10);
7368
- const { zettelSurprise } = await import("./zettelkasten-m5QPtb-L.mjs");
7369
+ const { zettelSurprise } = await import("./zettelkasten-DDvKRm2V.mjs");
7369
7370
  const db = getFedDb();
7370
7371
  console.log();
7371
7372
  console.log(header(" PAI Zettel Surprise"));
@@ -7420,7 +7421,7 @@ async function cmdSuggest(note, opts) {
7420
7421
  const vaultProjectId = parseInt(opts.vaultProjectId, 10);
7421
7422
  const limit = parseInt(opts.limit ?? "5", 10);
7422
7423
  const excludeLinked = opts.excludeLinked !== false;
7423
- const { zettelSuggest } = await import("./zettelkasten-m5QPtb-L.mjs");
7424
+ const { zettelSuggest } = await import("./zettelkasten-DDvKRm2V.mjs");
7424
7425
  const db = getFedDb();
7425
7426
  console.log();
7426
7427
  console.log(header(" PAI Zettel Suggest"));
@@ -7473,7 +7474,7 @@ async function cmdConverse(question, opts) {
7473
7474
  const vaultProjectId = parseInt(opts.vaultProjectId, 10);
7474
7475
  const depth = parseInt(opts.depth ?? "2", 10);
7475
7476
  const limit = parseInt(opts.limit ?? "15", 10);
7476
- const { zettelConverse } = await import("./zettelkasten-m5QPtb-L.mjs");
7477
+ const { zettelConverse } = await import("./zettelkasten-DDvKRm2V.mjs");
7477
7478
  const db = getFedDb();
7478
7479
  console.log();
7479
7480
  console.log(header(" PAI Zettel Converse"));
@@ -7536,7 +7537,7 @@ async function cmdThemes(opts) {
7536
7537
  const minClusterSize = parseInt(opts.minSize ?? "3", 10);
7537
7538
  const maxThemes = parseInt(opts.maxThemes ?? "10", 10);
7538
7539
  const similarityThreshold = parseFloat(opts.threshold ?? "0.65");
7539
- const { zettelThemes } = await import("./zettelkasten-m5QPtb-L.mjs");
7540
+ const { zettelThemes } = await import("./zettelkasten-DDvKRm2V.mjs");
7540
7541
  const db = getFedDb();
7541
7542
  console.log();
7542
7543
  console.log(header(" PAI Zettel Themes"));
@@ -9111,8 +9112,8 @@ async function escalate(task, headline, detail, state, now, opts) {
9111
9112
  state.alarmedAt[task.id] = now;
9112
9113
  let delivered = false;
9113
9114
  try {
9114
- const { routeNotification } = await import("./router-Bk77E7hj.mjs");
9115
- const { loadConfig } = await import("./config-C_ErGddD.mjs");
9115
+ const { routeNotification } = await import("./router-CAtyVQud.mjs");
9116
+ const { loadConfig } = await import("./config-DD52BPB-.mjs");
9116
9117
  delivered = (await routeNotification({
9117
9118
  event: "error",
9118
9119
  title: `PAI: ${headline}`,
@@ -11728,10 +11729,10 @@ function cmdActive(db, opts) {
11728
11729
  ], rows));
11729
11730
  }
11730
11731
  async function cmdAutoRoute(opts) {
11731
- const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-BlWOWT4z.mjs");
11732
+ const { autoRoute, formatAutoRoute, formatAutoRouteJson } = await import("./auto-route-BwzmlW3W.mjs");
11732
11733
  const { openRegistry } = await import("./db-XEwJbuGO.mjs");
11733
- const { createStorageBackend } = await import("./factory-BD-np0Vd.mjs");
11734
- const { loadConfig } = await import("./config-C_ErGddD.mjs");
11734
+ const { createStorageBackend } = await import("./factory-C7ua_3ik.mjs");
11735
+ const { loadConfig } = await import("./config-DD52BPB-.mjs");
11735
11736
  const config = loadConfig();
11736
11737
  const registryDb = openRegistry();
11737
11738
  const federation = await createStorageBackend(config);
@@ -12393,9 +12394,6 @@ function cmdHandover(db, projectSlug, numberOrLatest, sessionId) {
12393
12394
  * decision that depends on this number (the compaction handover chief among
12394
12395
  * them) for the entire session.
12395
12396
  */
12396
- /** Fallback window size when nothing on hand reports one. Matches the
12397
- * default statusline-command.sh already falls back to. */
12398
- const DEFAULT_CONTEXT_WINDOW = 2e5;
12399
12397
  /** How many trailing lines of the transcript to scan for a usage entry.
12400
12398
  * A single turn is rarely more than a handful of JSONL lines (assistant
12401
12399
  * text + tool_use/tool_result pairs), so 40 comfortably covers one turn
@@ -12458,16 +12456,22 @@ function usageTotal(usage) {
12458
12456
  * `message.usage` entry, read from the end backwards so a trailing line with
12459
12457
  * no usage field (a plain text turn, a tool_result) doesn't hide one just
12460
12458
  * before it.
12459
+ *
12460
+ * The window size, when the caller has none, is derived from the transcript's
12461
+ * own last assistant model id ("glm-5.3[1m]" → 1,000,000) before falling back
12462
+ * to the assumed default — a session whose model declares its window should
12463
+ * not be measured against a guess.
12461
12464
  */
12462
- function contextFillFromTranscript(transcriptPath, windowSize = DEFAULT_CONTEXT_WINDOW) {
12463
- if (!transcriptPath || !existsSync(transcriptPath)) return unknownReading(windowSize);
12465
+ function contextFillFromTranscript(transcriptPath, windowSize) {
12466
+ if (!transcriptPath || !existsSync(transcriptPath)) return unknownReading(windowSize ?? DEFAULT_CONTEXT_WINDOW);
12464
12467
  let raw;
12465
12468
  try {
12466
12469
  raw = readFileSync(transcriptPath, "utf-8");
12467
12470
  } catch {
12468
- return unknownReading(windowSize);
12471
+ return unknownReading(windowSize ?? DEFAULT_CONTEXT_WINDOW);
12469
12472
  }
12470
12473
  const tail = raw.trim().split("\n").filter((l) => l.trim()).slice(-TRANSCRIPT_TAIL_LINES);
12474
+ let lastModel = null;
12471
12475
  for (let i = tail.length - 1; i >= 0; i--) {
12472
12476
  let entry;
12473
12477
  try {
@@ -12475,28 +12479,32 @@ function contextFillFromTranscript(transcriptPath, windowSize = DEFAULT_CONTEXT_
12475
12479
  } catch {
12476
12480
  continue;
12477
12481
  }
12482
+ if (entry?.type === "assistant" && lastModel === null) {
12483
+ const model = entry.message?.model;
12484
+ if (typeof model === "string" && model !== "") lastModel = model;
12485
+ }
12478
12486
  const usage = entry?.message?.usage;
12479
12487
  if (usage && typeof usage === "object") {
12488
+ const window = windowSize ?? contextWindowFromModelId(lastModel) ?? DEFAULT_CONTEXT_WINDOW;
12480
12489
  const usedTokens = usageTotal(usage);
12481
12490
  return {
12482
12491
  status: "ok",
12483
12492
  usedTokens,
12484
- windowSize,
12485
- fraction: usedTokens / windowSize,
12493
+ windowSize: window,
12494
+ fraction: usedTokens / window,
12486
12495
  source: "transcript"
12487
12496
  };
12488
12497
  }
12489
12498
  }
12490
- return unknownReading(windowSize);
12499
+ return unknownReading(windowSize ?? contextWindowFromModelId(lastModel) ?? DEFAULT_CONTEXT_WINDOW);
12491
12500
  }
12492
12501
  function getContextFill(input, now = Date.now()) {
12493
- const windowSize = input.windowSize ?? DEFAULT_CONTEXT_WINDOW;
12494
12502
  if (input.sessionId) {
12495
12503
  const fromStatusline = readStatuslineFill(input.sessionId, now);
12496
12504
  if (fromStatusline) return fromStatusline;
12497
12505
  }
12498
- if (input.transcriptPath) return contextFillFromTranscript(input.transcriptPath, windowSize);
12499
- return unknownReading(windowSize);
12506
+ if (input.transcriptPath) return contextFillFromTranscript(input.transcriptPath, input.windowSize);
12507
+ return unknownReading(input.windowSize ?? DEFAULT_CONTEXT_WINDOW);
12500
12508
  }
12501
12509
  const DEFAULT_AUTOCOMPACT_PCT = 80;
12502
12510
  /** How many of the most recent compact_boundary events to consider, and to
@@ -12543,13 +12551,42 @@ function listProjectTranscripts(cwd, projectsDir) {
12543
12551
  const SYNTHETIC_MODEL = "<synthetic>";
12544
12552
  function modelFamily(model) {
12545
12553
  if (typeof model !== "string" || model === "" || model === SYNTHETIC_MODEL) return "unknown";
12546
- return model.startsWith("claude-") ? "claude" : "foreign";
12554
+ const base = stripModelVariant(model);
12555
+ return base.startsWith("claude-") ? "claude" : base;
12556
+ }
12557
+ /**
12558
+ * Is a model id foreign to this machine's sessions? A model is native when it
12559
+ * is a Claude model or one the worker registry configures (any provider's
12560
+ * default or fast alias — the models this stack actually runs on). Anything
12561
+ * else, with a readable model id, is foreign. Doubt (no id, unreadable
12562
+ * registry) reads as native: the callers that skip work on "foreign" must not
12563
+ * skip it on doubt.
12564
+ */
12565
+ function isForeignModelFamily(family, nativeFamilies) {
12566
+ if (family === "unknown" || family === "claude") return false;
12567
+ return !(nativeFamilies ?? nativeModelFamilies()).has(family);
12568
+ }
12569
+ /** Families of every model id the worker registry configures. Never throws:
12570
+ * an unreadable or missing registry means "nothing configured", which leaves
12571
+ * every non-Claude family foreign — the historical behaviour. */
12572
+ function nativeModelFamilies(configPath) {
12573
+ const families = /* @__PURE__ */ new Set();
12574
+ try {
12575
+ for (const p of Object.values(readWorkersSection(configPath).workers.providers)) {
12576
+ families.add(modelFamily(p.models.default));
12577
+ if (p.models.fast) families.add(modelFamily(p.models.fast));
12578
+ }
12579
+ } catch {
12580
+ return families;
12581
+ }
12582
+ return families;
12547
12583
  }
12548
12584
  /**
12549
12585
  * The family of the LAST assistant model in a transcript — scanned from the
12550
12586
  * end so a long transcript costs one read and a few lines of parsing.
12551
- * Unreadable or model-less transcripts are "unknown", never "foreign":
12552
- * the callers that skip work on "foreign" must not skip it on doubt.
12587
+ * Unreadable or model-less transcripts are "unknown", never foreign:
12588
+ * the callers that skip work on foreign models must not skip it on doubt.
12589
+ * Pair with isForeignModelFamily to judge the key.
12553
12590
  */
12554
12591
  function transcriptModelFamily(path) {
12555
12592
  let raw;
@@ -12580,7 +12617,7 @@ function transcriptModelFamily(path) {
12580
12617
  * the event's uuid (falling back to a timestamp+preTokens key for the rare
12581
12618
  * line with no uuid) so an event mirrored into `sessions/` is counted once.
12582
12619
  */
12583
- function readCompactBoundarySamples(cwd, projectsDir) {
12620
+ function readCompactBoundarySamples(cwd, projectsDir, nativeFamilies) {
12584
12621
  const byKey = /* @__PURE__ */ new Map();
12585
12622
  for (const path of listProjectTranscripts(cwd, projectsDir)) {
12586
12623
  let raw;
@@ -12609,7 +12646,7 @@ function readCompactBoundarySamples(cwd, projectsDir) {
12609
12646
  if (typeof preTokens !== "number" || !Number.isFinite(preTokens)) continue;
12610
12647
  const timestampMs = entry.timestamp ? Date.parse(entry.timestamp) : NaN;
12611
12648
  if (!Number.isFinite(timestampMs)) continue;
12612
- if (modelFamily(lastModel) === "foreign") {
12649
+ if (isForeignModelFamily(modelFamily(lastModel), nativeFamilies)) {
12613
12650
  foreignDiscards++;
12614
12651
  continue;
12615
12652
  }
@@ -12621,7 +12658,7 @@ function readCompactBoundarySamples(cwd, projectsDir) {
12621
12658
  uuid: entry.uuid
12622
12659
  });
12623
12660
  }
12624
- if (foreignDiscards > 0) console.error(`[context-fill] ignored ${foreignDiscards} compaction sample(s) from a non-Claude transcript (model=${lastModel}): ${path}`);
12661
+ if (foreignDiscards > 0) console.error(`[context-fill] ignored ${foreignDiscards} compaction sample(s) from a foreign-model transcript (model=${lastModel}): ${path}`);
12625
12662
  }
12626
12663
  return [...byKey.values()].sort((a, b) => b.timestampMs - a.timestampMs);
12627
12664
  }
@@ -12632,9 +12669,9 @@ function readCompactBoundarySamples(cwd, projectsDir) {
12632
12669
  * checkable: print these and the timestamps prove which three events
12633
12670
  * produced it, rather than asking for trust.
12634
12671
  */
12635
- function selectedCompactionSamples(cwd, projectsDir = CLAUDE_PROJECTS_DIR) {
12672
+ function selectedCompactionSamples(cwd, projectsDir = CLAUDE_PROJECTS_DIR, configPath) {
12636
12673
  if (!cwd) return [];
12637
- return readCompactBoundarySamples(cwd, projectsDir).slice(0, MEASURED_TRIGGER_SAMPLE_SIZE);
12674
+ return readCompactBoundarySamples(cwd, projectsDir, nativeModelFamilies(configPath)).slice(0, MEASURED_TRIGGER_SAMPLE_SIZE);
12638
12675
  }
12639
12676
  /**
12640
12677
  * The measured compaction trigger for a project, or null when it has no
@@ -12647,8 +12684,8 @@ function selectedCompactionSamples(cwd, projectsDir = CLAUDE_PROJECTS_DIR) {
12647
12684
  * timestamp" both matter (a file-mtime-ordered, non-deduplicated version of
12648
12685
  * this returned a stale pre-regime-change trigger on real project data).
12649
12686
  */
12650
- function measureCompactionTrigger(cwd, projectsDir = CLAUDE_PROJECTS_DIR) {
12651
- const samples = selectedCompactionSamples(cwd, projectsDir);
12687
+ function measureCompactionTrigger(cwd, projectsDir = CLAUDE_PROJECTS_DIR, configPath) {
12688
+ const samples = selectedCompactionSamples(cwd, projectsDir, configPath);
12652
12689
  if (samples.length === 0) return null;
12653
12690
  const trigger = Math.min(...samples.map((s) => s.preTokens));
12654
12691
  console.error(`[context-fill] measured trigger for ${cwd}: ${trigger} (minimum of ` + samples.map((s) => `${s.preTokens}@${s.timestamp}`).join(", ") + ")");
@@ -12908,8 +12945,8 @@ async function checkAndEnqueueContextHandover(input, deps = defaultDeps) {
12908
12945
  console.error(`[context-handover-trigger] session ${input.sessionId}: worker session (PAI_WORKER=1) — no handover.`);
12909
12946
  return NOTHING;
12910
12947
  }
12911
- if (input.transcriptPath && transcriptModelFamily(input.transcriptPath) === "foreign") {
12912
- console.error(`[context-handover-trigger] session ${input.sessionId}: transcript written by a non-Claude model — no handover.`);
12948
+ if (input.transcriptPath && isForeignModelFamily(transcriptModelFamily(input.transcriptPath))) {
12949
+ console.error(`[context-handover-trigger] session ${input.sessionId}: transcript written by a model this machine does not run — no handover.`);
12913
12950
  return NOTHING;
12914
12951
  }
12915
12952
  let state = deps.loadState(input.sessionId);
@@ -13591,8 +13628,11 @@ function registerSessionCommands(sessionCmd, getDb) {
13591
13628
  * - body → --append-system-prompt (prepended, so an explicit caller
13592
13629
  * flag still wins);
13593
13630
  * - tools → --allowedTools;
13594
- * - model → a task class (haiku→simple, sonnet→implement, opus→complex),
13595
- * unless --class is given.
13631
+ * - model → a task class via its tier (haiku→simple, sonnet→implement,
13632
+ * opus→complex). Non-Anthropic ids map through the provider
13633
+ * registry (models.fast → haiku tier, models.default → sonnet
13634
+ * tier, a provider's modelTiers override if set), unless
13635
+ * --class is given.
13596
13636
  */
13597
13637
  /** Directory the agent library lives in. */
13598
13638
  function agentsDir() {
@@ -13651,13 +13691,43 @@ function loadAgent(name) {
13651
13691
  }
13652
13692
  return parseAgentFile(name, text, path);
13653
13693
  }
13654
- /** Map an agent's front-matter model to a worker class. */
13655
- function modelToClass(model) {
13656
- if (!model) return void 0;
13694
+ /** Tier task class: the cheap tier runs as simple, the middle as
13695
+ * implement, the top as complex. */
13696
+ const TIER_CLASS = {
13697
+ haiku: "simple",
13698
+ sonnet: "implement",
13699
+ opus: "complex"
13700
+ };
13701
+ /** Which tier a model id belongs to: one of the CLI's tier aliases, or an
13702
+ * exact match against a configured provider's models (a modelTiers override
13703
+ * first, then fast → haiku tier, default → sonnet tier). null = no match. */
13704
+ function modelTier(model, providers) {
13657
13705
  const m = model.toLowerCase();
13658
- if (m.includes("haiku")) return "simple";
13659
- if (m.includes("sonnet")) return "implement";
13660
- if (m.includes("opus")) return "complex";
13706
+ if (m.includes("haiku")) return "haiku";
13707
+ if (m.includes("sonnet")) return "sonnet";
13708
+ if (m.includes("opus")) return "opus";
13709
+ if (providers) for (const p of Object.values(providers)) {
13710
+ const override = p.modelTiers?.[model];
13711
+ if (override) return override;
13712
+ if (p.models.fast === model) return "haiku";
13713
+ if (p.models.default === model) return "sonnet";
13714
+ }
13715
+ return null;
13716
+ }
13717
+ /** Model ids already warned about — one line per id, not one per run. */
13718
+ const unmatchedTierLogged = /* @__PURE__ */ new Set();
13719
+ /** Map an agent's front-matter model to a worker class. An id that matches
13720
+ * no tier is logged once and falls back to the middle tier's class rather
13721
+ * than dropping the hint silently. */
13722
+ function modelToClass(model, providers) {
13723
+ if (!model) return void 0;
13724
+ const tier = modelTier(model, providers);
13725
+ if (tier) return TIER_CLASS[tier];
13726
+ if (!unmatchedTierLogged.has(model)) {
13727
+ unmatchedTierLogged.add(model);
13728
+ console.error(`[agents] model "${model}" matches no known tier — falling back to the ${TIER_CLASS.sonnet} class.`);
13729
+ }
13730
+ return TIER_CLASS.sonnet;
13661
13731
  }
13662
13732
  /** Extra claude args the definition contributes (before the caller's own). */
13663
13733
  function agentClaudeArgs(def) {
@@ -13668,12 +13738,12 @@ function agentClaudeArgs(def) {
13668
13738
  }
13669
13739
  /** `<agent>: <first 50 chars of prompt>` — the default label for a run. */
13670
13740
  function agentLabel(name, prompt) {
13671
- return `${name}: ${prompt ?? "(no prompt)"}`.slice(0, name.length + 2 + 50);
13741
+ return `${name}: ${prompt ?? UNLABELED}`.slice(0, name.length + 2 + 50);
13672
13742
  }
13673
13743
 
13674
13744
  //#endregion
13675
13745
  //#region src/cli/commands/worker/providers.ts
13676
- function fail$1(e) {
13746
+ function fail$2(e) {
13677
13747
  const msg = e instanceof Error ? e.message : String(e);
13678
13748
  console.error(err("pai worker: ") + msg);
13679
13749
  process.exitCode = 1;
@@ -13763,7 +13833,7 @@ function registerWorkerProviderCommands(providersCmd) {
13763
13833
  if (workers.active === name) console.log(` active: ${name} — workers ${workers.enabled ? "on" : "off"}`);
13764
13834
  for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
13765
13835
  } catch (e) {
13766
- fail$1(e);
13836
+ fail$2(e);
13767
13837
  }
13768
13838
  });
13769
13839
  providersCmd.command("update <name>").description("Change cost tier and tags of a provider (routing constraints use these)").option("--cost-tier <1-5>", "Cost tier 1 (cheapest) … 5 (most expensive)", parseCostTier).option("--tags <tags>", "Capability tags, comma-separated (from: " + PROVIDER_TAGS.join(", ") + "); --tags '' clears", (v, acc) => [...acc, v], []).action((name, opts) => {
@@ -13778,7 +13848,7 @@ function registerWorkerProviderCommands(providersCmd) {
13778
13848
  const { workers } = readWorkersSection();
13779
13849
  for (const line of describeProviders(workers)) console.log(dim$1(` ${line}`));
13780
13850
  } catch (e) {
13781
- fail$1(e);
13851
+ fail$2(e);
13782
13852
  }
13783
13853
  });
13784
13854
  providersCmd.command("remove <name>").description("Remove a provider and any classes pointing at it").action((name) => {
@@ -13786,7 +13856,7 @@ function registerWorkerProviderCommands(providersCmd) {
13786
13856
  removeProvider(name);
13787
13857
  console.log(ok$1(`provider ${name} removed`));
13788
13858
  } catch (e) {
13789
- fail$1(e);
13859
+ fail$2(e);
13790
13860
  }
13791
13861
  });
13792
13862
  providersCmd.command("use <name>").description("Make this provider the active one for runs without --provider/--class").action((name) => {
@@ -13794,7 +13864,7 @@ function registerWorkerProviderCommands(providersCmd) {
13794
13864
  useProvider(name);
13795
13865
  console.log(ok$1(`active provider: ${name}`));
13796
13866
  } catch (e) {
13797
- fail$1(e);
13867
+ fail$2(e);
13798
13868
  }
13799
13869
  });
13800
13870
  providersCmd.command("enable <name>").description("Enable a provider (also clears its cooldown)").action((name) => {
@@ -13802,7 +13872,7 @@ function registerWorkerProviderCommands(providersCmd) {
13802
13872
  setProviderEnabled(name, true);
13803
13873
  console.log(ok$1(`provider ${name} enabled`));
13804
13874
  } catch (e) {
13805
- fail$1(e);
13875
+ fail$2(e);
13806
13876
  }
13807
13877
  });
13808
13878
  providersCmd.command("disable <name>").description("Disable a provider (auto-routing skips it; --provider still works)").action((name) => {
@@ -13810,7 +13880,7 @@ function registerWorkerProviderCommands(providersCmd) {
13810
13880
  setProviderEnabled(name, false);
13811
13881
  console.log(ok$1(`provider ${name} disabled`));
13812
13882
  } catch (e) {
13813
- fail$1(e);
13883
+ fail$2(e);
13814
13884
  }
13815
13885
  });
13816
13886
  providersCmd.command("test [name]").description("One-word pong probe through a provider (default: the active one)").action(async (name) => {
@@ -13830,7 +13900,7 @@ function registerWorkerProviderCommands(providersCmd) {
13830
13900
  console.log(` reply: ${r.result.slice(0, 200)}`);
13831
13901
  process.exitCode = r.ok ? 0 : 1;
13832
13902
  } catch (e) {
13833
- fail$1(e);
13903
+ fail$2(e);
13834
13904
  }
13835
13905
  });
13836
13906
  }
@@ -13851,7 +13921,7 @@ function registerClassSubcommands(classesCmd) {
13851
13921
  setClass(cls, built);
13852
13922
  console.log(ok$1(`class ${cls} → ${classTargetText(built)}`));
13853
13923
  } catch (e) {
13854
- fail$1(e);
13924
+ fail$2(e);
13855
13925
  }
13856
13926
  });
13857
13927
  classesCmd.command("unset <class>").description("Remove a class (runs then use the active provider)").action((cls) => {
@@ -13859,7 +13929,7 @@ function registerClassSubcommands(classesCmd) {
13859
13929
  unsetClass(cls);
13860
13930
  console.log(ok$1(`class ${cls} removed`));
13861
13931
  } catch (e) {
13862
- fail$1(e);
13932
+ fail$2(e);
13863
13933
  }
13864
13934
  });
13865
13935
  }
@@ -13869,6 +13939,130 @@ function registerWorkerClassCommands(workerCmd) {
13869
13939
  registerClassSubcommands(workerCmd.command("roles", { hidden: true }).description("Alias of `classes` (roles was renamed to classes)").action(() => printClasses()));
13870
13940
  }
13871
13941
 
13942
+ //#endregion
13943
+ //#region src/cli/commands/worker/model.ts
13944
+ function fail$1(e) {
13945
+ const msg = e instanceof Error ? e.message : String(e);
13946
+ console.error(err("pai worker: ") + msg);
13947
+ process.exitCode = 1;
13948
+ }
13949
+ function registerWorkerModelCommand(workerCmd) {
13950
+ workerCmd.command("model [what] [model]").description("Model ids per provider: no args lists them,\n`model <model-id>` sets the active provider's default model (back-compat),\n`model <capability>` shows one capability, `model <capability> <model-id>` sets it.\nCapabilities: default, fast, image. --provider targets another provider.").option("--provider <name>", "Provider to read or change (default: the active one)").action((what, model, opts) => {
13951
+ try {
13952
+ const { workers } = readWorkersSection();
13953
+ if (what === void 0) {
13954
+ for (const line of describeModels(workers)) console.log(` ${line}`);
13955
+ return;
13956
+ }
13957
+ let capability = "default";
13958
+ let id = what;
13959
+ if (isModelCapability(what)) {
13960
+ capability = what;
13961
+ if (model === void 0) {
13962
+ const name = resolveProviderName(workers, opts.provider);
13963
+ const p = workers.providers[name];
13964
+ if (p) console.log(` ${name} ${capability} ${p.models[capability] ?? "(none)"}`);
13965
+ return;
13966
+ }
13967
+ id = model;
13968
+ } else if (model !== void 0) throw new WorkersConfigError(`unexpected second argument "${model}" — usage: model [<capability>] <model-id>`);
13969
+ const name = resolveProviderName(workers, opts.provider);
13970
+ const p = setProviderModel(name, capability, id).providers[name];
13971
+ if (p) {
13972
+ console.log(ok$1(`${name} ${capability} model: ${p.models[capability]}`));
13973
+ console.log(dim$1(` ${modelPrefsText(p)}`));
13974
+ }
13975
+ } catch (e) {
13976
+ fail$1(e);
13977
+ }
13978
+ });
13979
+ }
13980
+
13981
+ //#endregion
13982
+ //#region src/workers/wait.ts
13983
+ /**
13984
+ * wait.ts — `pai worker wait <id...>`: the deterministic poller.
13985
+ *
13986
+ * A script, not guidance: polls each worker's status file until every one is
13987
+ * terminal (done/failed/killed/lost), then returns their result texts. The
13988
+ * orchestrator's Bash call stays synchronous and gets the JSON on stdout,
13989
+ * with exit 1 naming whoever failed or timed out.
13990
+ */
13991
+ const TERMINAL = [
13992
+ "done",
13993
+ "failed",
13994
+ "killed",
13995
+ "lost"
13996
+ ];
13997
+ function sleep$1(ms) {
13998
+ return new Promise((resolve) => setTimeout(resolve, ms));
13999
+ }
14000
+ /**
14001
+ * The `result` text of the last result event in the worker's `<id>.jsonl`,
14002
+ * scanning the file backwards; null when the file or the event is absent.
14003
+ */
14004
+ function lastResultText(logDir, id) {
14005
+ let raw;
14006
+ try {
14007
+ raw = readFileSync(eventsPath(logDir, id), "utf8");
14008
+ } catch {
14009
+ return null;
14010
+ }
14011
+ const lines = raw.split("\n");
14012
+ for (let i = lines.length - 1; i >= 0; i--) {
14013
+ const line = lines[i].trim();
14014
+ if (!line.startsWith("{")) continue;
14015
+ try {
14016
+ const e = JSON.parse(line);
14017
+ if (e.type === "result" && typeof e.result === "string") return e.result;
14018
+ } catch {}
14019
+ }
14020
+ return null;
14021
+ }
14022
+ function toWaitResult(logDir, st) {
14023
+ const result = lastResultText(logDir, st.id);
14024
+ return {
14025
+ id: st.id,
14026
+ label: st.label,
14027
+ state: st.state,
14028
+ rc: st.rc,
14029
+ secs: st.secs,
14030
+ ok: st.state === "done" && (st.rc ?? 0) === 0,
14031
+ result,
14032
+ report: parseWorkerReport(result ?? "")
14033
+ };
14034
+ }
14035
+ /**
14036
+ * Wait until every named worker reaches a terminal state. Unknown ids throw
14037
+ * immediately. On timeout nothing is thrown: the return carries what is known
14038
+ * and `timedOut` names the still-pending ids.
14039
+ */
14040
+ async function waitWorkers(logDir, ids, opts = {}) {
14041
+ const timeoutMs = opts.timeoutMs ?? 9e5;
14042
+ const intervalMs = opts.intervalMs ?? 2e3;
14043
+ const known = /* @__PURE__ */ new Map();
14044
+ const pending = /* @__PURE__ */ new Set();
14045
+ for (const id of ids) {
14046
+ const st = loadStatus(logDir, id);
14047
+ if (!st) throw new Error(`no worker named "${id}"`);
14048
+ known.set(id, st);
14049
+ if (!TERMINAL.includes(st.state)) pending.add(id);
14050
+ }
14051
+ const deadline = Date.now() + timeoutMs;
14052
+ while (pending.size > 0 && Date.now() < deadline) {
14053
+ await sleep$1(Math.min(intervalMs, Math.max(1, deadline - Date.now())));
14054
+ for (const id of [...pending]) {
14055
+ const st = loadStatus(logDir, id);
14056
+ if (st) known.set(id, st);
14057
+ if (!st || TERMINAL.includes(known.get(id).state)) pending.delete(id);
14058
+ }
14059
+ }
14060
+ return {
14061
+ results: ids.map((id) => toWaitResult(logDir, known.get(id))),
14062
+ timedOut: [...pending]
14063
+ };
14064
+ }
14065
+
13872
14066
  //#endregion
13873
14067
  //#region src/cli/commands/worker/index.ts
13874
14068
  function currentLogDir() {
@@ -13890,7 +14084,13 @@ function registerWorkerCommands(workerCmd) {
13890
14084
  if (opts.agent) {
13891
14085
  const def = loadAgent(opts.agent);
13892
14086
  claudeArgs = [...agentClaudeArgs(def), ...args];
13893
- agentClass = modelToClass(def.model);
14087
+ let agentProviders;
14088
+ try {
14089
+ agentProviders = readWorkersSection().workers.providers;
14090
+ } catch {
14091
+ agentProviders = void 0;
14092
+ }
14093
+ agentClass = modelToClass(def.model, agentProviders);
13894
14094
  if (!label) label = agentLabel(opts.agent, parseRunnerArgs(args).prompt);
13895
14095
  }
13896
14096
  if (opts.chain) {
@@ -14035,13 +14235,31 @@ function registerWorkerCommands(workerCmd) {
14035
14235
  fail(e);
14036
14236
  }
14037
14237
  });
14038
- workerCmd.command("merge <id>").description("Merge a worker's worktree branch (worker/<id>) into the original checkout, then remove the worktree").action((id) => {
14238
+ workerCmd.command("merge <id>").description("Merge a worker's worktree branch (worker/<id>) into the original checkout, then remove the worktree and delete the branch").action((id) => {
14039
14239
  try {
14040
14240
  console.log(mergeWorker(currentLogDir(), id));
14041
14241
  } catch (e) {
14042
14242
  fail(e);
14043
14243
  }
14044
14244
  });
14245
+ workerCmd.command("wait <ids...>").description("Poll workers until they finish; prints each result as one JSON line, exit 1 on failure or timeout").option("--timeout <secs>", "Give up after this many seconds (default 900)", parseIntArg).action(async (ids, opts) => {
14246
+ try {
14247
+ const { results, timedOut } = await waitWorkers(currentLogDir(), ids, { timeoutMs: (opts.timeout ?? 900) * 1e3 });
14248
+ for (const r of results) console.log(JSON.stringify(r));
14249
+ if (timedOut.length) {
14250
+ console.error(err(`pai worker wait: timed out waiting for ${timedOut.join(", ")}`));
14251
+ process.exitCode = 1;
14252
+ return;
14253
+ }
14254
+ const failed = results.filter((r) => !r.ok).map((r) => r.id);
14255
+ if (failed.length) {
14256
+ console.error(err(`pai worker wait: failed: ${failed.join(", ")}`));
14257
+ process.exitCode = 1;
14258
+ }
14259
+ } catch (e) {
14260
+ fail(e);
14261
+ }
14262
+ });
14045
14263
  workerCmd.command("discard <id>").description("Drop a worker's worktree and branch, keeping nothing").action((id) => {
14046
14264
  try {
14047
14265
  console.log(discardWorker(currentLogDir(), id));
@@ -14131,9 +14349,9 @@ function registerWorkerCommands(workerCmd) {
14131
14349
  fail(e);
14132
14350
  }
14133
14351
  });
14134
- workerCmd.command("status-line [term] [cwd]").description("One-line worker summary for a status bar (empty when none in scope).\nCalled by statusline-command.sh with ITERM_SESSION_ID and the pane cwd.").action((term, cwd) => {
14352
+ workerCmd.command("status-line [term] [cwd] [session]").description("One-line worker summary for a status bar (empty when none in scope).\nCalled by statusline-command.sh with ITERM_SESSION_ID, the pane cwd and\nthe claude session id (claims workers spawned by that session's Bash).").action((term, cwd, session) => {
14135
14353
  try {
14136
- const out = statusLineOutput(currentLogDir(), term ?? process.env.ITERM_SESSION_ID ?? "", cwd ?? process.cwd());
14354
+ const out = statusLineOutput(currentLogDir(), term ?? process.env.ITERM_SESSION_ID ?? "", cwd ?? process.cwd(), session ?? "");
14137
14355
  if (out) console.log(out);
14138
14356
  } catch (e) {
14139
14357
  fail(e);
@@ -14155,6 +14373,31 @@ function registerWorkerCommands(workerCmd) {
14155
14373
  fail(e);
14156
14374
  }
14157
14375
  });
14376
+ workerCmd.command("fallback [action] [provider]").description("Machine-wide fallback: every NEW Claude Code process runs on a worker\nprovider (settings.json env + model pin) until switched back.\non [provider] switches (default: active), off restores settings.json\nexactly, status shows state and running sessions. CLAUDE_SETTINGS_PATH\npoints at another settings.json for dry runs.").action((action, provider) => {
14377
+ try {
14378
+ if (action === void 0 || action === "status") {
14379
+ console.log(fallbackStatusText(fallbackStatus()).join("\n"));
14380
+ return;
14381
+ }
14382
+ if (action === "on") {
14383
+ const r = fallbackOn(provider);
14384
+ console.log(r.alreadyOn ? `fallback already on — provider ${r.provider}, env re-applied` : `fallback on — provider ${r.provider}; every new Claude Code process uses it`);
14385
+ console.log(` settings.json env: ${r.envKeys.join(", ")}`);
14386
+ console.log(` model pin: ${r.model}`);
14387
+ console.log("running sessions keep their current provider until restarted (see: pai worker fallback status)");
14388
+ return;
14389
+ }
14390
+ if (action === "off") {
14391
+ const r = fallbackOff();
14392
+ console.log(`fallback off — provider ${r.provider} released, settings.json restored`);
14393
+ console.log(` restored keys: ${r.envKeys.join(", ")}`);
14394
+ return;
14395
+ }
14396
+ fail(/* @__PURE__ */ new Error(`unknown fallback action "${action}" (expected: on, off, status)`));
14397
+ } catch (e) {
14398
+ fail(e);
14399
+ }
14400
+ });
14158
14401
  workerCmd.command("install").description("Migrate: Agent hook in settings.json, ~/.local/bin glm* shims, old script cleanup").action(() => {
14159
14402
  try {
14160
14403
  const r = installWorkers();
@@ -14165,6 +14408,7 @@ function registerWorkerCommands(workerCmd) {
14165
14408
  });
14166
14409
  registerWorkerProviderCommands(workerCmd.command("providers").description("Providers: list, add, remove, use, enable, disable, test"));
14167
14410
  registerWorkerClassCommands(workerCmd);
14411
+ registerWorkerModelCommand(workerCmd);
14168
14412
  }
14169
14413
  function parseIntArg(v) {
14170
14414
  const n = parseInt(v, 10);
@@ -14534,8 +14778,8 @@ function analyzeProject(db, project) {
14534
14778
  async function countVectorDbPaths(oldPaths) {
14535
14779
  if (oldPaths.length === 0) return 0;
14536
14780
  try {
14537
- const { loadConfig } = await import("./config-C_ErGddD.mjs");
14538
- const { PostgresBackend } = await import("./postgres-mW1n7Vi1.mjs");
14781
+ const { loadConfig } = await import("./config-DD52BPB-.mjs");
14782
+ const { PostgresBackend } = await import("./postgres-Dwxe290H.mjs");
14539
14783
  const config = loadConfig();
14540
14784
  if (config.storageBackend !== "postgres") return 0;
14541
14785
  const pgBackend = new PostgresBackend(config.postgres ?? {});
@@ -14555,8 +14799,8 @@ async function countVectorDbPaths(oldPaths) {
14555
14799
  async function updateVectorDbPaths(moves) {
14556
14800
  if (moves.length === 0) return 0;
14557
14801
  try {
14558
- const { loadConfig } = await import("./config-C_ErGddD.mjs");
14559
- const { PostgresBackend } = await import("./postgres-mW1n7Vi1.mjs");
14802
+ const { loadConfig } = await import("./config-DD52BPB-.mjs");
14803
+ const { PostgresBackend } = await import("./postgres-Dwxe290H.mjs");
14560
14804
  const config = loadConfig();
14561
14805
  if (config.storageBackend !== "postgres") return 0;
14562
14806
  const pgBackend = new PostgresBackend(config.postgres ?? {});
@@ -15618,6 +15862,8 @@ async function runSelector(lines, onRemove) {
15618
15862
  if (cur?.record.live) parts.push(chalk.cyan("g") + dim$1(" go to tab"));
15619
15863
  if (cur?.record.dir) parts.push(chalk.cyan("n") + dim$1(" new"));
15620
15864
  if (cur?.record.dir) parts.push(chalk.cyan("c") + dim$1(" cd"));
15865
+ if (cur?.record.dir) parts.push(chalk.cyan("w") + dim$1(" worker"));
15866
+ if (cur?.record.dir) parts.push(chalk.cyan("a") + dim$1(" claude"));
15621
15867
  if (cur?.record.dir) parts.push(chalk.cyan("f") + dim$1(" finder"));
15622
15868
  if (cur?.record.slug) parts.push(chalk.cyan("d") + dim$1(" remove"));
15623
15869
  parts.push(chalk.cyan("s") + dim$1(" search"));
@@ -15714,6 +15960,14 @@ async function runSelector(lines, onRemove) {
15714
15960
  record: cur.record,
15715
15961
  action: "cd"
15716
15962
  });
15963
+ if (s === "w" && cur.record.dir) return finish({
15964
+ record: cur.record,
15965
+ action: "worker"
15966
+ });
15967
+ if (s === "a" && cur.record.dir) return finish({
15968
+ record: cur.record,
15969
+ action: "anthropic"
15970
+ });
15717
15971
  if (s === "f" && cur.record.dir) {
15718
15972
  openInFileManager(cur.record.dir);
15719
15973
  return;
@@ -15832,8 +16086,10 @@ async function cmdPick(db, opts = {}) {
15832
16086
  printExitDir(record.dir);
15833
16087
  return;
15834
16088
  }
16089
+ const engine = action === "worker" ? "worker" : action === "anthropic" ? "claude" : void 0;
15835
16090
  launchInDir(record.dir, record.name, {
15836
16091
  forceFresh: true,
16092
+ engine,
15837
16093
  dryRun: opts.dryRun
15838
16094
  });
15839
16095
  }
@@ -16081,4 +16337,4 @@ claude() {
16081
16337
 
16082
16338
  //#endregion
16083
16339
  export { drainStdio as n, buildProgram as t };
16084
- //# sourceMappingURL=program-DXBwJV7h.mjs.map
16340
+ //# sourceMappingURL=program-AR0qlYRg.mjs.map