agent-yes 1.139.0 → 1.140.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.
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-Cjaqub-v.js";
1
+ import { t as CLIS_CONFIG } from "./ts-Bal_DlNX.js";
2
2
 
3
3
  //#region ts/SUPPORTED_CLIS.ts
4
4
  const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
5
5
 
6
6
  //#endregion
7
7
  export { SUPPORTED_CLIS as t };
8
- //# sourceMappingURL=SUPPORTED_CLIS-BFecLJMA.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-DnhSdiHO.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-Bal_DlNX.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-BRJsYunz.js";
4
+ import "./pidStore-fqXqTKkh.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DnhSdiHO.js";
7
+
8
+ export { SUPPORTED_CLIS };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-zdFbz1ST.js";
3
3
  import { n as logger } from "./logger-CDIsZ-Pp.js";
4
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-FREexFyS.js";
4
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-BRJsYunz.js";
5
5
  import { argv } from "process";
6
6
  import { execFileSync, spawn } from "child_process";
7
7
  import ms from "ms";
@@ -480,7 +480,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
480
480
  const rawArg = process.argv[2];
481
481
  const managerCommands = !invokedCliName(process.argv);
482
482
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
483
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BcjTv_7c.js");
483
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-jcuTXpcx.js");
484
484
  if (isHelpFlag && process.argv.length === 3) {
485
485
  cmdHelp(managerCommands);
486
486
  process.exit(0);
@@ -513,7 +513,7 @@ if (config.useRust) {
513
513
  }
514
514
  }
