agent-yes 1.265.4 → 1.266.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 (75) hide show
  1. package/dist/{SUPPORTED_CLIS-C3tcRetU.js → SUPPORTED_CLIS-AFV5pjSx.js} +2 -2
  2. package/dist/{SUPPORTED_CLIS-CY3SaV0D.js → SUPPORTED_CLIS-DRVuSo48.js} +3 -3
  3. package/dist/{agentShare-g6N1A6A_.js → agentShare-qdFSp9Uq.js} +6 -5
  4. package/dist/{callback-L4k-v2GG.js → callback-Da7MRLh6.js} +6 -5
  5. package/dist/{callback-z-w0ND5z.js → callback-DfNFdZin.js} +5 -5
  6. package/dist/{callbackCore-Cv4EOGjE.js → callbackCore-DcptaZwH.js} +1 -1
  7. package/dist/{callbackCore-Cg3Ye0ZS.js → callbackCore-K99x3C75.js} +1 -1
  8. package/dist/{channels-BwpN_8mu.js → channels-DQaZbare.js} +2 -2
  9. package/dist/cli.js +11 -11
  10. package/dist/{cwdConflictWarn-DZTxTGXq.js → cwdConflictWarn-CtQhl-vF.js} +1 -1
  11. package/dist/{cwdPassthroughHint-DDVkhBiD.js → cwdPassthroughHint-apaX_O7p.js} +1 -1
  12. package/dist/{expose-BG17GV7D.js → expose-BXn6My6A.js} +1 -1
  13. package/dist/{forkNested-VroZS-nk.js → forkNested-CDfWPiHw.js} +12 -2
  14. package/dist/{hist-CoXBBjJt.js → hist-BpocAe1N.js} +1 -1
  15. package/dist/index.js +2 -2
  16. package/dist/initMsg-BjRseolL.js +3 -0
  17. package/dist/initMsg-C-w4A_eF.js +85 -0
  18. package/dist/needsInput-103zhZ90.js +79 -0
  19. package/dist/{nodeRuntime-CT0w6zsB.js → nodeRuntime-CRoEMWkp.js} +1 -1
  20. package/dist/{notifyDaemon-BL1tmxUK.js → notifyDaemon-BFzqcdN1.js} +32 -5
  21. package/dist/notifyStore-CEDiVyis.js +4 -0
  22. package/dist/notifyStore-Cbw2ZJ0X.js +559 -0
  23. package/dist/{openBrowser-Cw8hg7ok.js → openBrowser-DhQ9pJRi.js} +1 -1
  24. package/dist/parentLink-B798ASCW.js +5 -0
  25. package/dist/parentLink-C0cIRjfL.js +53 -0
  26. package/dist/parentPingLoop-BIKxzbPH.js +469 -0
  27. package/dist/{remotes-DWVw-Q7V.js → remotes-Dg_JscKk.js} +2 -2
  28. package/dist/{remotes-iIvqiFWK.js → remotes-iMhdZytp.js} +3 -3
  29. package/dist/{rustBinary-DSVRhDzO.js → rustBinary-JGNROWfY.js} +2 -2
  30. package/dist/{schedule-i4o5SJ_h.js → schedule-DbLpjgl5.js} +6 -6
  31. package/dist/{serve-CpGJD2Kt.js → serve-ucVXOci1.js} +31 -30
  32. package/dist/{setup-B-iQKsQW.js → setup-De7uYRZb.js} +4 -4
  33. package/dist/{share-DOnynkf4.js → share-Db_zb6gH.js} +1 -1
  34. package/dist/{share-B-e_9WBx.js → share-NUhi-Wdd.js} +1 -1
  35. package/dist/{spawnGate-S6EoFdzX.js → spawnGate-BvaZeHE7.js} +1 -1
  36. package/dist/{spawnGate-BmsTv340.js → spawnGate-CEXVmTtv.js} +2 -2
  37. package/dist/subcommands-C04exkIM.js +11 -0
  38. package/dist/{subcommands-CQPnT5qu.js → subcommands-DtF31RoW.js} +41 -656
  39. package/dist/{systemPathLink-CjLmiPbB.js → systemPathLink-7TFPwJna.js} +1 -1
  40. package/dist/{terminal-CybRgMZu.js → terminal-Dm32SXU-.js} +2 -2
  41. package/dist/{todoCli-DZmOmcvd.js → todoCli-c_R7D7Y-.js} +1 -1
  42. package/dist/{tray-Cjm3a8JH.js → tray-DZnGjJBK.js} +1 -1
  43. package/dist/trayApp-CZbwLK9t.js +5 -0
  44. package/dist/{trayApp-UglHbk_m.js → trayApp-CfkPG0wy.js} +2 -2
  45. package/dist/{ts-nuUnrVcf.js → ts-CGtoPER5.js} +43 -10
  46. package/dist/{versionChecker-CMOLbEEi.js → versionChecker-CJgW9DHY.js} +2 -2
  47. package/dist/{webrtcLink-CS4D-3G1.js → webrtcLink-DyCj380i.js} +1 -1
  48. package/dist/{webrtcRemote-zQybN9IR.js → webrtcRemote-BDg0jjTs.js} +2 -2
  49. package/dist/{widget-y5Bpvwsa.js → widget-DXtaX3QR.js} +3 -3
  50. package/dist/{workspaceConfig-BDqp7fYl.js → workspaceConfig-HiWQzq6A.js} +1 -1
  51. package/dist/{ws-CH8GzcQn.js → ws-CqlS5JhN.js} +5 -4
  52. package/dist/{ws-BrLV-AlH.js → ws-aKRZpI9b.js} +9 -11
  53. package/package.json +1 -1
  54. package/ts/forkNested.spec.ts +22 -0
  55. package/ts/forkNested.ts +19 -0
  56. package/ts/index.ts +77 -12
  57. package/ts/initMsg.spec.ts +114 -0
  58. package/ts/initMsg.ts +107 -0
  59. package/ts/ownerHeartbeat.ts +40 -1
  60. package/ts/parentLink.spec.ts +64 -0
  61. package/ts/parentLink.ts +58 -0
  62. package/ts/parentPing.spec.ts +176 -0
  63. package/ts/parentPing.ts +195 -0
  64. package/ts/parentPingLoop.delivery.spec.ts +236 -0
  65. package/ts/parentPingLoop.spec.ts +79 -0
  66. package/ts/parentPingLoop.ts +196 -0
  67. package/ts/parentPingSend.spec.ts +195 -0
  68. package/ts/parentPingSend.ts +148 -0
  69. package/ts/parentWatching.spec.ts +61 -0
  70. package/ts/parentWatching.ts +89 -0
  71. package/ts/subcommands.ts +11 -2
  72. package/ts/trayApp.spec.ts +31 -4
  73. package/ts/ws.ts +17 -12
  74. package/dist/subcommands-Cgt2-kjg.js +0 -10
  75. package/dist/trayApp-CmwwBg8v.js +0 -5
