agent-yes 1.205.0 → 1.207.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 (31) hide show
  1. package/README.md +2 -0
  2. package/dist/{SUPPORTED_CLIS-C8-qh98a.js → SUPPORTED_CLIS-B1CDJwYn.js} +2 -2
  3. package/dist/SUPPORTED_CLIS-vH8hvcZr.js +9 -0
  4. package/dist/{agentShare-99IGGOkv.js → agentShare-DdEbHtAn.js} +3 -3
  5. package/dist/cli.js +4 -4
  6. package/dist/index.js +2 -2
  7. package/dist/{notifyDaemon-DK1HOMjj.js → notifyDaemon-DivHVzZx.js} +2 -2
  8. package/dist/{rustBinary-Tgn948GR.js → rustBinary-CuBIr-zX.js} +2 -2
  9. package/dist/{schedule-CBoM67Vy.js → schedule-Bg_CDt2j.js} +4 -4
  10. package/dist/{serve-IvOlaL-7.js → serve-B-ZCy67S.js} +258 -41
  11. package/dist/{setup-BaKdS-3i.js → setup-D6UBzk4-.js} +2 -2
  12. package/dist/{share-Ciw1mWVN.js → share-_QYjPN7q.js} +1 -1
  13. package/dist/{share-BfIU8t_h.js → share-vchIyVd8.js} +98 -5
  14. package/dist/{subcommands-Bs7wJRBB.js → subcommands-6AQ8egRn.js} +7 -7
  15. package/dist/{subcommands-DyDHZ5YI.js → subcommands-CeRH9-_h.js} +1 -1
  16. package/dist/{ts-Dm6jjLwC.js → ts-DZWZvbLM.js} +2 -2
  17. package/dist/{versionChecker-BomfUpSo.js → versionChecker-C2CV9VOX.js} +2 -2
  18. package/dist/{ws-CSkP2mkD.js → ws-CGNmxCPD.js} +2 -2
  19. package/lab/ui/console-logic.js +23 -1
  20. package/lab/ui/index.html +188 -29
  21. package/package.json +1 -1
  22. package/ts/serve.spec.ts +13 -1
  23. package/ts/serve.ts +263 -34
  24. package/ts/serveLock.spec.ts +15 -14
  25. package/ts/share.ts +80 -1
  26. package/ts/sizeNego.spec.ts +61 -0
  27. package/ts/sizeNego.ts +59 -0
  28. package/ts/statusText.spec.ts +17 -0
  29. package/ts/statusText.ts +19 -0
  30. package/ts/ws.spec.ts +8 -10
  31. package/dist/SUPPORTED_CLIS-D3XTFfS7.js +0 -9
package/lab/ui/index.html CHANGED
@@ -1325,6 +1325,14 @@
1325
1325
  text-overflow: ellipsis;
1326
1326
  white-space: nowrap;
1327
1327
  }
1328
+ .statusline {
1329
+ color: var(--muted);
1330
+ font-size: 12.5px;
1331
+ margin-top: 4px;
1332
+ overflow: hidden;
1333
+ text-overflow: ellipsis;
1334
+ white-space: nowrap;
1335
+ }
1328
1336
  /* compact view: one line per agent — dot + cli + live title (or prompt), age */
1329
1337
  .row.crow {
1330
1338
  display: flex;
@@ -1682,11 +1690,28 @@
1682
1690
  h1 {
1683
1691
  font-size: 17px;
1684
1692
  }
1685
- /* roomier tap targets for the header controls */
1693
+ /* roomier tap targets for the header controls — min 44px square so
1694
+ touch users can hit them (WCAG 2.5.5). inline-flex keeps the glyph
1695
+ centered once min-height stretches the box past its text. */
1686
1696
  .newbtn,
1687
1697
  .viewbtn {
1688
1698
  padding: 7px 12px;
1689
1699
  font-size: 12.5px;
1700
+ min-height: 44px;
1701
+ min-width: 44px;
1702
+ display: inline-flex;
1703
+ align-items: center;
1704
+ justify-content: center;
1705
+ }
1706
+ /* the filter box is the primary control — give it a full-height touch
1707
+ target too, not the 38px it collapses to from text metrics alone.
1708
+ Stretch the input itself to the box height so a tap anywhere in the
1709
+ box lands on the field, not just its 17px text line. */
1710
+ .ibox {
1711
+ min-height: 44px;
1712
+ }
1713
+ #q {
1714
+ align-self: stretch;
1690
1715
  }
