@tekmidian/pai 0.37.0 → 0.39.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 (85) 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-sXcK5bDZ.mjs → chain-CTtHligI.mjs} +1040 -782
  4. package/dist/chain-CTtHligI.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 +207 -34
  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/hooks/route-agents-to-worker.mjs +112 -20
  23. package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
  24. package/dist/hooks/worker-proxy.mjs +110 -18
  25. package/dist/hooks/worker-proxy.mjs.map +2 -2
  26. package/dist/hooks/worker-status-line.mjs +176 -23
  27. package/dist/hooks/worker-status-line.mjs.map +4 -4
  28. package/dist/{indexer-backend-Cox9BCo-.mjs → indexer-backend-TG64CCQC.mjs} +1 -1
  29. package/dist/{indexer-backend-Cox9BCo-.mjs.map → indexer-backend-TG64CCQC.mjs.map} +1 -1
  30. package/dist/{latent-ideas-BC1oINZ-.mjs → latent-ideas-B7wq75Pt.mjs} +2 -2
  31. package/dist/{latent-ideas-BC1oINZ-.mjs.map → latent-ideas-B7wq75Pt.mjs.map} +1 -1
  32. package/dist/{link-boost-QFLrJwD6.mjs → link-boost-HkG7JWZR.mjs} +1 -1
  33. package/dist/{link-boost-QFLrJwD6.mjs.map → link-boost-HkG7JWZR.mjs.map} +1 -1
  34. package/dist/{neighborhood-D9MJ1c8f.mjs → neighborhood-lThN-MaQ.mjs} +1 -1
  35. package/dist/{neighborhood-D9MJ1c8f.mjs.map → neighborhood-lThN-MaQ.mjs.map} +1 -1
  36. package/dist/{note-context-d1wT_-GA.mjs → note-context-b6k0mAKi.mjs} +1 -1
  37. package/dist/{note-context-d1wT_-GA.mjs.map → note-context-b6k0mAKi.mjs.map} +1 -1
  38. package/dist/planner-BDI7bE9B.mjs +243 -0
  39. package/dist/planner-BDI7bE9B.mjs.map +1 -0
  40. package/dist/{postgres--BjPtLa0.mjs → postgres-mW1n7Vi1.mjs} +1 -1
  41. package/dist/{postgres--BjPtLa0.mjs.map → postgres-mW1n7Vi1.mjs.map} +1 -1
  42. package/dist/{program-CEIHn_Ma.mjs → program-DXBwJV7h.mjs} +274 -65
  43. package/dist/program-DXBwJV7h.mjs.map +1 -0
  44. package/dist/providers-FYkZjn_C.mjs +1405 -0
  45. package/dist/providers-FYkZjn_C.mjs.map +1 -0
  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 +34 -12
  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 +12 -7
  75. package/docs/commands/worker.md +110 -17
  76. package/docs/worker.md +244 -25
  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/program-CEIHn_Ma.mjs.map +0 -1
  82. package/dist/providers-sXcK5bDZ.mjs.map +0 -1
  83. package/dist/query-feedback-DhyLOe5S.mjs +0 -3
  84. package/dist/router-1zi8jiNF.mjs +0 -3
  85. /package/dist/{main-resolver-IhZo4pI0.mjs → main-resolver-D6IImXvF.mjs} +0 -0
@@ -131,6 +131,14 @@ var CONFIG_DIR = join(homedir(), ".config", "pai");
131
131
  var CONFIG_FILE = join(CONFIG_DIR, "config.json");
132
132
 
133
133
  // src/workers/config.ts
134
+ var PROVIDER_TAGS = [
135
+ "code",
136
+ "vision",
137
+ "image-gen",
138
+ "long-context",
139
+ "fast",
140
+ "reasoning"
141
+ ];
134
142
  var DEFAULT_LOG_DIR = "~/.claude/logs/workers";