@@ -1,23 +1,23 @@
1
1
  import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
2
- import { n as logger } from "./logger-CDIsZ-Pp.js";
3
2
  import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
4
3
  import { a as updateGlobalPidStatus, i as readGlobalPids } from "./globalPidIndex-Dq4KY2IV.js";
5
4
  import { a as recordOutbox, i as recordMessage, n as readMailbox, t as partyMatches } from "./messageLog-BYFwioFY.js";
6
5
  import { t as loadSharedCliDefaults } from "./configShared-DtHMBCl7.js";
7
- import { n as isWebrtcSpec } from "./webrtcLink-CS4D-3G1.js";
8
- import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-iIvqiFWK.js";
6
+ import { n as isWorkingScreen, r as parseMenu, t as classifyNeedsInput } from "./needsInput-103zhZ90.js";
7
+ import { S as postmortemStartedAt, _ as filterSinceSeq, a as getCursor, b as maxSeq, f as readInbox, o as heartbeatWatcher, p as setCursor, r as clearWatcher, s as hostId, v as filterSinceTs, y as filterUnread } from "./notifyStore-Cbw2ZJ0X.js";
8
+ import { n as isWebrtcSpec } from "./webrtcLink-DyCj380i.js";
9
+ import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-iMhdZytp.js";
9
10
  import { spawn } from "child_process";
10
11
  import ms from "ms";
11
12
  import yargs from "yargs";
12
- import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
13
+ import { appendFile, mkdir, open, readFile, stat, writeFile } from "fs/promises";
13
14
  import { homedir } from "os";
14
15
  import path from "path";
15
- import { randomUUID } from "node:crypto";
16
16
  import { readFileSync } from "node:fs";
17
17
  import path$1 from "node:path";
18
- import os$1, { homedir as homedir$1, hostname as hostname$1, userInfo as userInfo$1 } from "node:os";
18
+ import { homedir as homedir$1, hostname as hostname$1, userInfo as userInfo$1 } from "node:os";
19
19
  import { fileURLToPath } from "node:url";
20
- import { randomBytes as randomBytes$1 } from "crypto";
20
+ import { randomBytes } from "crypto";
21
21
 
22
22
  //#region ts/identity.ts
23
23
  /**
@@ -357,83 +357,6 @@ function badgeLabel(id, defs = BADGE_DEFS) {
357
357
  return def.label.includes("$1") ? def.label.replace("$1", arg) : def.label;
358
358
  }
359
359
 
360
- //#endregion
361
- //#region ts/needsInput.ts
362
- function reTest(re, s) {
363
- return (re.global || re.sticky ? new RegExp(re.source, re.flags.replace(/[gy]/g, "")) : re).test(s);
364
- }
365
- function isChromeLine(s) {
366
- const t = s.trim();
367
- return !t || /^─+$/.test(t) || /^esc to (interrupt|cancel)/i.test(t) || /\? for shortcuts/.test(t) || /\d+%\s*until auto-compact/i.test(t);
368
- }
369
- /**
370
- * Returns a NeedsInput when the screen shows an unresolved selection menu, else
371
- * null. `cfg.working` short-circuits to null (an actively-working agent isn't
372
- * blocked). Pure + synchronous so it's trivially unit-testable.
373
- */
374
- function classifyNeedsInput(lines, cfg) {
375
- const patterns = cfg.needsInput ?? [];
376
- if (patterns.length === 0) return null;
377
- const text = lines.join("\n");
378
- if ((cfg.working ?? []).some((re) => reTest(re, text))) return null;
379
- if (!patterns.some((re) => reTest(re, text))) return null;
380
- let last = -1;
381
- for (let i = 0; i < lines.length; i++) if (patterns.some((re) => reTest(re, lines[i]))) last = i;
382
- const start = Math.max(0, last - 6);
383
- const end = Math.min(lines.length, last + 6);
384
- return { question: lines.slice(start, end).map((l) => l.trim()).filter((l) => l && !isChromeLine(l)).join(" • ").slice(0, 400) };
385
- }
386
- const OPTION_LINE = /^[\s❯›>▶◉○●·│*-]*?(\d+)\.\s/;
387
- /**
388
- * Parse the selection menu a `needs_input` agent is parked on into a cursor
389
- * position + the available option numbers, so a caller can compute how far the
390
- * cursor must move (Down/Up) to reach a target option. Returns null when the
391
- * screen isn't a menu (delegates that judgement to {@link classifyNeedsInput},
392
- * so `working` still wins) or no numbered cursor line is found. Pure — the
393
- * `ay select` action reuses the exact detection `ay ls` renders with.
394
- */
395
- function parseMenu(lines, cfg) {
396
- const ni = classifyNeedsInput(lines, cfg);
397
- if (!ni) return null;
398
- const patterns = cfg.needsInput ?? [];
399
- let cursorLine = -1;
400
- for (let i = 0; i < lines.length; i++) if (patterns.some((re) => reTest(re, lines[i]))) cursorLine = i;
401
- if (cursorLine < 0) return null;
402
- const cm = /(\d+)\./.exec(lines[cursorLine]);
403
- if (!cm) return null;
404
- const cursor = parseInt(cm[1], 10);
405
- const start = Math.max(0, cursorLine - 12);
406
- const end = Math.min(lines.length, cursorLine + 12);
407
- const options = [];
408
- for (let i = start; i < end; i++) {
409
- const m = OPTION_LINE.exec(lines[i]);
410
- if (m) {
411
- const v = parseInt(m[1], 10);
412
- if (!options.includes(v)) options.push(v);
413
- }
414
- }
415
- if (!options.includes(cursor)) options.push(cursor);
416
- options.sort((a, b) => a - b);
417
- return {
418
- cursor,
419
- options,
420
- question: ni.question
421
- };
422
- }
423
- /**
424
- * True when the rendered screen still shows a "busy" marker (config `working`,
425
- * e.g. claude's `esc to interrupt`). Paired with a long-quiet log this is the
426
- * `stuck` signal: a live spinner writes to the log every frame, so a busy marker
427
- * on screen WITHOUT recent output means the agent wedged mid-stream (a silent
428
- * API stream stall) rather than finishing. Pure + synchronous like the rest of
429
- * this module so it's trivially unit-testable.
430
- */
431
- function isWorkingScreen(lines, working) {
432
- if (!working?.length) return false;
433
- const text = lines.join("\n");
434
- return working.some((re) => reTest(re, text));
435
- }
436
-
437
360
  //#endregion
438
361
  //#region ts/lsWatch.ts