1691
1716
  .meta {
1692
1717
  align-items: center;
@@ -1819,6 +1844,7 @@
1819
1844
  <input
1820
1845
  id="q"
1821
1846
  placeholder="filter… repo:agent-yes claude (space = AND)"
1847
+ aria-label="Filter agents by repo, CLI, or title"
1822
1848
  autocomplete="off"
1823
1849
  autofocus
1824
1850
  />
@@ -1831,7 +1857,14 @@
1831
1857
  <span class="metaright">
1832
1858
  <button id="foldbtn" class="viewbtn" title="fold subagent trees">⊞ subs</button>
1833
1859
  <button id="sortbtn" class="viewbtn" title="cycle sort order">⇅ state</button>
1834
- <button id="viewbtn" class="viewbtn" title="toggle compact list">☰</button>
1860
+ <button
1861
+ id="viewbtn"
1862
+ class="viewbtn"
1863
+ title="toggle compact list"
1864
+ aria-label="Toggle compact list view"
1865
+ >
1866
+
1867
+ </button>
1835
1868
  <button id="portsbtn" class="viewbtn" title="manage exposed localhost ports">
1836
1869
  ⇄ ports
1837
1870
  </button>
@@ -2140,7 +2173,7 @@
2140
2173
  selFromBottom,
2141
2174
  parseSel,
2142
2175
  selSegments,
2143
- fitTransform,
2176
+ fitTransformCentered,
2144
2177
  docTitle,
2145
2178
  omniScore,
2146
2179
  } from "./console-logic.js";
@@ -2197,7 +2230,15 @@
2197
2230
  } catch {}
2198
2231
  if (term) {
2199
2232
  term.options.fontSize = termFontSize;
2200
- if (fit)
2233
+ // Negotiating host: the grid is canonical (host-owned) — a font change
2234
+ // here only alters our glyph px, so re-letterbox and report the new
2235
+ // capacity; the host reflows the PTY if our readable cols/rows moved.
2236
+ // This is what makes font size a ZOOM control on mobile: bigger font →
2237
+ // fewer readable cells → the PTY itself shrinks → bigger crisp text.
2238
+ if (negoActive) {
2239
+ applyCanvasScale();
2240
+ sendPresence();
2241
+ } else if (fit)
2201
2242
  try {
2202
2243
  fit.fit();
2203
2244
  } catch {}
@@ -3099,6 +3140,13 @@
3099
3140
  // short lease after that, the heartbeat won't follow/override — so our own
3100
3141
  // push isn't fought while the agent's /api/size is still catching up.
3101
3142
  let lastDroveAt = 0;
3143
+ // The selected agent's host negotiates the PTY size from viewer
3144
+ // capacities (its /api/size responses carry nego:true). On such hosts we
3145
+ // never push /api/resize — we report our readable capacity in presence
3146
+ // (sendPresence cap) and follow the negotiated canonical grid, so a phone
3147
+ // and a desktop watching the same agent converge on the smallest
3148
+ // viewer's readable size instead of fighting last-writer-wins.
3149
+ let negoActive = false;
3102
3150
 
3103
3151
  function isDarkColor(c) {
3104
3152
  try {
@@ -3244,6 +3292,18 @@
3244
3292
  const s = term.getSelectionPosition && term.getSelectionPosition();
3245
3293
  selR = selFromBottom(s, term.buffer.active.length);
3246
3294
  } catch {}
3295
+ // Our readable capacity: the grid our pane fits at the current font —
3296
+ // measured from the container (transform-independent), NOT term.cols
3297
+ // (which may be a foreign canonical grid we're letterboxing). The host
3298
+ // negotiates the PTY to the min cap across viewers; read-only viewers
3299
+ // report none (they can't influence the PTY). Older hosts ignore it.
3300
+ let cap = null;
3301
+ try {
3302
+ if (!isReadonlyEnt(cur.e) && fit) {
3303
+ const d = fit.proposeDimensions();
3304
+ if (d && d.cols > 0 && d.rows > 0) cap = { cols: d.cols, rows: d.rows };
3305
+ }
3306
+ } catch {}
3247
3307
  cur.tx
3248
3308
  .post("/api/presence", {
3249
3309
  viewer: myViewerId,
@@ -3251,6 +3311,7 @@
3251
3311
  cols: term.cols,
3252
3312
  rows: term.rows,
3253
3313
  sel: selR,
3314
+ cap,
3254
3315
  })
3255
3316
  .catch(() => {});
3256
3317
  }
@@ -3567,14 +3628,36 @@
3567
3628
  const cs = getComputedStyle(logEl);
3568
3629
  const pw = logEl.clientWidth - parseFloat(cs.paddingLeft) - parseFloat(cs.paddingRight);
3569
3630
  const ph = logEl.clientHeight - parseFloat(cs.paddingTop) - parseFloat(cs.paddingBottom);
3570
- // fitTransform (console-logic.js) decides none-vs-scale (unit-tested).
3571
- xt.style.transform = fitTransform(gw, gh, pw, ph);
3631
+ // fitTransformCentered (console-logic.js, unit-tested) decides
3632
+ // none-vs-scale and centers the letterboxed grid in the pane (fit
3633
+ // width → vertically centered, fit height → horizontally). The peer
3634
+ // overlay reads the transformed screen rect back via
3635
+ // getBoundingClientRect, so the translate needs no extra bookkeeping.
3636
+ xt.style.transform = fitTransformCentered(gw, gh, pw, ph).transform;
3572
3637
  } catch {}