135
143
  var DEFAULT_PANE = {
136
144
  enabled: true,
@@ -142,16 +150,24 @@ var DEFAULT_ROUTING2 = {
142
150
  cooldownMinutes: 30,
143
151
  retryOnQuota: true
144
152
  };
153
+ var DEFAULT_TREE = {
154
+ maxDepth: 2,
155
+ maxChildren: 4
156
+ };
157
+ var DEFAULT_MCP_SETS = {
158
+ desktop: ["clickr"]
159
+ };
145
160
  function defaultWorkersConfig() {
146
161
  return {
147
162
  enabled: false,
148
163
  active: null,
149
164
  providers: {},
150
- roles: {},
151
- mcpSets: {},
165
+ classes: {},
166
+ mcpSets: { ...DEFAULT_MCP_SETS },
152
167
  pane: { ...DEFAULT_PANE },
153
168
  logDir: DEFAULT_LOG_DIR,
154
- routing: { ...DEFAULT_ROUTING2, order: [] }
169
+ routing: { ...DEFAULT_ROUTING2, order: [] },
170
+ tree: { ...DEFAULT_TREE }
155
171
  };
156
172
  }
157
173
  var WorkersConfigError = class extends Error {
@@ -210,6 +226,24 @@ function parseProvider(name, raw) {
210
226
  if (protocol === "openai" && !upstreamUrl) {
211
227
  bad(`.providers.${name}.upstreamUrl`, `is required for protocol "openai" (the Chat Completions base, e.g. "https://api.openai.com/v1")`);
212
228
  }
229
+ const costTier = p.costTier === void 0 ? void 0 : p.costTier;
230
+ if (costTier !== void 0) {
231
+ if (typeof costTier !== "number" || !Number.isInteger(costTier) || costTier < 1 || costTier > 5) {
232
+ bad(`.providers.${name}.costTier`, "must be an integer 1 (cheapest) \u2026 5 (most expensive)");
233
+ }
234
+ }
235
+ let tags;
236
+ if (p.tags !== void 0) {
237
+ if (!Array.isArray(p.tags) || p.tags.some((x) => typeof x !== "string")) {
238
+ bad(`.providers.${name}.tags`, `must be an array of tags from: ${PROVIDER_TAGS.join(", ")}`);
239
+ }
240
+ for (const t of p.tags) {
241
+ if (!PROVIDER_TAGS.includes(t)) {
242
+ bad(`.providers.${name}.tags`, `"${t}" is not a tag (from: ${PROVIDER_TAGS.join(", ")})`);
243
+ }
244
+ }
245
+ tags = p.tags;
246
+ }
213
247
  return {
214
248
  enabled: p.enabled === void 0 ? true : p.enabled === true,
215
249
  protocol,
@@ -222,7 +256,9 @@ function parseProvider(name, raw) {
222
256
  ...engine !== "claude" ? { engine } : {},
223
257
  ...str(p.quotaProbe) ? { quotaProbe: str(p.quotaProbe) } : {},
224
258
  ...quotaSkipAt !== void 0 ? { quotaSkipAt } : {},
225
- ...contextWindow !== void 0 ? { contextWindow } : {}
259
+ ...contextWindow !== void 0 ? { contextWindow } : {},
260
+ ...costTier !== void 0 ? { costTier } : {},
261
+ ...tags ? { tags } : {}
226
262
  };
227
263
  }
228
264
  function parseWorkersConfig(raw) {
@@ -241,34 +277,68 @@ function parseWorkersConfig(raw) {
241
277
  providers[name] = parseProvider(name, p);
242
278
  }
243
279
  }
244
- const roles = {};
245
- if (w.roles !== void 0) {
246
- if (typeof w.roles !== "object" || w.roles === null || Array.isArray(w.roles)) {
247
- bad(".roles", "must be an object of role \u2192 provider[/alias] or {provider, mcp}");
280
+ const classes = {};
281
+ const classesRaw = w.classes !== void 0 ? w.classes : w.roles;
282
+ if (classesRaw !== void 0) {
283
+ if (typeof classesRaw !== "object" || classesRaw === null || Array.isArray(classesRaw)) {
284
+ bad(w.classes !== void 0 ? ".classes" : ".roles", "must be an object of class \u2192 provider[/alias] or {provider, mcp, maxCostTier, requireTags, order}");
248
285
  }
249
- for (const [role, target] of Object.entries(w.roles)) {
286
+ for (const [cls, target] of Object.entries(classesRaw)) {
250
287
  if (typeof target === "object" && target !== null && !Array.isArray(target)) {
251
288
  const o = target;
252
289
  const provider = str(o.provider);
253
- if (!provider || provider.includes(" ")) {
254
- bad(`.roles.${role}.provider`, `invalid provider "${provider}"`);
290
+ if (o.provider !== void 0 && (!provider || provider.includes(" "))) {
291
+ bad(`.classes.${cls}.provider`, `invalid provider "${provider}"`);
255
292
  }
256
293
  let mcp;
257
294
  if (o.mcp !== void 0) {
258
295
  if (!Array.isArray(o.mcp) || o.mcp.some((x) => typeof x !== "string")) {
259
- bad(`.roles.${role}.mcp`, "must be an array of MCP server or set names");
296
+ bad(`.classes.${cls}.mcp`, "must be an array of MCP server or set names");
260
297
  }
261
298
  mcp = o.mcp;
262
299
  }
263
- roles[role] = mcp ? { provider, mcp } : { provider };
300
+ let maxCostTier;
301
+ if (o.maxCostTier !== void 0) {
302
+ if (typeof o.maxCostTier !== "number" || !Number.isInteger(o.maxCostTier) || o.maxCostTier < 1 || o.maxCostTier > 5) {
303
+ bad(`.classes.${cls}.maxCostTier`, "must be an integer 1 \u2026 5");
304
+ }
305
+ maxCostTier = o.maxCostTier;
306
+ }
307
+ let requireTags;
308
+ if (o.requireTags !== void 0) {
309
+ if (!Array.isArray(o.requireTags) || o.requireTags.some((x) => typeof x !== "string")) {
310
+ bad(`.classes.${cls}.requireTags`, `must be an array of tags from: ${PROVIDER_TAGS.join(", ")}`);
311
+ }
312
+ for (const t of o.requireTags) {
313
+ if (!PROVIDER_TAGS.includes(t)) {
314
+ bad(`.classes.${cls}.requireTags`, `"${t}" is not a tag (from: ${PROVIDER_TAGS.join(", ")})`);
315
+ }
316
+ }
317
+ requireTags = o.requireTags;
318
+ }
319
+ let order;
320
+ if (o.order !== void 0) {
321
+ if (!Array.isArray(o.order) || o.order.some((x) => typeof x !== "string")) {
322
+ bad(`.classes.${cls}.order`, "must be an array of provider names");
323
+ }
324
+ order = o.order;
325
+ }
326
+ const obj = {
327
+ ...provider ? { provider } : {},
328
+ ...mcp ? { mcp } : {},
329
+ ...maxCostTier !== void 0 ? { maxCostTier } : {},
330
+ ...requireTags ? { requireTags } : {},
331
+ ...order ? { order } : {}
332
+ };
333
+ classes[cls] = Object.keys(obj).length ? obj : {};
264
334
  } else {
265
335
  const t = str(target);
266
- if (!t || t.includes(" ")) bad(`.roles.${role}`, `invalid target "${t}"`);
267
- roles[role] = t;
336
+ if (!t || t.includes(" ")) bad(`.classes.${cls}`, `invalid target "${t}"`);
337
+ classes[cls] = t;
268
338
  }
269
339
  }
270
340
  }
271
- const mcpSets = {};
341
+ const mcpSets = { ...DEFAULT_MCP_SETS };
272
342
  if (w.mcpSets !== void 0) {
273
343
  if (typeof w.mcpSets !== "object" || w.mcpSets === null || Array.isArray(w.mcpSets)) {
274
344
  bad(".mcpSets", "must be an object of set name \u2192 [server names]");
@@ -319,6 +389,27 @@ function parseWorkersConfig(raw) {
319
389
  retryOnQuota: r.retryOnQuota === void 0 ? DEFAULT_ROUTING2.retryOnQuota : r.retryOnQuota === true
320
390
  };
321
391
  }
392
+ let tree = { ...DEFAULT_TREE };
393
+ if (w.tree !== void 0) {
394
+ if (typeof w.tree !== "object" || w.tree === null || Array.isArray(w.tree)) {
395
+ bad(".tree", "must be an object");
396
+ }
397
+ const t = w.tree;
398
+ if (t.maxDepth !== void 0) {
399
+ if (typeof t.maxDepth !== "number" || !Number.isInteger(t.maxDepth) || t.maxDepth < 0) {
400
+ bad(".tree.maxDepth", "must be a non-negative integer");
401
+ }
402
+ }
403
+ if (t.maxChildren !== void 0) {
404
+ if (typeof t.maxChildren !== "number" || !Number.isInteger(t.maxChildren) || t.maxChildren < 1) {
405
+ bad(".tree.maxChildren", "must be a positive integer");
406
+ }
407
+ }
408
+ tree = {
409
+ maxDepth: t.maxDepth === void 0 ? DEFAULT_TREE.maxDepth : t.maxDepth,
410
+ maxChildren: t.maxChildren === void 0 ? DEFAULT_TREE.maxChildren : t.maxChildren
411
+ };
412
+ }
322
413
  const active = w.active === void 0 || w.active === null ? null : str(w.active);
323
414
  if (active !== null && active !== "auto" && !(active in providers)) {
324
415
  }
@@ -326,11 +417,12 @@ function parseWorkersConfig(raw) {
326
417
  enabled: w.enabled === void 0 ? d.enabled : w.enabled === true,
327
418
  active,
328
419
  providers,
329
- roles,
420
+ classes,
330
421
  mcpSets,
331
422
  pane,
332
423
  logDir: str(w.logDir) || d.logDir,
333
- routing
424
+ routing,
425
+ tree
334
426
  };
335
427
  }
336
428
  function readWorkersSection() {
@@ -406,7 +498,44 @@ function workerInScope(worker, term2) {
406
498
  return tabKey(worker.term) === tabKey(term2) && tabKey(term2) !== "";
407
499
  }
408
500
 
501
+ // src/workers/handoff.ts
502
+ import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "node:fs";
503
+ import { dirname, join as join5 } from "node:path";
504
+ function inboxPath(logDir, id) {
505
+ return join5(logDir, `${id}.inbox.jsonl`);
506
+ }
507
+ function readInbox(logDir, id) {
508
+ const path = inboxPath(logDir, id);
509
+ if (!existsSync3(path)) return [];
510
+ const out = [];
511
+ for (const line of readFileSync3(path, "utf8").split("\n")) {
512
+ if (!line.trim()) continue;
513
+ try {
514
+ out.push(JSON.parse(line));
515
+ } catch {
516
+ }
517
+ }
518
+ return out;
519
+ }
520
+
521
+ // src/workers/tree.ts
522
+ function workerDepth(statuses, id) {
523
+ const byId = new Map(statuses.map((s) => [s.id, s]));
524
+ let depth = 0;
525
+ let cur = byId.get(id);
526
+ const seen = /* @__PURE__ */ new Set([id]);
527
+ while (cur?.parent && !seen.has(cur.parent)) {
528
+ seen.add(cur.parent);
529
+ const next = byId.get(cur.parent);
530
+ if (!next) break;
531
+ depth += 1;
532
+ cur = next;
533
+ }
534
+ return depth;
535
+ }
536
+
409
537
  // src/workers/report.ts
538
+ var OPERATOR_MARK = "[operator]";
410
539
  var WORKER_CONTRACT_PROMPT = [
411
540
  "You are a headless implementation worker, run non-interactively by an orchestrating session.",
412
541
  "No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.",
@@ -414,7 +543,20 @@ var WORKER_CONTRACT_PROMPT = [
414
543
  '{"changed":[{"path":"\u2026","summary":"\u2026"}],"commands":["\u2026"],"checks":[{"name":"\u2026","ok":true,"detail":"\u2026"}],"open":["\u2026"],"notes":"one line"}',
415
544
  "changed: files you touched (path + one-line summary). commands: the commands that verify the work.",
416
545
  "checks: each with ok true/false and the evidence in detail. open: what you could not finish, if anything.",
417
- "notes: one line, the headline a reviewer reads first."
546
+ "notes: one line, the headline a reviewer reads first.",
547
+ "",
548
+ `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,
549
+ "run (the prompt of a resumed run is the operator's too). Answer it FIRST, in one or two plain lines \u2014",
550
+ "a question gets its answer, an instruction gets one line naming what will change \u2014 then continue the",
551
+ "task you were on.",
552
+ "",
553
+ "Sub-workers: you may start your own workers with `pai worker run --class <class> -p '<prompt>'`",
554
+ "(Bash tool). Your children run on their own provider and report back to you automatically when",
555
+ "they finish. Send a handoff UP instead of doing work yourself when a cheaper provider would",
556
+ "suffice, the task is out of your scope, or a decision is needed:",
557
+ '`pai worker handoff \'{"kind":"proposal","text":"\u2026","data":{\u2026}}\'` (kinds: proposal,',
558
+ "question, blocker; results are sent for you when you finish). Sibling workers are not a",
559
+ "coordination path \u2014 there is no sideways channel; everything goes up to your parent."
418
560
  ].join("\n");
419
561
 
420
562
  // src/workers/render.ts
@@ -442,9 +584,9 @@ function contextMeter(c, s, minPct = 0) {
442
584
  if (pct > 70) return c("yellow", label);
443
585
  return label;
444
586
  }
445
- function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(true)) {
587
+ function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(true), all = mine, inbox = {}) {
446
588
  if (!mine.length) return "";
447
- const running = mine.filter((s) => s.state === "running" && alive(s.pid));
589
+ const running = mine.filter((s) => s.state === "running" && alive(s.pid)).sort((a, b) => workerDepth(all, a.id) - workerDepth(all, b.id));
448
590
  const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
449
591
  const doneToday = mine.filter((s) => s.state !== "running" && s.started.startsWith(today));
450
592
  const ok = doneToday.filter((s) => s.state === "done").length;
@@ -454,7 +596,10 @@ function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(
454
596
  let head = `${providerTag} \u25B6${running.length}`;
455
597
  const parts = running.slice(0, 3).map((s) => {
456
598
  const meter = contextMeter(c, s, 60);
457
- return `${s.id.slice(-4)} ${s.label.slice(0, 26)} ${ageOf(s.started, now)} \xB7 ${s.last.slice(0, 30)}` + (meter ? ` ${meter}` : "");
599
+ const depth = workerDepth(all, s.id);
600
+ const lead = depth > 0 ? " ".repeat(depth - 1) + "\u21B3 " : "";
601
+ const box = inbox[s.id] ? ` \u25C6${inbox[s.id]}` : "";
602
+ return `${lead}${s.id.slice(-4)} ${s.label.slice(0, 26)} ${ageOf(s.started, now)} \xB7 ${s.last.slice(0, 30)}${box}` + (meter ? ` ${meter}` : "");
458
603
  });
459
604
  if (parts.length) head += " " + parts.join(" | ");
460
605
  if (doneToday.length) head += ` \u2713${ok} \u2717${bad2} today`;
@@ -462,6 +607,14 @@ function renderStatusLine(mine, now = /* @__PURE__ */ new Date(), c = makeColor(
462
607
  }
463
608
 
464
609
  // src/workers/viewer.ts
610
+ function inboxCounts(logDir, statuses) {
611
+ const out = {};
612
+ for (const s of statuses) {
613
+ const n = readInbox(logDir, s.id).length;
614
+ if (n) out[s.id] = n;
615
+ }
616
+ return out;
617
+ }
465
618
  function statusLineOutput(logDir, term2, cwd2, now = /* @__PURE__ */ new Date()) {
466
619
  const statuses = loadStatuses(logDir);
467
620
  const mine = statuses.filter((s) => {
@@ -469,7 +622,7 @@ function statusLineOutput(logDir, term2, cwd2, now = /* @__PURE__ */ new Date())
469
622
  const sameDir = cwd2 && s.cwd.startsWith(cwd2);
470
623
  return sameScope || !term2 && sameDir;
471
624
  });
472
- return renderStatusLine(mine, now);
625
+ return renderStatusLine(mine, now, makeColor(false), statuses, inboxCounts(logDir, mine));
473
626
  }
474
627
 
475
628
  // src/workers/standalone/status-line.ts