515
515
  if (rustBinary) {
516
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-vtxMZB6R.js");
516
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-vnAMkGCv.js");
517
517
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
518
518
  if (config.verbose) {
519
519
  console.log(`[rust] Using binary: ${rustBinary}`);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-Cjaqub-v.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-Bal_DlNX.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-FREexFyS.js";
3
+ import "./versionChecker-BRJsYunz.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts-Cjaqub-v.js";
1
+ import "./ts-Bal_DlNX.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-FREexFyS.js";
3
+ import "./versionChecker-BRJsYunz.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BFecLJMA.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DnhSdiHO.js";
7
7
  import { n as resolveSpawnCwd } from "./workspaceConfig-BCOqRBEW.js";
8
8
  import { createHash } from "node:crypto";
9
9
 
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
141
141
 
142
142
  //#endregion
143
143
  export { cmdSchedule };
144
- //# sourceMappingURL=schedule-4GF8Vbln.js.map
144
+ //# sourceMappingURL=schedule-BBJndWWa.js.map
@@ -1,13 +1,13 @@
1
- import "./ts-Cjaqub-v.js";
1
+ import "./ts-Bal_DlNX.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-FREexFyS.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-BRJsYunz.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
6
6
  import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
7
7
  import "./configShared-C1C04bbq.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BFecLJMA.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DnhSdiHO.js";
9
9
  import "./remotes-PKKjfTI1.js";
10
- import { d as listRecords, g as resolveOne, i as controlCodeFromName, m as renderRawLog, p as readNotes, s as extractTaskCounts, x as writeToIpc, y as snapshotStatus } from "./subcommands-DOjOgwxr.js";
10
+ import { S as writeToIpc, _ as resolveOne, b as snapshotStatus, c as extractTaskCounts, f as listRecords, h as renderRawLog, i as controlCodeFromName, m as readNotes, o as deriveLiveStatus } from "./subcommands-Clu93rSP.js";
11
11
  import yargs from "yargs";
12
12
  import { mkdir, open, readFile, stat, writeFile } from "fs/promises";
13
13
  import { homedir, hostname, userInfo } from "os";
@@ -651,12 +651,16 @@ Options:
651
651
  if (!root) return null;
652
652
  return ensureRepoWatch(root).val;
653
653
  };
654
- const withMeta = async (r) => ({
655
- ...r,
656
- title: await logTitle(r.log_file),
657
- git: r.status === "exited" ? null : await gitStatus(r.cwd),
658
- tasks: r.status === "exited" ? null : await logTasks(r.log_file)
659
- });
654
+ const withMeta = async (r) => {
655
+ const status = await deriveLiveStatus(r);
656
+ return {
657
+ ...r,
658
+ status,
659
+ title: await logTitle(r.log_file),
660
+ git: status === "exited" ? null : await gitStatus(r.cwd),
661
+ tasks: status === "exited" ? null : await logTasks(r.log_file)
662
+ };
663
+ };
660
664
  const presence = /* @__PURE__ */ new Map();
661
665
  const PRESENCE_TTL_MS = 12e3;
662
666
  const apiFetch = async (req) => {
@@ -1171,4 +1175,4 @@ Options:
1171
1175
 
1172
1176
  //#endregion
1173
1177
  export { cmdServe };
1174
- //# sourceMappingURL=serve-I9lIuvEu.js.map
1178
+ //# sourceMappingURL=serve-DKAxT4TT.js.map
@@ -32,7 +32,7 @@ async function cmdSetup(rest) {
32
32
  if (!existsSync(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
33
33
  if (noShare) return 0;
34
34
  process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
35
- const { cmdServe } = await import("./serve-I9lIuvEu.js");
35
+ const { cmdServe } = await import("./serve-DKAxT4TT.js");
36
36
  return cmdServe([
37
37
  "install",
38
38
  "--share",
@@ -42,4 +42,4 @@ async function cmdSetup(rest) {
42
42
 
43
43
  //#endregion
44
44
  export { cmdSetup };
45
- //# sourceMappingURL=setup-CIVspdWF.js.map
45
+ //# sourceMappingURL=setup-pO43opst.js.map
@@ -533,15 +533,15 @@ async function runSubcommand(argv) {
533
533
  case "restart": return await cmdRestart(rest);
534
534
  case "note": return await cmdNote(rest);
535
535
  case "serve": {
536
- const { cmdServe } = await import("./serve-I9lIuvEu.js");
536
+ const { cmdServe } = await import("./serve-DKAxT4TT.js");
537
537
  return cmdServe(rest);
538
538
  }
539
539
  case "setup": {
540
- const { cmdSetup } = await import("./setup-CIVspdWF.js");
540
+ const { cmdSetup } = await import("./setup-pO43opst.js");
541
541
  return cmdSetup(rest);
542
542
  }
543
543
  case "schedule": {
544
- const { cmdSchedule } = await import("./schedule-4GF8Vbln.js");
544
+ const { cmdSchedule } = await import("./schedule-BBJndWWa.js");
545
545
  return cmdSchedule(rest);
546
546
  }
547
547
  case "remote": {
@@ -942,22 +942,34 @@ async function runAllRemotesLs(opts) {
942
942
  return 0;
943
943
  }
944
944
  /**
945
+ * Cheap live status from liveness + log quiescence only (no log-content read):
946
+ * `exited` when the pid is gone or the record is exited, else `idle` when the log
947
+ * has been quiet longer than IDLE_THRESHOLD_MS, else `active`. The stored `status`
948
+ * field can go stale (the wrapper's idle mirror lags), so anything surfacing live
949
+ * status should derive it here. Safe to call per-agent on a hot path — one stat(),
950
+ * no 32KB tail read — which is why the serve's 1s console tick uses THIS rather
951
+ * than the richer deriveLiveState below.
952
+ */
953
+ async function deriveLiveStatus(r) {
954
+ if (r.status === "exited" || !isPidAlive(r.pid)) return "exited";
955
+ if (!r.log_file) return "active";
956
+ const mtime = await stat(r.log_file).then((s) => s.mtimeMs).catch(() => null);
957
+ return mtime !== null && Date.now() - mtime > IDLE_THRESHOLD_MS ? "idle" : "active";
958
+ }
959
+ /**
945
960
  * The live display state of one agent: stopped (exited) / idle (alive+quiet) /
946
961
  * active (alive+recent output) / needs_input (alive but parked on an unanswered
947
962
  * menu). Shared by the `ay ls` human table AND its `--json` output so both report
948
963
  * needs_input identically — an orchestrator parsing `ay ls --json` is the primary
949
- * consumer.
964
+ * consumer. Builds on the cheap deriveLiveStatus, then adds the menu (needs_input)
965
+ * override, which DOES read the log tail.
950
966
  */
951
967
  async function deriveLiveState(r) {
952
- if (!isPidAlive(r.pid)) return {
968
+ const base = await deriveLiveStatus(r);
969
+ if (base === "exited") return {
953
970
  state: "stopped",
954
971
  question: null
955
972
  };
956
- let state = "active";
957
- if (r.log_file) {
958
- const mtime = await stat(r.log_file).then((s) => s.mtimeMs).catch(() => null);
959
- state = mtime !== null && Date.now() - mtime > IDLE_THRESHOLD_MS ? "idle" : "active";
960
- }
961
973
  if (r.log_file) {
962
974
  const ni = await extractNeedsInput(r.log_file, r.cli);
963
975
  if (ni) return {
@@ -966,7 +978,7 @@ async function deriveLiveState(r) {
966
978
  };
967
979
  }
968
980
  return {
969
- state,
981
+ state: base,
970
982
  question: null
971
983
  };
972
984
  }
@@ -2427,5 +2439,5 @@ async function cmdResultSet(rest) {
2427
2439
  }
2428
2440
 
2429
2441
  //#endregion
2430
- export { resolveReadWindow as _, cursorAbs as a, stopTipForCli as b, finalizedLines as c, listRecords as d, matchKeyword as f, resolveOne as g, renderRawLogLines as h, controlCodeFromName as i, isPidAlive as l, renderRawLog as m, READ_PAGE_DEFAULT as n, extractNeedsInput as o, readNotes as p, cmdHelp as r, extractTaskCounts as s, GRACEFUL_EXIT_COMMANDS as t, isSubcommand as u, runSubcommand as v, writeToIpc as x, snapshotStatus as y };
2431
- //# sourceMappingURL=subcommands-DOjOgwxr.js.map
2442
+ export { writeToIpc as S, resolveOne as _, cursorAbs as a, snapshotStatus as b, extractTaskCounts as c, isSubcommand as d, listRecords as f, renderRawLogLines as g, renderRawLog as h, controlCodeFromName as i, finalizedLines as l, readNotes as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, matchKeyword as p, cmdHelp as r, extractNeedsInput as s, GRACEFUL_EXIT_COMMANDS as t, isPidAlive as u, resolveReadWindow as v, stopTipForCli as x, runSubcommand as y };
2443
+ //# sourceMappingURL=subcommands-Clu93rSP.js.map
@@ -0,0 +1,7 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import "./globalPidIndex-DlmmJlO8.js";
3
+ import "./configShared-C1C04bbq.js";
4
+ import "./remotes-PKKjfTI1.js";
5
+ import { S as writeToIpc, _ as resolveOne, a as cursorAbs, b as snapshotStatus, c as extractTaskCounts, d as isSubcommand, f as listRecords, g as renderRawLogLines, h as renderRawLog, i as controlCodeFromName, l as finalizedLines, m as readNotes, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as matchKeyword, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isPidAlive, v as resolveReadWindow, x as stopTipForCli, y as runSubcommand } from "./subcommands-Clu93rSP.js";
6
+
7
+ export { cmdHelp, isSubcommand, runSubcommand };
@@ -1,5 +1,5 @@
1
1
  import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
2
- import { r as getInstalledPackage } from "./versionChecker-FREexFyS.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-BRJsYunz.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-_eJa3DaX.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-V4qvXgAw.js";
5
5
  import { t as PidStore } from "./pidStore-fqXqTKkh.js";
@@ -1788,4 +1788,4 @@ function sleep(ms) {
1788
1788
 
1789
1789
  //#endregion
1790
1790
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1791
- //# sourceMappingURL=ts-Cjaqub-v.js.map
1791
+ //# sourceMappingURL=ts-Bal_DlNX.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "agent-yes";
10
- var version = "1.139.0";
10
+ var version = "1.140.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -215,4 +215,4 @@ async function displayVersion() {
215
215
 
216
216
  //#endregion
217
217
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
218
- //# sourceMappingURL=versionChecker-FREexFyS.js.map
218
+ //# sourceMappingURL=versionChecker-BRJsYunz.js.map
@@ -475,3 +475,21 @@ export function docTitle(name, status) {
475
475
  const g = statusGlyph(status);
476
476
  return (g ? g + " " : "") + n + " - agent-yes";
477
477
  }
478
+
479
+ // Relevance score for the Cmd+K omnibox — higher ranks first. Title hits beat
480
+ // cwd/prompt hits so the "quick title match" surfaces at the top; 0 means no
481
+ // title/cwd/prompt hit (such an agent only appears via a tail-content match,
482
+ // which the caller scores separately and ranks below these).
483
+ export function omniScore(e, query) {
484
+ const q = String(query || "")
485
+ .trim()
486
+ .toLowerCase();
487
+ if (!q) return 0;
488
+ const title = (e.title || "").toLowerCase();
489
+ if (title === q) return 100;
490
+ if (title.startsWith(q)) return 80;
491
+ if (title.includes(q)) return 60;
492
+ if ((e.cwd || "").toLowerCase().includes(q)) return 40;
493
+ if ((e.prompt || "").toLowerCase().includes(q)) return 20;
494
+ return 0;
495
+ }
package/lab/ui/index.html CHANGED
@@ -545,6 +545,102 @@
545
545
  align-items: center;
546
546
  justify-content: center;
547
547
  }
548
+ /* Cmd/Ctrl+K omnibox */
549
+ .omni {
550
+ position: fixed;
551
+ inset: 0;
552
+ z-index: 30;
553
+ background: rgba(2, 6, 12, 0.6);
554
+ display: flex;
555
+ align-items: flex-start;
556
+ justify-content: center;
557
+ padding-top: 12vh;
558
+ }
559
+ .omnibox {
560
+ width: min(680px, 92vw);
561
+ background: var(--panel);
562
+ border: 1px solid var(--line);
563
+ border-radius: 10px;
564
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
565
+ overflow: hidden;
566
+ font-family: var(--mono);
567
+ }
568
+ #omni-input {
569
+ width: 100%;
570
+ box-sizing: border-box;
571
+ padding: 14px 16px;
572
+ border: 0;
573
+ border-bottom: 1px solid var(--line);
574
+ background: transparent;
575
+ color: var(--fg);
576
+ font: 15px var(--mono);
577
+ outline: none;
578
+ }
579
+ .omni-results {
580
+ max-height: 52vh;
581
+ overflow-y: auto;
582
+ }
583
+ .omni-row {
584
+ display: flex;
585
+ align-items: center;
586
+ gap: 8px;
587
+ padding: 8px 14px;
588
+ cursor: pointer;
589
+ border-left: 2px solid transparent;
590
+ }
591
+ .omni-row.sel {
592
+ background: color-mix(in srgb, var(--accent) 16%, transparent);
593
+ border-left-color: var(--accent);
594
+ }
595
+ .omni-main {
596
+ flex: 1;
597
+ min-width: 0;
598
+ }
599
+ .omni-title {
600
+ color: var(--fg);
601
+ white-space: nowrap;
602
+ overflow: hidden;
603
+ text-overflow: ellipsis;
604
+ }
605
+ .omni-sub {
606
+ color: var(--muted);
607
+ font-size: 11px;
608
+ white-space: nowrap;
609
+ overflow: hidden;
610
+ text-overflow: ellipsis;
611
+ }
612
+ .omni-snip {
613
+ color: var(--muted);
614
+ font-size: 11px;
615
+ opacity: 0.85;
616
+ white-space: nowrap;
617
+ overflow: hidden;
618
+ text-overflow: ellipsis;
619
+ }
620
+ .omni-snip b {
621
+ color: var(--amber);
622
+ font-weight: 600;
623
+ }
624
+ .omni-spawn {
625
+ border-top: 1px solid var(--line);
626
+ }
627
+ .omni-spawn .omni-title {
628
+ color: var(--green);
629
+ }
630
+ .omni-empty {
631
+ padding: 14px;
632
+ color: var(--muted);
633
+ }
634
+ .omni-foot {
635
+ padding: 8px 14px;
636
+ border-top: 1px solid var(--line);
637
+ color: var(--muted);
638
+ font-size: 11px;
639
+ }
640
+ .omni-foot b {
641
+ color: var(--fg);
642
+ font-weight: 600;
643
+ }
548
644
  .lcard {
549
645
  background: var(--panel);
550
646
  border: 1px solid var(--line);
@@ -1380,6 +1476,24 @@
1380
1476
  <div class="launchoverlay" id="launch" style="display: none"></div>
1381
1477
  <div class="launchoverlay" id="newform" style="display: none"></div>
1382
1478
 
1479
+ <!-- Cmd/Ctrl+K omnibox: search agents by title (instant) then output (tail),
1480
+ or spawn a new agent in the highlighted agent's cwd with the typed prompt. -->
1481
+ <div class="omni" id="omni" style="display: none">
1482
+ <div class="omnibox">
1483
+ <input
1484
+ id="omni-input"
1485
+ placeholder="Search agents by title, then output…"
1486
+ spellcheck="false"
1487
+ autocapitalize="off"
1488
+ autocomplete="off"
1489
+ />
1490
+ <div class="omni-results" id="omni-results"></div>
1491
+ <div class="omni-foot">
1492
+ <span><b>↑↓</b> move &nbsp; <b>⏎</b> open &nbsp; <b>⌘⏎</b> spawn here &nbsp; <b>esc</b> close</span>
1493
+ </div>
1494
+ </div>
1495
+ </div>
1496
+
1383
1497
  <script type="module">
1384
1498
  // Pure list/identity/filter helpers live in a sibling module so they can be
1385
1499
  // unit-tested (tests/ui-logic/console-logic.spec.ts) without a DOM. This
@@ -1407,6 +1521,7 @@
1407
1521
  selSegments,
1408
1522
  fitTransform,
1409
1523
  docTitle,
1524
+ omniScore,
1410
1525
  } from "./console-logic.js";
1411
1526
  import {
1412
1527
  MARKER as E2E_MARKER,
@@ -1953,6 +2068,9 @@
1953
2068
  async fetchJSON(path) {
1954
2069
  return (await fetch(withTok(path))).json();
1955
2070
  },
2071
+ async fetchText(path) {
2072
+ return (await fetch(withTok(path))).text();
2073
+ },
1956
2074
  async post(path, bodyObj) {
1957
2075
  const r = await fetch(withTok(path), {
1958
2076
  method: "POST",
@@ -1974,6 +2092,9 @@
1974
2092
  async fetchJSON(path) {
1975
2093
  return JSON.parse((await rtc.req("GET", path)).text);
1976
2094
  },
2095
+ async fetchText(path) {
2096
+ return (await rtc.req("GET", path)).text;
2097
+ },
1977
2098
  async post(path, bodyObj) {
1978
2099
  const r = await rtc.req("POST", path, JSON.stringify(bodyObj));
1979
2100
  return { ok: r.status >= 200 && r.status < 300, text: r.text };
@@ -3523,6 +3644,7 @@
3523
3644
  window.addEventListener(
3524
3645
  "keydown",
3525
3646
  (e) => {
3647
+ if (omniOpen()) return; // omnibox owns the keyboard while open
3526
3648
  const mod = IS_MAC ? e.metaKey : e.altKey;
3527
3649
  if (!mod || (e.key !== "ArrowDown" && e.key !== "ArrowUp")) return;
3528
3650
  e.preventDefault();
@@ -3532,6 +3654,190 @@
3532
3654
  true,
3533
3655
  );
3534
3656
 
3657
+ // ---- Cmd/Ctrl+K omnibox: search agents (title → output) or spawn here ----
3658
+ let omniRows = []; // [{kind:'agent', entry, snippet?} | {kind:'spawn'}]
3659
+ let omniIdx = 0;
3660
+ let omniTailTimer = null;
3661
+ let omniTailSeq = 0;
3662
+ const omniOpen = () => $("omni").style.display !== "none";
3663
+
3664
+ function openOmni() {
3665
+ $("omni").style.display = "flex";
3666
+ const inp = $("omni-input");
3667
+ inp.value = "";
3668
+ inp.focus();
3669
+ runOmni();
3670
+ }
3671
+ function closeOmni() {
3672
+ if (omniTailTimer) clearTimeout(omniTailTimer);
3673
+ omniTailSeq++; // cancel any in-flight tail search
3674
+ $("omni").style.display = "none";
3675
+ }
3676
+
3677
+ // The agent the spawn action is anchored to: the highlighted agent row, else
3678
+ // the first agent result, else whatever's open in the console.
3679
+ function omniAnchor() {
3680
+ const hi = omniRows[omniIdx];
3681
+ if (hi && hi.kind === "agent") return hi.entry;
3682
+ const first = omniRows.find((r) => r.kind === "agent");
3683
+ return first ? first.entry : entries.find((x) => x._key === sel) || null;
3684
+ }
3685
+
3686
+ function renderOmni() {
3687
+ const q = $("omni-input").value.trim();
3688
+ if (omniIdx >= omniRows.length) omniIdx = Math.max(0, omniRows.length - 1);
3689
+ const anchor = omniAnchor();
3690
+ const html = omniRows
3691
+ .map((r, i) => {
3692
+ const sc = i === omniIdx ? " sel" : "";
3693
+ if (r.kind === "spawn") {
3694
+ return `<div class="omni-row omni-spawn${sc}" data-i="${i}">
3695
+ <span class="dot active"></span>
3696
+ <div class="omni-main">
3697
+ <div class="omni-title">✦ Spawn new ${esc(anchor?.cli || "claude")} agent here</div>
3698
+ <div class="omni-sub">${esc(anchor?.cwd || "(host default)")} &nbsp;·&nbsp; prompt: ${esc(q || "(empty)")}</div>
3699
+ </div></div>`;
3700
+ }
3701
+ const e = r.entry;
3702
+ const snip = r.snippet ? `<div class="omni-snip">…${r.snippet}…</div>` : "";
3703
+ return `<div class="omni-row${sc}" data-i="${i}">
3704
+ <span class="dot ${esc(e.status)}"></span>
3705
+ <div class="omni-main">
3706
+ <div class="omni-title">${esc(e.title || cliLabel(e) || ident(e) || "agent")}</div>
3707
+ <div class="omni-sub">${esc((e.cli || "") + " · " + (e.cwd || ""))}</div>
3708
+ ${snip}
3709
+ </div></div>`;
3710
+ })
3711
+ .join("");
3712
+ $("omni-results").innerHTML = html || `<div class="omni-empty">no matches</div>`;
3713
+ const selrow = $("omni-results").querySelector(".omni-row.sel");
3714
+ if (selrow) selrow.scrollIntoView({ block: "nearest" });
3715
+ }
3716
+
3717
+ function runOmni() {
3718
+ const q = $("omni-input").value.trim();
3719
+ const toks = q.split(/\s+/).filter(Boolean);
3720
+ // Tier 1 — instant title/identity match, ranked title-first then by recency.
3721
+ const hits = entries
3722
+ .filter((e) => (toks.length ? matches(e, toks) : true))
3723
+ .sort(
3724
+ (a, b) =>
3725
+ omniScore(b, q) - omniScore(a, q) || (b.started_at || 0) - (a.started_at || 0),
3726
+ )
3727
+ .slice(0, 30);
3728
+ omniRows = hits.map((entry) => ({ kind: "agent", entry }));
3729
+ if (q) omniRows.push({ kind: "spawn" });
3730
+ omniIdx = 0;
3731
+ renderOmni();
3732
+ // Tier 2 — debounced output (tail) search for alive agents not already shown.
3733
+ if (omniTailTimer) clearTimeout(omniTailTimer);
3734
+ if (q.length >= 2) omniTailTimer = setTimeout(() => omniTailSearch(q), 320);
3735
+ }
3736
+
3737
+ async function omniTailSearch(q) {
3738
+ const seq = ++omniTailSeq;
3739
+ const have = new Set(omniRows.filter((r) => r.kind === "agent").map((r) => r.entry._key));
3740
+ const ql = q.toLowerCase();
3741
+ const cands = entries.filter((e) => !have.has(e._key) && e.status !== "exited").slice(0, 20);
3742
+ await Promise.all(
3743
+ cands.map(async (e) => {
3744
+ const src = srcFor(e);
3745
+ if (!src?.tx?.fetchText) return;
3746
+ let text = "";
3747
+ try {
3748
+ text = await src.tx.fetchText(
3749
+ "/api/read/" + encodeURIComponent(e.pid) + "?mode=tail&n=120",
3750
+ );
3751
+ } catch {
3752
+ return;
3753
+ }
3754
+ if (seq !== omniTailSeq) return; // superseded or closed
3755
+ const idx = text.toLowerCase().indexOf(ql);
3756
+ if (idx < 0) return;
3757
+ const raw = text
3758
+ .slice(Math.max(0, idx - 24), idx + q.length + 24)
3759
+ .replace(/\s+/g, " ")
3760
+ .trim();
3761
+ const li = raw.toLowerCase().indexOf(ql);
3762
+ const snippet =
3763
+ li >= 0
3764
+ ? esc(raw.slice(0, li)) +
3765
+ "<b>" +
3766
+ esc(raw.slice(li, li + q.length)) +
3767
+ "</b>" +
3768
+ esc(raw.slice(li + q.length))
3769
+ : esc(raw);
3770
+ const at = omniRows.findIndex((r) => r.kind === "spawn");
3771
+ const row = { kind: "agent", entry: e, snippet };
3772
+ if (at >= 0) omniRows.splice(at, 0, row);
3773
+ else omniRows.push(row);
3774
+ }),
3775
+ );
3776
+ if (seq === omniTailSeq) renderOmni();
3777
+ }
3778
+
3779
+ function omniMove(d) {
3780
+ if (!omniRows.length) return;
3781
+ omniIdx = (omniIdx + d + omniRows.length) % omniRows.length;
3782
+ renderOmni();
3783
+ }
3784
+ async function omniActivate(forceSpawn) {
3785
+ const q = $("omni-input").value.trim();
3786
+ const row = omniRows[omniIdx];
3787
+ if (forceSpawn || (row && row.kind === "spawn")) {
3788
+ const anchor = omniAnchor();
3789
+ closeOmni();
3790
+ await spawnAndSelect(
3791
+ { cli: anchor?.cli || "claude", cwd: anchor?.cwd || undefined, prompt: q || undefined },
3792
+ anchor?._room,
3793
+ );
3794
+ return;
3795
+ }
3796
+ if (row && row.kind === "agent") {
3797
+ closeOmni();
3798
+ select(row.entry._key);
3799
+ }
3800
+ }
3801
+
3802
+ $("omni-input").addEventListener("input", runOmni);
3803
+ $("omni-results").addEventListener("click", (ev) => {
3804
+ const r = ev.target.closest(".omni-row");
3805
+ if (!r) return;
3806
+ omniIdx = +r.dataset.i;
3807
+ omniActivate(false);
3808
+ });
3809
+ $("omni").addEventListener("mousedown", (ev) => {
3810
+ if (ev.target === $("omni")) closeOmni(); // backdrop click closes
3811
+ });
3812
+ $("omni-input").addEventListener("keydown", (ev) => {
3813
+ if (ev.key === "Escape") {
3814
+ ev.preventDefault();
3815
+ closeOmni();
3816
+ } else if (ev.key === "ArrowDown") {
3817
+ ev.preventDefault();
3818
+ omniMove(1);
3819
+ } else if (ev.key === "ArrowUp") {
3820
+ ev.preventDefault();
3821
+ omniMove(-1);
3822
+ } else if (ev.key === "Enter") {
3823
+ ev.preventDefault();
3824
+ omniActivate(ev.metaKey || ev.ctrlKey);
3825
+ }
3826
+ });
3827
+ // Cmd+K (Mac) / Ctrl+K (others) toggles the omnibox. Capture phase so it beats
3828
+ // xterm's textarea handler and isn't forwarded to the agent as a keystroke.
3829
+ window.addEventListener(
3830
+ "keydown",
3831
+ (e) => {
3832
+ if ((e.metaKey || e.ctrlKey) && (e.key === "k" || e.key === "K")) {
3833
+ e.preventDefault();
3834
+ e.stopPropagation();
3835
+ omniOpen() ? closeOmni() : openOmni();
3836
+ }
3837
+ },
3838
+ true,
3839
+ );
3840
+
3535
3841
  // ---- rooms: localStorage cache + a manager you open by clicking the badge ----
3536
3842
  const ROOMS_KEY = "ay.rooms";
3537
3843
  const ROOM_TTL_MS = 90 * 24 * 60 * 60 * 1000; // evict stale rooms to bound how long a secret lingers in localStorage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.139.0",
3
+ "version": "1.140.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
package/ts/serve.ts CHANGED
@@ -7,6 +7,7 @@ import path from "path";
7
7
  import yargs from "yargs";
8
8
  import {
9
9
  controlCodeFromName,
10
+ deriveLiveStatus,
10
11
  extractTaskCounts,
11
12
  listRecords,
12
13
  readNotes,
@@ -933,14 +934,22 @@ export async function cmdServe(rest: string[]): Promise<number> {
933
934
 
934
935
  // One agent record decorated for the console: the latest OSC title + a git
935
936
  // snapshot (skipped for exited agents — their repo state is no longer live).
936
- const withMeta = async (r: Awaited<ReturnType<typeof listRecords>>[number]) => ({
937
- ...r,
938
- title: await logTitle(r.log_file),
939
- git: r.status === "exited" ? null : await gitStatus(r.cwd),
940
- // Task progress from the rendered todo block (null when none detected no
941
- // badge). Skipped for exited agents — their screen is no longer live.
942
- tasks: r.status === "exited" ? null : await logTasks(r.log_file),
943
- });
937
+ const withMeta = async (r: Awaited<ReturnType<typeof listRecords>>[number]) => {
938
+ // The stored `status` field lags (the wrapper's idle mirror is fire-and-forget),
939
+ // so the console showed agents as "active" long after they went quiet. Derive
940
+ // the LIVE status here same liveness+log-mtime basis as `ay ls` — so the
941
+ // console's dot (and the browser tab glyph) flips to idle in step with `ay ls`.
942
+ const status = await deriveLiveStatus(r);
943
+ return {
944
+ ...r,
945
+ status,
946
+ title: await logTitle(r.log_file),
947
+ git: status === "exited" ? null : await gitStatus(r.cwd),
948
+ // Task progress from the rendered todo block (null when none detected → no
949
+ // badge). Skipped for exited agents — their screen is no longer live.
950
+ tasks: status === "exited" ? null : await logTasks(r.log_file),
951
+ };
952
+ };
944
953
 
945
954
  // Multi-peer presence blackboard: viewerId -> what that viewer is watching +
946
955
  // its viewport/selection. Purely cosmetic ("who else is looking at this agent"),
@@ -1,5 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
2
+ import { mkdir, mkdtemp, rm, utimes, writeFile } from "fs/promises";
3
3
  import { tmpdir } from "os";
4
4
  import path from "path";
5
5
 
@@ -1466,3 +1466,60 @@ describe("subcommands.resolveReadWindow", () => {
1466
1466
  });
1467
1467
  });
1468
1468
  });
1469
+
1470
+ describe("subcommands.deriveLiveStatus", () => {
1471
+ const rec = (over: any) => ({
1472
+ pid: process.pid,
1473
+ cli: "claude",
1474
+ prompt: null,
1475
+ cwd: "/tmp",
1476
+ log_file: null,
1477
+ fifo_file: null,
1478
+ status: "active",
1479
+ exit_code: null,
1480
+ exit_reason: null,
1481
+ started_at: 0,
1482
+ ...over,
1483
+ });
1484
+
1485
+ it("returns 'exited' for a dead pid", async () => {
1486
+ const mod = await loadModule();
1487
+ expect(await mod.deriveLiveStatus(rec({ pid: 2147483646 }))).toBe("exited");
1488
+ });
1489
+
1490
+ it("returns 'exited' when the record is already exited", async () => {
1491
+ const mod = await loadModule();
1492
+ expect(await mod.deriveLiveStatus(rec({ status: "exited" }))).toBe("exited");
1493
+ });
1494
+
1495
+ it("returns 'active' for an alive pid with no log file", async () => {
1496
+ const mod = await loadModule();
1497
+ expect(await mod.deriveLiveStatus(rec({ log_file: null }))).toBe("active");
1498
+ });
1499
+
1500
+ it("returns 'active' for an alive pid with a freshly-written log", async () => {
1501
+ const dir = await mkdtemp(path.join(tmpdir(), "ay-dls-"));
1502
+ try {
1503
+ const log = path.join(dir, "a.log");
1504
+ await writeFile(log, "hi");
1505
+ const mod = await loadModule();
1506
+ expect(await mod.deriveLiveStatus(rec({ log_file: log }))).toBe("active");
1507
+ } finally {
1508
+ await rm(dir, { recursive: true, force: true }).catch(() => null);
1509
+ }
1510
+ });
1511
+
1512
+ it("returns 'idle' when the log has been quiet past the threshold", async () => {
1513
+ const dir = await mkdtemp(path.join(tmpdir(), "ay-dls-"));
1514
+ try {
1515
+ const log = path.join(dir, "a.log");
1516
+ await writeFile(log, "hi");
1517
+ const old = new Date(Date.now() - 5 * 60 * 1000); // 5 min ago > 60s threshold
1518
+ await utimes(log, old, old);
1519
+ const mod = await loadModule();
1520
+ expect(await mod.deriveLiveStatus(rec({ log_file: log }))).toBe("idle");
1521
+ } finally {
1522
+ await rm(dir, { recursive: true, force: true }).catch(() => null);
1523
+ }
1524
+ });
1525
+ });
package/ts/subcommands.ts CHANGED
Binary file
@@ -1,8 +0,0 @@
1
- import "./ts-Cjaqub-v.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-FREexFyS.js";
4
- import "./pidStore-fqXqTKkh.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BFecLJMA.js";
7
-
8
- export { SUPPORTED_CLIS };
@@ -1,7 +0,0 @@
1
- import "./logger-CDIsZ-Pp.js";
2
- import "./globalPidIndex-DlmmJlO8.js";
3
- import "./configShared-C1C04bbq.js";
4
- import "./remotes-PKKjfTI1.js";
5
- import { _ as resolveReadWindow, a as cursorAbs, b as stopTipForCli, c as finalizedLines, d as listRecords, f as matchKeyword, g as resolveOne, h as renderRawLogLines, i as controlCodeFromName, l as isPidAlive, m as renderRawLog, n as READ_PAGE_DEFAULT, o as extractNeedsInput, p as readNotes, r as cmdHelp, s as extractTaskCounts, t as GRACEFUL_EXIT_COMMANDS, u as isSubcommand, v as runSubcommand, x as writeToIpc, y as snapshotStatus } from "./subcommands-DOjOgwxr.js";
6
-
7
- export { cmdHelp, isSubcommand, runSubcommand };