3573
3638
  }
3639
+ // Pane geometry changed (window resize, splitter drag, soft keyboard, tab
3640
+ // refocus): on legacy hosts re-fit the grid to the pane (the size push
3641
+ // rides term.onResize → pushSize); on negotiating hosts the grid is
3642
+ // canonical — never reflow it locally, just re-letterbox and report the
3643
+ // new capacity so the host can move the min if needed.
3644
+ function refitPane() {
3645
+ if (!term) return;
3646
+ if (negoActive) {
3647
+ applyCanvasScale();
3648
+ sendPresence();
3649
+ return;
3650
+ }
3651
+ if (fit)
3652
+ try {
3653
+ fit.fit();
3654
+ } catch {}
3655
+ }
3574
3656
  // Follow the canonical grid: resize our xterm to the agent's size (set by the
3575
3657
  // active driver / local owner) WITHOUT reflowing to our pane, then scale to
3576
3658
  // fit. No-op when we already match (we're the driver, or sizes agree).
3577
3659
  function followAgentSize(sz) {
3660
+ if (sz && sz.nego != null) negoActive = !!sz.nego;
3578
3661
  if (!term || !sz || !sz.cols || !sz.rows) return;
3579
3662
  // While our own recent push is still settling, don't fight it.
3580
3663
  if (Date.now() - lastDroveAt < 6000) {
@@ -4832,7 +4915,7 @@
4832
4915
  .map((r) => {
4833
4916
  if (r.kind !== "agent") return headerHtml(r);
4834
4917
  const e = r.entry;
4835
- const t = e.title || e.prompt || "";
4918
+ const t = e.title || e.status_text || e.prompt || "";
4836
4919
  const id = compactIdent(e, ctx, 3, r.parentEntry);
4837
4920
  const cli = cliLabel(e);
4838
4921
  return `<div class="row crow ${e._key === sel ? "sel" : ""}${rowFlags(e)}" data-key="${esc(e._key)}">
@@ -4875,9 +4958,10 @@
4875
4958
  ${taskChipHtml(e)}
4876
4959
  ${badgeChipsHtml(e)}
4877
4960
  ${gitChipHtml(e)}
4878
- ${peerChipHtml(e)}
4879
- <span class="age">${age(e)}</span></div>
4961
+ ${peerChipHtml(e)}
4962
+ <span class="age">${age(e)}</span></div>
4880
4963
  ${e.title ? `<div class="rowtitle" title="${esc(e.title)}">${esc(e.title)}</div>` : ""}
4964
+ ${e.status_text ? `<div class="statusline" title="${esc(e.status_text)}">${esc(e.status_text)}</div>` : ""}
4881
4965
  ${
4882
4966
  e.status === "needs_input" && e.question
4883
4967
  ? `<div class="detail ask" title="${esc(e.question)}">⌨ ${esc(e.question)}</div>`
@@ -5153,6 +5237,14 @@
5153
5237
  applyCanvasScale();
5154
5238
  return;
5155
5239
  }
5240
+ // Negotiating host: the PTY size is the host's min over viewer
5241
+ // capacities, not ours to push. Re-letterbox and refresh our cap
5242
+ // (the pane may have changed) — the host resizes if the min moved.
5243
+ if (negoActive) {
5244
+ applyCanvasScale();
5245
+ sendPresence();
5246
+ return;
5247
+ }
5156
5248
  lastDroveAt = Date.now(); // we're driving the size now (lease the grid)
5157
5249
  tx.post("/api/resize/" + encodeURIComponent(pid), {
5158
5250
  cols: term.cols,
@@ -5193,6 +5285,7 @@
5193
5285
  const selKey = e._key;
5194
5286
  const fitAndSync = (sz) => {
5195
5287
  if (sel !== selKey || !term) return;
5288
+ negoActive = !!(sz && sz.nego);
5196
5289
  if (sz && sz.cols && sz.rows) agentSize = sz; // remember for the badge tooltip
5197
5290
  // Read-only viewer: don't reflow the agent to our pane (we can't resize
5198
5291
  // it, and shouldn't). Adopt the AGENT's own grid and CSS-scale it to fit,
@@ -5204,6 +5297,22 @@
5204
5297
  suppressPush = false;
5205
5298
  return;
5206
5299
  }
5300
+ // Negotiating host: adopt the canonical (negotiated) grid like a
5301
+ // watcher and report our capacity via presence — the host takes the
5302
+ // min over all viewers and resizes the PTY itself. No direct push, so
5303
+ // two viewers can't fight over the size.
5304
+ if (negoActive) {
5305
+ if (sz.cols && sz.rows) followAgentSize(sz);
5306
+ else {
5307
+ // agent has no size on record yet — seed from our own fit
5308
+ try {
5309
+ fit.fit();
5310
+ } catch {}
5311
+ }
5312
+ suppressPush = false;
5313
+ sendPresence(); // cap → host negotiates → size event brings the grid
5314
+ return;
5315
+ }
5207
5316
  try {
5208
5317
  fit.fit();
5209
5318
  } catch {}
@@ -5379,13 +5488,9 @@
5379
5488
  renderList();
5380
5489
  });
5381
5490
  window.addEventListener("resize", () => {
5382
- // Resizing our window is strong intent → re-fit to our pane (we become the
5383
- // size driver; the push rides term.onResize). applyCanvasScale then clears
5384
- // the transform since our grid now matches our pane.
5385
- if (fit)
5386
- try {
5387
- fit.fit();
5388
- } catch {}
5491
+ // Resizing our window is strong intent → re-fit / re-report capacity
5492
+ // (refitPane picks the legacy-push vs negotiate path).
5493
+ refitPane();
5389
5494
  applyCanvasScale();
5390
5495
  renderPeerSelections(); // cell size changed → reposition peer-selection overlays
5391
5496
  });
@@ -5400,15 +5505,76 @@
5400
5505
  const onVV = () => {
5401
5506
  const kb = Math.max(0, Math.round(window.innerHeight - vv.height - vv.offsetTop));
5402
5507
  document.documentElement.style.setProperty("--kb", kb + "px");
5403
- if (fit)
5404
- try {
5405
- fit.fit();
5406
- } catch {}
5508
+ refitPane();
5407
5509
  };
5408
5510
  vv.addEventListener("resize", onVV);
5409
5511
  vv.addEventListener("scroll", onVV);
5410
5512
  onVV();
5411
5513
  }
5514
+ // ── mobile pinch on the terminal = terminal zoom, not page zoom ─────────
5515
+ // Two fingers over the log pane adjust the terminal FONT SIZE. On a
5516
+ // negotiating host that reflows the PTY itself (bigger font → fewer
5517
+ // readable cells → the host shrinks the grid → bigger crisp text; smaller
5518
+ // font → more rows/cols), which is what a phone user actually means by
5519
+ // pinch: "show me more / make it readable", with the surrounding UI
5520
+ // staying put. Only 2-touch gestures over .log are intercepted — 1-finger
5521
+ // scroll/selection passes to xterm, and the page keeps its native
5522
+ // pinch-zoom everywhere else (an a11y requirement, WCAG 1.4.4).
5523
+ (function () {
5524
+ const logEl = $("log");
5525
+ if (!logEl) return;
5526
+ let startDist = 0; // 0 = no pinch in flight
5527
+ let startFont = 0;
5528
+ let lastRatio = 1;
5529
+ let baseTransform = "";
5530
+ const dist = (t) => Math.hypot(t[0].clientX - t[1].clientX, t[0].clientY - t[1].clientY);
5531
+ logEl.addEventListener(
5532
+ "touchstart",
5533
+ (ev) => {
5534
+ if (ev.touches.length !== 2) return;
5535
+ ev.preventDefault(); // this gesture is ours — no page zoom
5536
+ startDist = dist(ev.touches);
5537
+ startFont = termFontSize;
5538
+ lastRatio = 1;
5539
+ const xt = logEl.querySelector(".xterm");
5540
+ baseTransform = xt ? xt.style.transform || "none" : "none";
5541
+ },
5542
+ { passive: false },
5543
+ );
5544
+ logEl.addEventListener(
5545
+ "touchmove",
5546
+ (ev) => {
5547
+ if (ev.touches.length !== 2 || !startDist) return;
5548
+ ev.preventDefault();
5549
+ lastRatio = dist(ev.touches) / startDist;
5550
+ // Live preview: compose the pinch onto the fitted transform. The
5551
+ // grid truly reflows only when the negotiated size lands after the
5552
+ // gesture commits — a brief settle, traded for zero SIGWINCH storm
5553
+ // while the fingers are still moving.
5554
+ const xt = logEl.querySelector(".xterm");
5555
+ if (xt)
5556
+ xt.style.transform =
5557
+ `scale(${lastRatio.toFixed(3)})` +
5558
+ (baseTransform === "none" ? "" : " " + baseTransform);
5559
+ },
5560
+ { passive: false },
5561
+ );
5562
+ const endPinch = () => {
5563
+ if (!startDist) return;
5564
+ const target = Math.round(startFont * lastRatio);
5565
+ startDist = 0;
5566
+ if (target !== termFontSize) setTermFontSize(target); // nego: cap → host reflows
5567
+ applyCanvasScale(); // clear the preview; re-letterbox at the real grid
5568
+ };
5569
+ logEl.addEventListener("touchend", (ev) => {
5570
+ if (ev.touches.length < 2) endPinch();
5571
+ });
5572
+ logEl.addEventListener("touchcancel", () => endPinch());
5573
+ // Safari's proprietary gesture events fire for pinch regardless of
5574
+ // touch-action; swallow them over the terminal so the page never zooms.
5575
+ for (const t of ["gesturestart", "gesturechange", "gestureend"])
5576
+ logEl.addEventListener(t, (ev) => ev.preventDefault());
5577
+ })();
5412
5578
 
5413
5579
  // Step the selection up/down the (filtered) list — same order the left panel
5414
5580
  // renders. Clamps at the ends, scrolls the row into view.
@@ -6949,11 +7115,7 @@
6949
7115
  // terminal of a different size) that re-push fights the local terminal
6950
7116
  // and reflows the stream on every tab switch — the glitch the eager
6951
7117
  // per-focus resync introduced. The one-shot adopt still runs on select.
6952
- if (term && fit) {
6953
- try {
6954
- fit.fit();
6955
- } catch {}
6956
- }
7118
+ refitPane();
6957
7119
  }
6958
7120
  });
6959
7121
  watchVersion();
@@ -7005,10 +7167,7 @@
7005
7167
  if (!dragging) return;
7006
7168
  const w = Math.min(Math.max(e.clientX, 280), window.innerWidth - 360);
7007
7169
  app.style.setProperty("--leftw", w + "px");
7008
- if (fit)
7009
- try {
7010
- fit.fit();
7011
- } catch {}
7170
+ refitPane();
7012
7171
  });
7013
7172
  const end = (e) => {
7014
7173
  if (!dragging) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.205.0",
3
+ "version": "1.207.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.spec.ts CHANGED
@@ -1,6 +1,18 @@
1
1
  import { readFile } from "fs/promises";
2
2
  import { describe, expect, it } from "vitest";
3
- import { installerArgv, isNoNodeExecError, oxmgrVersionHasWindowsFix } from "./serve.ts";
3
+ import {
4
+ installerArgv,
5
+ isNoNodeExecError,
6
+ oxmgrVersionHasWindowsFix,
7
+ portlessConsoleUrl,
8
+ } from "./serve.ts";
9
+
10
+ describe("portlessConsoleUrl", () => {
11
+ it("uses the stable local HTTPS hostname and keeps auth in the fragment", () => {
12
+ expect(portlessConsoleUrl()).toBe("https://agent-yes.localhost/");
13
+ expect(portlessConsoleUrl("a b")).toBe("https://agent-yes.localhost/#k=a%20b");
14
+ });
15
+ });
4
16
 
5
17
  // Guards the Windows daemon-manager selection: on Windows we only PREFER oxmgr
6
18
  // when the installed build carries the daemon-socket-inheritance fix. Stock