439
362
  /**
@@ -490,556 +413,6 @@ function toEvent(a, ts) {
490
413
  };
491
414
  }
492
415
 
493
- //#endregion
494
- //#region ts/notifyInbox.ts
495
- /**
496
- * Pure, fs-free core for the subagent→parent notification inbox — the storage
497
- * and watermark layer under `ay notify` / `ay notifyd`.
498
- *
499
- * Motivation (real incident): a parent agent fanned out sub-agents that finished
500
- * their work but sat at an idle `❯` prompt WITHOUT exiting (claude-yes does not
501
- * exit on idle). Claude Code's background-task notification only fires on process
502
- * EXIT, so the parent never learned the children went idle and left them parked
503
- * 16 minutes. `ay ls --watch` already streams transitions, but it is PULL: the
504
- * parent must run the watch loop. The whole point of this pain is the parent is
505
- * NOT watching. So we accumulate qualifying edges into a per-parent append-only
506
- * inbox that the parent drains on its own schedule (its Monitor loop), with a
507
- * persisted cursor so a parent that restarts reads only the unread edges.
508
- *
509
- * This module is the pure part (path math, NDJSON (de)serialization, seq/cursor
510
- * filtering, retention decisions) so it is trivially unit-testable, mirroring
511
- * `lsWatch.ts` / `needsInput.ts` / `resultEnvelope.ts`. The fs read/write + lock
512
- * + CLI live in `subcommands.ts`; the detection/debounce lives in
513
- * `notifyRouter.ts`.
514
- */
515
- /** Root dir for all notification state. */
516
- function notifyDir() {
517
- return path.join(agentYesHome(), "notify");
518
- }
519
- /** Per-host inbox directory (pids are only unique within a host). */
520
- function inboxDir(host) {
521
- return path.join(notifyDir(), "inbox", sanitizeHost(host));
522
- }
523
- /** Absolute path of one parent's append-only NDJSON inbox. */
524
- function inboxPath(host, parentPid) {
525
- return path.join(inboxDir(host), `${parentPid}.ndjson`);
526
- }
527
- /** Sidecar seq counter for a parent inbox (last allocated seq). */
528
- function seqPath(host, parentPid) {
529
- return path.join(inboxDir(host), `${parentPid}.seq`);
530
- }
531
- /** Per-consumer cursor directory for a parent inbox. */
532
- function cursorDir(host, parentPid) {
533
- return path.join(notifyDir(), "cursors", sanitizeHost(host), String(parentPid));
534
- }
535
- /** Absolute path of one consumer's cursor file for a parent inbox. */
536
- function cursorPath(host, parentPid, consumer = "parent") {
537
- return path.join(cursorDir(host, parentPid), `${sanitizeConsumer(consumer)}.json`);
538
- }
539
- /** The daemon singleton lock dir (mkdir-based, like pidStore). */
540
- function daemonLockDir() {
541
- return path.join(notifyDir(), "notifyd.lock");
542
- }
543
- /**
544
- * Owner metadata inside the lock dir ({pid, started_at, ts}) — the SINGLE source
545
- * of truth for the running daemon's identity, used for stale-lock steal and for
546
- * `notifyd status`/`stop` (a running daemon refreshes `ts` each tick).
547
- */
548
- function daemonLockOwnerPath() {
549
- return path.join(daemonLockDir(), "owner.json");
550
- }
551
- /** Registry dir of parents currently running `ay notify watch` (heartbeats). */
552
- function watchersDir() {
553
- return path.join(notifyDir(), "watchers");
554
- }
555
- /** Heartbeat file for one watching parent. */
556
- function watcherPath(parentPid) {
557
- return path.join(watchersDir(), `${parentPid}.json`);
558
- }
559
- /** A watcher heartbeat is "live" if refreshed within this window. */
560
- const WATCHER_TTL_MS = 15e3;
561
- function sanitizeSegment(s, fallback) {
562
- const cleaned = s.replace(/[^A-Za-z0-9._-]/g, "_");
563
- return cleaned === "" || cleaned === "." || cleaned === ".." ? fallback : cleaned;
564
- }
565
- function sanitizeHost(host) {
566
- return sanitizeSegment(host, "localhost");
567
- }
568
- function sanitizeConsumer(consumer) {
569
- return sanitizeSegment(consumer, "parent");
570
- }
571
- /** Serialize one event to its NDJSON line (no trailing newline). */
572
- function serializeEvent(ev) {
573
- return JSON.stringify(ev);
574
- }
575
- /**
576
- * Parse an inbox's raw text into events. Tolerant by design: a torn final line
577
- * (a writer mid-append) or any unparseable line is skipped, never throws — a
578
- * reader must survive a concurrent writer. Order is file order (= seq order,
579
- * since a single locked writer appends in increasing seq).
580
- */
581
- function parseInboxText(text) {
582
- const out = [];
583
- for (const line of text.split("\n")) {
584
- const t = line.trim();
585
- if (!t) continue;
586
- try {
587
- const ev = JSON.parse(t);
588
- if (isValidEvent(ev)) out.push(ev);
589
- } catch {}
590
- }
591
- return out;
592
- }
593
- const EDGES = new Set([
594
- "needs_input",
595
- "idle",
596
- "exited"
597
- ]);
598
- /**
599
- * Full shape validation at the storage boundary — a torn/partial line missing
600
- * the correlation fields must never flow through to a consumer's output. Every
601
- * event we surface has the fields a reader relies on.
602
- */
603
- function isValidEvent(ev) {
604
- if (!ev || typeof ev !== "object") return false;
605
- const e = ev;
606
- return typeof e.seq === "number" && typeof e.ts === "number" && typeof e.host === "string" && typeof e.parent_pid === "number" && typeof e.child_pid === "number" && typeof e.cli === "string" && typeof e.cwd === "string" && typeof e.edge === "string" && EDGES.has(e.edge) && typeof e.state === "string" && (e.question === null || typeof e.question === "string");
607
- }
608
- /** Next seq to allocate given the last stored seq counter (or the inbox max). */
609
- function nextSeq(lastSeq) {
610
- return (Number.isFinite(lastSeq) && lastSeq > 0 ? lastSeq : 0) + 1;
611
- }
612
- /** Highest seq present in a parsed inbox (0 when empty). */
613
- function maxSeq(events) {
614
- let m = 0;
615
- for (const e of events) if (e.seq > m) m = e.seq;
616
- return m;
617
- }
618
- /**
619
- * Filter events for `--since <seq>`: strictly-greater than the given seq. A
620
- * `since` of 0/undefined returns everything.
621
- */
622
- function filterSinceSeq(events, sinceSeq) {
623
- if (!sinceSeq || sinceSeq <= 0) return events.slice();
624
- return events.filter((e) => e.seq > sinceSeq);
625
- }
626
- /** Filter events after a wall-clock `--since <ts>` (inclusive lower bound). */
627
- function filterSinceTs(events, sinceTs) {
628
- if (!sinceTs || sinceTs <= 0) return events.slice();
629
- return events.filter((e) => e.ts >= sinceTs);
630
- }
631
- /** Unread = seq strictly greater than the consumer's cursor. */
632
- function filterUnread(events, cursorSeq) {
633
- return events.filter((e) => e.seq > (cursorSeq > 0 ? cursorSeq : 0));
634
- }
635
- /** Parse a cursor file's text; a missing/garbage cursor reads as seq 0. */
636
- function parseCursor(text) {
637
- if (!text) return { seq: 0 };
638
- try {
639
- const c = JSON.parse(text);
640
- return { seq: Number.isFinite(c?.seq) && c.seq > 0 ? c.seq : 0 };
641
- } catch {
642
- return { seq: 0 };
643
- }
644
- }
645
- function serializeCursor(seq) {
646
- return JSON.stringify({ seq: Math.max(0, Math.floor(seq)) });
647
- }
648
- /**
649
- * Which incarnation of a parent pid a POSTMORTEM read should show (pure).
650
- *
651
- * The normal read path resolves the parent's `started_at` from the registry and
652
- * fails closed when there is no live record — deliberately, so a recycled pid
653
- * can't read a prior session's inbox. That also makes a FINISHED parent's inbox
654
- * permanently uninspectable, which is exactly when an operator wants to see what
655
- * the children reported. Postmortem keeps the identity guard but takes the
656
- * identity from the inbox's own `parent_started_at` stamps instead.
657
- *
658
- * - An explicit choice always wins.
659
- * - One distinct stamp is unambiguous — use it.
660
- * - Several mean the pid was reused across sessions and the file holds more than
661
- * one agent's edges: refuse and list them rather than guess, because guessing
662
- * would show one agent's children under another's name.
663
- * - None (a legacy, identity-less inbox) yields 0 = "no filter"; there is nothing
664
- * to disambiguate and nothing to mis-attribute.
665
- *
666
- * Issue #169 item 6.
667
- */
668
- function postmortemStartedAt(events, explicit) {
669
- if (Number.isFinite(explicit) && explicit > 0) return explicit;
670
- const distinct = [...new Set(events.map((e) => e.parent_started_at ?? 0).filter((v) => v > 0))].sort((a, b) => a - b);
671
- if (distinct.length === 0) return 0;
672
- if (distinct.length === 1) return distinct[0];
673
- throw new Error(`inbox holds edges from ${distinct.length} incarnations of this pid (started_at ${distinct.join(", ")}) — pass --started-at <ms> to pick one.`);
674
- }
675
- /** Byte size one event occupies in the inbox file (its line + newline). */
676
- function eventBytes(events) {
677
- let n = 0;
678
- for (const e of events) n += serializeEvent(e).length + 1;
679
- return n;
680
- }
681
- /**
682
- * EMERGENCY rotation (pure): keep the newest events that fit `capBytes`,
683
- * IGNORING the un-acked watermark that {@link rotateKeep} protects.
684
- *
685
- * This is the escape hatch for the one way an inbox can grow without bound: a
686
- * parent that watches WITHOUT `--ack` (the default, at-least-once) never
687
- * advances its cursor, so every event stays protected and normal rotation can
688
- * trim nothing. That is the right default — an unacked edge must not be dropped
689
- * — but "right" stops being right once the file threatens the disk. Past a hard
690
- * cap well above the soft one, dropping the OLDEST unacked events (loudly) beats
691
- * unbounded growth.
692
- *
693
- * `minKeep` still floors the result, so the newest edges — the ones a parent is
694
- * most likely to still act on — are never the ones sacrificed.
695
- *
696
- * Issue #169 item 3.
697
- */
698
- function emergencyKeep(events, capBytes, minKeep = 100) {
699
- if (events.length <= minKeep) return events.slice();
700
- const kept = [];
701
- let bytes = 0;
702
- for (let i = events.length - 1; i >= 0; i--) {
703
- const e = events[i];
704
- bytes += serializeEvent(e).length + 1;
705
- if (kept.length < minKeep || bytes <= capBytes) kept.push(e);
706
- else break;
707
- }
708
- kept.reverse();
709
- return kept;
710
- }
711
- /**
712
- * Rotation decision (pure): given an inbox's events, a byte cap, and the minimum
713
- * un-acked cursor across ALL consumers, return the events to KEEP after
714
- * rotating. Critically, an event with `seq > protectAboveSeq` is NEVER dropped —
715
- * that is the at-least-once guarantee: we must not evict an edge no consumer has
716
- * acknowledged yet, even under the byte cap. Below that watermark we keep the
717
- * newest events that fit the cap (and at least `minKeep`).
718
- */
719
- function rotateKeep(events, capBytes, protectAboveSeq = 0, minKeep = 100) {
720
- if (events.length <= minKeep) return events.slice();
721
- const kept = [];
722
- let bytes = 0;
723
- for (let i = events.length - 1; i >= 0; i--) {
724
- const e = events[i];
725
- const line = serializeEvent(e) + "\n";
726
- bytes += line.length;
727
- if (e.seq > protectAboveSeq || kept.length < minKeep || bytes <= capBytes) kept.push(e);
728
- else break;
729
- }
730
- kept.reverse();
731
- return kept;
732
- }
733
-
734
- //#endregion
735
- //#region ts/notifyStore.ts
736
- /**
737
- * Filesystem side of the notification inbox — the impure half of the pure
738
- * `notifyInbox.ts` path/serialization core. Handles the locked append (so
739
- * concurrent daemon ticks / future writers never interleave a seq), inbox and
740
- * cursor reads/writes, and retention GC.
741
- *
742
- * The lock is a mkdir-based advisory lock (same primitive `pidStore` uses), held
743
- * only for the microseconds of a read-counter → append-line → write-counter, so
744
- * contention is negligible even with many parents.
745
- */
746
- /** Stable local host id — the pid namespace is per-host. */
747
- function hostId() {
748
- return os$1.hostname() || "localhost";
749
- }
750
- function pidAlive(pid) {
751
- if (!Number.isFinite(pid) || pid <= 0) return false;
752
- try {
753
- process.kill(pid, 0);
754
- return true;
755
- } catch (e) {
756
- return e.code === "EPERM";
757
- }
758
- }
759
- /**
760
- * Pure steal decision for a per-inbox lock (extracted so it's unit-testable):
761
- * steal the lock ONLY when its holder is provably gone (torn/missing owner on a
762
- * lock that has EXISTED longer than the grace, or a dead pid) or its heartbeat is
763
- * stale — NEVER merely because the contender has waited a while.
764
- *
765
- * Crucially, the torn-owner grace is measured from the LOCK INSTANCE's age
766
- * (`lockAgeMs` = now − lockDir mtime), NOT from how long the contender has been
767
- * waiting. Otherwise a contender that waited out one holder could instantly steal
768
- * the NEXT holder's freshly-created lock during its mkdir→write-owner window
769
- * (torn but brand-new) — letting two writers into the critical section.
770
- */
771
- function shouldStealLock(ownerRaw, now, opts) {
772
- const graceMs = opts.graceMs ?? 1e3;
773
- let ownerPid = 0;
774
- let ownerTs = 0;
775
- let parsed = false;
776
- try {
777
- const o = JSON.parse(ownerRaw);
778
- ownerPid = o?.pid ?? 0;
779
- ownerTs = o?.ts ?? 0;
780
- parsed = true;
781
- } catch {}
782
- if (!parsed || ownerPid <= 0 || ownerTs <= 0) return opts.lockAgeMs > graceMs;
783
- const holderDead = ownerPid !== opts.selfPid && !opts.isAlive(ownerPid);
784
- const holderStale = now - ownerTs > opts.staleMs;
785
- return holderDead || holderStale;
786
- }
787
- async function ensureDir(dir) {
788
- await mkdir(dir, { recursive: true }).catch(() => {});
789
- }
790
- /**
791
- * Atomically overwrite a single file (temp + rename) so a concurrent reader never
792
- * observes a torn/partial write — the sweep invariant for every single-file
793
- * mutation (cursor, watcher heartbeat, lock owner). Returns whether it landed.
794
- */
795
- async function atomicWrite(file, data) {
796
- const tmp = `${file}.${randomUUID()}.tmp`;
797
- try {
798
- await writeFile(tmp, data);
799
- await rename(tmp, file);
800
- return true;
801
- } catch {
802
- await rm(tmp, { force: true }).catch(() => {});
803
- return false;
804
- }
805
- }
806
- /**
807
- * Acquire a per-inbox lock; returns a release fn. Ownership is proven ONLY by a
808
- * successful `mkdir(recursive:false)` (atomic exclusive create), and stealing is
809
- * driven by HOLDER LIVENESS, not elapsed wait time: the holder writes an
810
- * `owner` file ({pid, ts}) on acquire AND heartbeats its ts for the whole time
811
- * it holds the lock, so a contender steals ONLY when that holder is dead or its
812
- * heartbeat is stale. A live holder whose critical section legitimately runs long
813
- * (a big GC rewrite, a slow disk) is never robbed — its heartbeat stays fresh;
814
- * and a wedged holder stops heartbeating, so staleMs still reclaims it. There is
815
- * deliberately NO wall-clock backstop: elapsed wait time alone never steals, so a
816
- * live holder is inviolable.
817
- */
818
- function isTransientLockMkdirError(code, platform = process.platform) {
819
- if (code === "EEXIST") return true;
820
- return platform === "win32" && (code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES");
821
- }
822
- async function acquireLock(lockDir, staleMs = 3e4) {
823
- const ownerFile = path.join(lockDir, "owner");
824
- const token = randomUUID();
825
- const readOwnerToken = async () => {
826
- try {
827
- return JSON.parse(await readFile(ownerFile, "utf8")).token ?? null;
828
- } catch {
829
- return null;
830
- }
831
- };
832
- const stampOwner = async () => {
833
- const tmp = `${ownerFile}.${token}.tmp`;
834
- try {
835
- await writeFile(tmp, JSON.stringify({
836
- pid: process.pid,
837
- ts: Date.now(),
838
- token
839
- }));
840
- await rename(tmp, ownerFile);
841
- return true;
842
- } catch {
843
- await rm(tmp, { force: true }).catch(() => {});
844
- return false;
845
- }
846
- };
847
- const lockAgeMs = async () => {
848
- const m = await stat(lockDir).then((s) => s.mtimeMs).catch(() => Date.now());
849
- return Date.now() - m;
850
- };
851
- let transientErrors = 0;
852
- for (;;) try {
853
- await mkdir(lockDir, { recursive: false });
854
- if (!await stampOwner()) {
855
- await rm(lockDir, {
856
- recursive: true,
857
- force: true
858
- }).catch(() => {});
859
- await new Promise((r) => setTimeout(r, 15));
860
- continue;
861
- }
862
- const beat = setInterval(() => {
863
- (async () => {
864
- if (await readOwnerToken() !== token) {
865
- clearInterval(beat);
866
- return;
867
- }
868
- await stampOwner();
869
- })();
870
- }, Math.max(500, Math.floor(staleMs / 3)));
871
- if (typeof beat.unref === "function") beat.unref();
872
- return async () => {
873
- clearInterval(beat);
874
- if (await readOwnerToken() === token) await rm(lockDir, {
875
- recursive: true,
876
- force: true
877
- }).catch(() => {});
878
- };
879
- } catch (e) {
880
- const code = e.code;
881
- if (!isTransientLockMkdirError(code)) throw e;
882
- if (code !== "EEXIST") {
883
- if (++transientErrors > 200) throw e;
884
- await new Promise((r) => setTimeout(r, 15));
885
- continue;
886
- }
887
- if (shouldStealLock(await readFile(ownerFile, "utf8").catch(() => ""), Date.now(), {
888
- staleMs,
889
- lockAgeMs: await lockAgeMs(),
890
- selfPid: process.pid,
891
- isAlive: pidAlive
892
- })) {
893
- await rm(lockDir, {
894
- recursive: true,
895
- force: true
896
- }).catch(() => {});
897
- continue;
898
- }
899
- await new Promise((r) => setTimeout(r, 15));
900
- }
901
- }
902
- /**
903
- * Append one event to a parent's inbox under lock, allocating the next seq. The
904
- * caller passes the event WITHOUT seq (we stamp it). Returns the stamped seq.
905
- */
906
- async function appendEvent(parentPid, ev) {
907
- const host = ev.host;
908
- const dir = inboxDir(host);
909
- await ensureDir(dir);
910
- const release = await acquireLock(path.join(dir, `${parentPid}.lock`));
911
- try {
912
- let last = 0;
913
- const counterRaw = await readFile(seqPath(host, parentPid), "utf8").catch(() => "");
914
- const parsed = parseInt(counterRaw.trim(), 10);
915
- if (Number.isFinite(parsed) && parsed > 0) last = parsed;
916
- else last = maxSeq(parseInboxText(await readFile(inboxPath(host, parentPid), "utf8").catch(() => "")));
917
- const seq = nextSeq(last);
918
- const full = {
919
- ...ev,
920
- seq
921
- };
922
- await writeFile(seqPath(host, parentPid), String(seq));
923
- await appendFile(inboxPath(host, parentPid), serializeEvent(full) + "\n");
924
- return seq;
925
- } finally {
926
- await release();
927
- }
928
- }
929
- /** Read + parse a parent's inbox (empty array when none). */
930
- async function readInbox(host, parentPid) {
931
- return parseInboxText(await readFile(inboxPath(host, parentPid), "utf8").catch(() => ""));
932
- }
933
- /** All parent pids that currently have an inbox on this host. */
934
- async function listInboxParents(host) {
935
- const names = await readdir(inboxDir(host)).catch(() => []);
936
- const pids = [];
937
- for (const n of names) {
938
- const m = /^(\d+)\.ndjson$/.exec(n);
939
- if (m) pids.push(parseInt(m[1], 10));
940
- }
941
- return pids;
942
- }
943
- async function getCursor(host, parentPid, consumer = "parent") {
944
- return parseCursor(await readFile(cursorPath(host, parentPid, consumer), "utf8").catch(() => null)).seq;
945
- }
946
- /**
947
- * Minimum cursor across ALL consumers of a parent inbox — the watermark below
948
- * which rotation may evict events (nothing above it has been acked by every
949
- * reader). Returns 0 when there are no consumers — which PROTECTS EVERYTHING:
950
- * with a 0 watermark, `rotateKeep` treats every event as unacked and evicts
951
- * nothing (an inbox nobody reads is never trimmed).
952
- */
953
- async function minConsumerCursor(host, parentPid) {
954
- const cursors = (await readdir(cursorDir(host, parentPid)).catch(() => [])).filter((n) => n.endsWith(".json")).map((n) => n.slice(0, -5));
955
- if (cursors.length === 0) return 0;
956
- let min = Infinity;
957
- for (const c of cursors) min = Math.min(min, await getCursor(host, parentPid, c));
958
- return Number.isFinite(min) ? min : 0;
959
- }
960
- async function setCursor(host, parentPid, seq, consumer = "parent") {
961
- const p = cursorPath(host, parentPid, consumer);
962
- await ensureDir(path.dirname(p));
963
- await atomicWrite(p, serializeCursor(seq));
964
- }
965
- /**
966
- * Retention: delete inboxes (+ their seq counter + cursors) for parents that are
967
- * both dead and unreferenced by any live child. `capBytes` also rotates a live
968
- * inbox that has grown past the cap, keeping the newest events.
969
- */
970
- async function gcInboxes(host, livePids, liveChildParentPids, capBytes = 10 * 1024 * 1024, hardCapBytes = capBytes * 4) {
971
- const parents = await listInboxParents(host);
972
- for (const p of parents) {
973
- const release = await acquireLock(path.join(inboxDir(host), `${p}.lock`));
974
- try {
975
- if (!livePids.has(p) && !liveChildParentPids.has(p)) {
976
- await rm(inboxPath(host, p), { force: true }).catch(() => {});
977
- await rm(seqPath(host, p), { force: true }).catch(() => {});
978
- await rm(cursorDir(host, p), {
979
- recursive: true,
980
- force: true
981
- }).catch(() => {});
982
- continue;
983
- }
984
- const size = await stat(inboxPath(host, p)).then((s) => s.size).catch(() => 0);
985
- if (size <= capBytes) continue;
986
- const events = await readInbox(host, p);
987
- let kept = rotateKeep(events, capBytes, await minConsumerCursor(host, p));
988
- let dropped = [];
989
- if (eventBytes(kept) > hardCapBytes) {
990
- const before = kept;
991
- kept = emergencyKeep(kept, capBytes);
992
- dropped = before.slice(0, before.length - kept.length);
993
- }
994
- if (kept.length < events.length) {
995
- await writeFile(inboxPath(host, p), kept.map(serializeEvent).join("\n") + "\n");
996
- if (dropped.length > 0) logger.error(`[notify] EMERGENCY TRIM: inbox for parent ${p} exceeded the hard cap ${hardCapBytes} bytes — dropped ${dropped.length} UNACKED events (seq ${dropped[0].seq}..${dropped[dropped.length - 1].seq}). The consumer's cursor is not advancing: run \`ay notify watch --ack\` (or \`ay notify cursor set <seq>\`) for parent ${p}.`);
997
- } else logger.warn(`[notify] inbox for parent ${p} is ${size} bytes (> soft cap ${capBytes}) but all events are unacked — cursor not advancing?`);
998
- } finally {
999
- await release();
1000
- }
1001
- }
1002
- }
1003
- /** Register / refresh this parent's watch heartbeat (atomically, so a concurrent
1004
- * liveWatchers never reads a torn file and transiently drops the watcher). */
1005
- async function heartbeatWatcher(parentPid, startedAt) {
1006
- await ensureDir(watchersDir());
1007
- await atomicWrite(watcherPath(parentPid), JSON.stringify({
1008
- pid: parentPid,
1009
- started_at: startedAt,
1010
- ts: Date.now(),
1011
- watch_pid: process.pid
1012
- }));
1013
- }
1014
- /** Remove this parent's watch heartbeat (on watch exit). */
1015
- async function clearWatcher(parentPid) {
1016
- await rm(watcherPath(parentPid), { force: true }).catch(() => {});
1017
- }
1018
- /**
1019
- * Live watchers as a map of parent pid → the parent's own `started_at` (its
1020
- * authoritative self-reported start time, so the daemon never has to guess it
1021
- * from the registry and stamp a 0). A watcher counts as live ONLY if its
1022
- * heartbeat is fresh AND its process is actually alive — so a crashed `watch`
1023
- * whose heartbeat lingers for the TTL doesn't keep the daemon writing to a dead
1024
- * parent's inbox (which would violate "nothing happens unless you watch"). A
1025
- * heartbeat with a missing/non-positive `started_at` is treated as NOT live: the
1026
- * daemon's cross-session scope guard keys off a positive parent start time, so a
1027
- * 0 would defeat it — fail closed.
1028
- */
1029
- async function liveWatchers(now = Date.now()) {
1030
- const names = await readdir(watchersDir()).catch(() => []);
1031
- const live = /* @__PURE__ */ new Map();
1032
- for (const n of names) {
1033
- if (!n.endsWith(".json")) continue;
1034
- const raw = await readFile(path.join(watchersDir(), n), "utf8").catch(() => "");
1035
- try {
1036
- const o = JSON.parse(raw);
1037
- if (typeof o?.pid === "number" && typeof o?.started_at === "number" && o.started_at > 0 && typeof o?.ts === "number" && now - o.ts <= WATCHER_TTL_MS && pidAlive(o.pid) && (typeof o.watch_pid !== "number" || o.watch_pid <= 0 || pidAlive(o.watch_pid))) live.set(o.pid, o.started_at);
1038
- } catch {}
1039
- }
1040
- return live;
1041
- }
1042
-
1043
416
  //#endregion
1044
417
  //#region ts/resultEnvelope.ts
1045
418
  /**
@@ -1531,7 +904,7 @@ async function runSubcommand(argv) {
1531
904
  case "tail": return await cmdRead(rest, { mode: "tail" });
1532
905
  case "head": return await cmdRead(rest, { mode: "head" });
1533
906
  case "hist":
1534
- case "history": return await (await import("./hist-CoXBBjJt.js")).cmdHist(rest);
907
+ case "history": return await (await import("./hist-BpocAe1N.js")).cmdHist(rest);
1535
908
  case "send": return await cmdSend(rest);
1536
909
  case "msgs": return await cmdMsgs(rest);
1537
910
  case "key": return await cmdKey(rest);
@@ -1543,56 +916,56 @@ async function runSubcommand(argv) {
1543
916
  case "restart": return await cmdRestart(rest);
1544
917
  case "note": return await cmdNote(rest);
1545
918
  case "todo": {
1546
- const { runTodoSubcommand } = await import("./todoCli-DZmOmcvd.js");
919
+ const { runTodoSubcommand } = await import("./todoCli-c_R7D7Y-.js");
1547
920
  return runTodoSubcommand(rest);
1548
921
  }
1549
922
  case "ch":
1550
923
  case "channels": {
1551
- const { cmdCh } = await import("./channels-BwpN_8mu.js");
924
+ const { cmdCh } = await import("./channels-DQaZbare.js");
1552
925
  return cmdCh(rest);
1553
926
  }
1554
927
  case "term": {
1555
- const { cmdTerm } = await import("./terminal-CybRgMZu.js");
928
+ const { cmdTerm } = await import("./terminal-Dm32SXU-.js");
1556
929
  return cmdTerm(rest);
1557
930
  }
1558
931
  case "widget": {
1559
- const { cmdWidget } = await import("./widget-y5Bpvwsa.js");
932
+ const { cmdWidget } = await import("./widget-DXtaX3QR.js");
1560
933
  return cmdWidget(rest);
1561
934
  }
1562
935
  case "mint": {
1563
- const { cmdMint } = await import("./widget-y5Bpvwsa.js");
936
+ const { cmdMint } = await import("./widget-DXtaX3QR.js");
1564
937
  return cmdMint(rest);
1565
938
  }
1566
939
  case "serve": {
1567
- const { cmdServe } = await import("./serve-CpGJD2Kt.js");
940
+ const { cmdServe } = await import("./serve-ucVXOci1.js");
1568
941
  return cmdServe(rest);
1569
942
  }
1570
943
  case "tray": {
1571
- const { cmdTray } = await import("./trayApp-CmwwBg8v.js");
944
+ const { cmdTray } = await import("./trayApp-CZbwLK9t.js");
1572
945
  return cmdTray(rest);
1573
946
  }
1574
947
  case "setup": {
1575
- const { cmdSetup } = await import("./setup-B-iQKsQW.js");
948
+ const { cmdSetup } = await import("./setup-De7uYRZb.js");
1576
949
  return cmdSetup(rest);
1577
950
  }
1578
951
  case "ws": {
1579
- const { cmdWs } = await import("./ws-CH8GzcQn.js");
952
+ const { cmdWs } = await import("./ws-CqlS5JhN.js");
1580
953
  return cmdWs(rest);
1581
954
  }
1582
955
  case "schedule": {
1583
- const { cmdSchedule } = await import("./schedule-i4o5SJ_h.js");
956
+ const { cmdSchedule } = await import("./schedule-DbLpjgl5.js");
1584
957
  return cmdSchedule(rest);
1585
958
  }
1586
959
  case "remote": {
1587
- const { cmdRemote } = await import("./remotes-DWVw-Q7V.js");
960
+ const { cmdRemote } = await import("./remotes-Dg_JscKk.js");
1588
961
  return cmdRemote(rest);
1589
962
  }
1590
963
  case "expose": {
1591
- const { cmdExpose } = await import("./expose-BG17GV7D.js");
964
+ const { cmdExpose } = await import("./expose-BXn6My6A.js");
1592
965
  return cmdExpose(rest);
1593
966
  }
1594
967
  case "callback": {
1595
- const { cmdCallback } = await import("./callback-L4k-v2GG.js");
968
+ const { cmdCallback } = await import("./callback-Da7MRLh6.js");
1596
969
  return cmdCallback(rest);
1597
970
  }
1598
971
  case "reap":
@@ -1656,7 +1029,19 @@ async function buildAgentContextSection(self) {
1656
1029
  latest: false,
1657
1030
  cwdScope: null
1658
1031
  })).find((r) => r.wrapper_pid === self.parent_pid) : void 0;
1659
- return `You are running inside an agent:\n ${`You are agent pid ${self.pid} (${self.cli}) in ${shortenPath(self.cwd)}.`}\n ${!hasParentPid ? `Top-level agent — no parent (started from a human shell or scheduler).` : parent ? `Spawned by agent pid ${parent.pid} (${parent.cli}) in ${shortenPath(parent.cwd)}.` : `Nested under a parent (wrapper pid ${self.parent_pid}) whose record isn't in the local registry.`}\n\nAs an agent, you can:\n Spawn a sub-agent:\n ay <cli> -- "<prompt>" auto-links as your child\n ay claude --model sonnet --advisor opus -- "<prompt>" routine task\n ay claude --model opus --advisor fable -- "<prompt>" complex task\n (pick --model by task complexity so easy tasks don't cost like hard ones;\n --advisor is a claude-cli flag — only takes effect for claude/cy)\n List agents (your children nest under your own pid in the tree):\n ay ls --cwd ${shortenPath(self.cwd)}\n Watch agent state changes, scoped to your workspace:\n ay ls --watch --cwd ${shortenPath(self.cwd)}\n (NDJSON stream of state changes across every matched agent — one watcher\n for the whole fan-out instead of N \`ay status --watch\`es)\n Read one sub-agent's output:\n ay tail -f <pid> follow live output (no single command tails\n many agents' content at once yet — loop\n \`ay ls --json\` pids into per-pid \`ay tail\`)\n\n`;
1032
+ const whoAmI = `You are agent pid ${self.pid} (${self.cli}) in ${shortenPath(self.cwd)}.`;
1033
+ const parentLine = !hasParentPid ? `Top-level agent — no parent (started from a human shell or scheduler).` : parent ? `Spawned by agent pid ${parent.pid} (${parent.cli}) in ${shortenPath(parent.cwd)}.` : `Nested under a parent (wrapper pid ${self.parent_pid}) whose record isn't in the local registry.`;
1034
+ const dutyLine = parent ? ` You owe it a report: \`ay send ${parent.agent_id || parent.pid} "..."\` when you finish, and\n when you are blocked. It is not watching your terminal.\n` : ``;
1035
+ return `You are running inside an agent:\n ${whoAmI}\n ${parentLine}\n` + dutyLine + `
1036
+ As an agent, you can:
1037
+ Spawn a sub-agent:
1038
+ ay <cli> -- "<prompt>" auto-links as your child
1039
+ ay claude --model sonnet --advisor opus -- "<prompt>" routine task
1040
+ ay claude --model opus --advisor fable -- "<prompt>" complex task
1041
+ (pick --model by task complexity so easy tasks don't cost like hard ones;
1042
+ --advisor is a claude-cli flag — only takes effect for claude/cy)
1043
+ List agents (your children nest under your own pid in the tree):
1044
+ ay ls --cwd ${shortenPath(self.cwd)}\n Watch agent state changes, scoped to your workspace:\n ay ls --watch --cwd ${shortenPath(self.cwd)}\n (NDJSON stream of state changes across every matched agent — one watcher\n for the whole fan-out instead of N \`ay status --watch\`es)\n Read one sub-agent's output:\n ay tail -f <pid> follow live output (no single command tails\n many agents' content at once yet — loop\n \`ay ls --json\` pids into per-pid \`ay tail\`)\n\n`;
1660
1045
  }
1661
1046
  async function cmdHelp(managerCommands = true) {
1662
1047
  const setupLine = managerCommands ? ` ay setup guided setup: pick a workspace, share to agent-yes.com\n` : ``;
@@ -2043,7 +1428,7 @@ async function fetchRemoteRecordsRaw(url, token, opts) {
2043
1428
  let base = url;
2044
1429
  let bearer = token;
2045
1430
  if (isWebrtcSpec(url)) {
2046
- const { startWebrtcBridge } = await import("./webrtcRemote-zQybN9IR.js");
1431
+ const { startWebrtcBridge } = await import("./webrtcRemote-BDg0jjTs.js");
2047
1432
  bridge = await startWebrtcBridge(url);
2048
1433
  base = bridge.baseUrl;
2049
1434
  bearer = bridge.token;
@@ -3482,7 +2867,7 @@ async function cmdSend(rest) {
3482
2867
  let suffix = "";
3483
2868
  let nonce;
3484
2869
  if (sender.agent && !isSlashCommand(body) && !raw) {
3485
- nonce = randomBytes$1(4).toString("hex");
2870
+ nonce = randomBytes(4).toString("hex");
3486
2871
  const identity = formatIdentity({
3487
2872
  cwd: sender.agent.cwd,
3488
2873
  pid: sender.agent.pid
@@ -4080,7 +3465,7 @@ async function cmdAttach(rest) {
4080
3465
  const winsizeDir = path.join(ayHome, "winsize");
4081
3466
  await mkdir(winsizeDir, { recursive: true });
4082
3467
  const winsizePath = path.join(winsizeDir, String(record.pid));
4083
- const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-CpGJD2Kt.js");
3468
+ const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-ucVXOci1.js");
4084
3469
  const daemonBase = await resolveDaemonHttpBase().catch(() => null);
4085
3470
  const daemonToken = daemonBase ? await loadTokenReadOnly().catch(() => null) ?? "" : "";
4086
3471
  const capViewer = `attach:${process.pid}`;
@@ -4790,7 +4175,7 @@ async function cmdNotify(rest) {
4790
4175
  }
4791
4176
  const ensure = async () => {
4792
4177
  if (!argv["ensure-daemon"]) return;
4793
- const { ensureDaemon } = await import("./notifyDaemon-BL1tmxUK.js");
4178
+ const { ensureDaemon } = await import("./notifyDaemon-BFzqcdN1.js");
4794
4179
  await ensureDaemon().catch(() => null);
4795
4180
  };
4796
4181
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4864,7 +4249,7 @@ async function cmdNotifyCursor(args) {
4864
4249
  }
4865
4250
  async function cmdNotifyd(rest) {
4866
4251
  const sub = rest[0] ?? "status";
4867
- const daemon = await import("./notifyDaemon-BL1tmxUK.js");
4252
+ const daemon = await import("./notifyDaemon-BFzqcdN1.js");
4868
4253
  switch (sub) {
4869
4254
  case "run": return daemon.runDaemon();
4870
4255
  case "once": return daemon.runDaemon({ once: true });
@@ -4890,5 +4275,5 @@ async function cmdNotifyd(rest) {
4890
4275
  }
4891
4276
 
4892
4277
  //#endregion
4893
- export { isTransientLockMkdirError as $, readLogForRender as A, resolveResumeArgs as B, isUnknownManagerToken as C, matchKeyword as D, listRecords as E, renderLogTailLines as F, stopTipForCli as G, runSubcommand as H, renderRawLog as I, writeKeysPaced as J, submitAndConfirm as K, renderRawLogLines as L, readPtysize as M, recentMessageEdges as N, menuSelectKeys as O, recentReadEdges as P, hostId as Q, resolveOne as R, isSubcommand as S, lastStdinAt as T, snapshotStatus as U, restartHintLines as V, stdinActivityPath as W, appendEvent as X, writeToIpc as Y, gcInboxes as Z, isAgentStuck as _, backoffWhileTyping as a, daemonLockOwnerPath as at, isPidAlive as b, controlCodeFromName as c, deriveLiveStatus as d, listInboxParents as et, extractBadges as f, finalizedLines as g, extractTaskCounts as h, TYPING_WINDOW_MS as i, daemonLockDir as it, readNotes as j, readAgentPtysize as k, cursorAbs as l, extractNeedsInput as m, MAX_RENDER_BYTES as n, readInbox as nt, cmdDeepseek as o, notifyDir as ot, extractMenu as p, waitForLogQuiet as q, READ_PAGE_DEFAULT as r, shouldStealLock as rt, cmdHelp as s, TYPING_BADGE as st, GRACEFUL_EXIT_COMMANDS as t, liveWatchers as tt, deriveLiveState as u, isBareManagerInvocation as v, isUserTyping as w, isSlashCommand as x, isExitRequest as y, resolveReadWindow as z };
4894
- //# sourceMappingURL=subcommands-CQPnT5qu.js.map
4278
+ export { menuSelectKeys as A, resolveOne as B, isSubcommand as C, lastStdinAt as D, lastReadAt as E, recentMessageEdges as F, snapshotStatus as G, resolveResumeArgs as H, recentReadEdges as I, submitAndConfirm as J, stdinActivityPath as K, renderLogTailLines as L, readLogForRender as M, readNotes as N, listRecords as O, readPtysize as P, TYPING_BADGE as Q, renderRawLog as R, isSlashCommand as S, isUserTyping as T, restartHintLines as U, resolveReadWindow as V, runSubcommand as W, writeKeysPaced as X, waitForLogQuiet as Y, writeToIpc as Z, finalizedLines as _, TYPING_WINDOW_MS as a, isExitRequest as b, cmdHelp as c, deriveLiveState as d, deriveLiveStatus as f, extractTaskCounts as g, extractNeedsInput as h, READ_WINDOW_MS as i, readAgentPtysize as j, matchKeyword as k, controlCodeFromName as l, extractMenu as m, MAX_RENDER_BYTES as n, backoffWhileTyping as o, extractBadges as p, stopTipForCli as q, READ_PAGE_DEFAULT as r, cmdDeepseek as s, GRACEFUL_EXIT_COMMANDS as t, cursorAbs as u, isAgentStuck as v, isUnknownManagerToken as w, isPidAlive as x, isBareManagerInvocation as y, renderRawLogLines as z };
4279
+ //# sourceMappingURL=subcommands-DtF31RoW.js.map