agent-yes 1.123.0 → 1.124.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.
@@ -0,0 +1,8 @@
1
+ import "./ts-DGIglR4L.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-gaQkM2Hy.js";
4
+ import "./pidStore-CGKIhaJO.js";
5
+ import "./globalPidIndex-C7r2m6s7.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-D_-bIOlW.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-DgukRoEI.js";
1
+ import { t as CLIS_CONFIG } from "./ts-DGIglR4L.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-B4O2cFlt.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-D_-bIOlW.js.map
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { n as logger } from "./logger-B9h0djqx.js";
3
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-BqOr1YqC.js";
3
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-gaQkM2Hy.js";
4
4
  import { argv } from "process";
5
5
  import { execFileSync, spawn } from "child_process";
6
6
  import ms from "ms";
@@ -482,7 +482,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
482
482
  {
483
483
  const rawArg = process.argv[2];
484
484
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
485
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-Tv6AwUkD.js");
485
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-Llf9o8nh.js");
486
486
  if (isHelpFlag && process.argv.length === 3) {
487
487
  cmdHelp();
488
488
  process.exit(0);
@@ -515,7 +515,7 @@ if (config.useRust) {
515
515
  }
516
516
  }
517
517
  if (rustBinary) {
518
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DHkqGoNv.js");
518
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-Cvm7yo5d.js");
519
519
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
520
520
  if (config.verbose) {
521
521
  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-DgukRoEI.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DGIglR4L.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-BqOr1YqC.js";
3
+ import "./versionChecker-gaQkM2Hy.js";
4
4
  import "./pidStore-CGKIhaJO.js";
5
5
  import "./globalPidIndex-C7r2m6s7.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts-DgukRoEI.js";
1
+ import "./ts-DGIglR4L.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-BqOr1YqC.js";
3
+ import "./versionChecker-gaQkM2Hy.js";
4
4
  import "./pidStore-CGKIhaJO.js";
5
5
  import "./globalPidIndex-C7r2m6s7.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B4O2cFlt.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-D_-bIOlW.js";
7
7
  import { n as resolveSpawnCwd } from "./workspaceConfig-BJO4fzEn.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-DULdIkU9.js.map
144
+ //# sourceMappingURL=schedule-D2cn8N7o.js.map
@@ -1,13 +1,13 @@
1
- import "./ts-DgukRoEI.js";
1
+ import "./ts-DGIglR4L.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import { r as getInstalledPackage } from "./versionChecker-BqOr1YqC.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-gaQkM2Hy.js";
4
4
  import "./pidStore-CGKIhaJO.js";
5
5
  import { a as updateGlobalPidStatus } from "./globalPidIndex-C7r2m6s7.js";
6
6
  import { t as pgidForWrapper } from "./reaper-BkjPN7mw.js";
7
7
  import "./configShared-C5QaNPnz.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B4O2cFlt.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-D_-bIOlW.js";
9
9
  import "./remotes-D8GvSbhf.js";
10
- import { f as readNotes, g as snapshotStatus, m as resolveOne, o as extractTaskCounts, p as renderRawLog, r as controlCodeFromName, u as listRecords, v as writeToIpc } from "./subcommands-B13Kto-u.js";
10
+ import { f as readNotes, g as snapshotStatus, m as resolveOne, o as extractTaskCounts, p as renderRawLog, r as controlCodeFromName, u as listRecords, v as writeToIpc } from "./subcommands-ClVHy-xI.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";
@@ -1107,4 +1107,4 @@ Options:
1107
1107
 
1108
1108
  //#endregion
1109
1109
  export { cmdServe };
1110
- //# sourceMappingURL=serve-r_2v9EKc.js.map
1110
+ //# sourceMappingURL=serve-Bo3bDXQG.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-r_2v9EKc.js");
35
+ const { cmdServe } = await import("./serve-Bo3bDXQG.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-DHa6fX8M.js.map
45
+ //# sourceMappingURL=setup-CvOr258q.js.map
@@ -524,15 +524,15 @@ async function runSubcommand(argv) {
524
524
  case "restart": return await cmdRestart(rest);
525
525
  case "note": return await cmdNote(rest);
526
526
  case "serve": {
527
- const { cmdServe } = await import("./serve-r_2v9EKc.js");
527
+ const { cmdServe } = await import("./serve-Bo3bDXQG.js");
528
528
  return cmdServe(rest);
529
529
  }
530
530
  case "setup": {
531
- const { cmdSetup } = await import("./setup-DHa6fX8M.js");
531
+ const { cmdSetup } = await import("./setup-CvOr258q.js");
532
532
  return cmdSetup(rest);
533
533
  }
534
534
  case "schedule": {
535
- const { cmdSchedule } = await import("./schedule-DULdIkU9.js");
535
+ const { cmdSchedule } = await import("./schedule-D2cn8N7o.js");
536
536
  return cmdSchedule(rest);
537
537
  }
538
538
  case "remote": {
@@ -2304,4 +2304,4 @@ async function cmdResultSet(rest) {
2304
2304
 
2305
2305
  //#endregion
2306
2306
  export { stopTipForCli as _, extractNeedsInput as a, isPidAlive as c, matchKeyword as d, readNotes as f, snapshotStatus as g, runSubcommand as h, cursorAbs as i, isSubcommand as l, resolveOne as m, cmdHelp as n, extractTaskCounts as o, renderRawLog as p, controlCodeFromName as r, finalizedLines as s, GRACEFUL_EXIT_COMMANDS as t, listRecords as u, writeToIpc as v };
2307
- //# sourceMappingURL=subcommands-B13Kto-u.js.map
2307
+ //# sourceMappingURL=subcommands-ClVHy-xI.js.map
@@ -2,6 +2,6 @@ import "./logger-B9h0djqx.js";
2
2
  import "./globalPidIndex-C7r2m6s7.js";
3
3
  import "./configShared-C5QaNPnz.js";
4
4
  import "./remotes-D8GvSbhf.js";
5
- import { _ as stopTipForCli, a as extractNeedsInput, c as isPidAlive, d as matchKeyword, f as readNotes, g as snapshotStatus, h as runSubcommand, i as cursorAbs, l as isSubcommand, m as resolveOne, n as cmdHelp, o as extractTaskCounts, p as renderRawLog, r as controlCodeFromName, s as finalizedLines, t as GRACEFUL_EXIT_COMMANDS, u as listRecords, v as writeToIpc } from "./subcommands-B13Kto-u.js";
5
+ import { _ as stopTipForCli, a as extractNeedsInput, c as isPidAlive, d as matchKeyword, f as readNotes, g as snapshotStatus, h as runSubcommand, i as cursorAbs, l as isSubcommand, m as resolveOne, n as cmdHelp, o as extractTaskCounts, p as renderRawLog, r as controlCodeFromName, s as finalizedLines, t as GRACEFUL_EXIT_COMMANDS, u as listRecords, v as writeToIpc } from "./subcommands-ClVHy-xI.js";
6
6
 
7
7
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -1,5 +1,5 @@
1
1
  import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
2
- import { r as getInstalledPackage } from "./versionChecker-BqOr1YqC.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-gaQkM2Hy.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-BvaUOzCV.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CJxsoGdb.js";
5
5
  import { t as PidStore } from "./pidStore-CGKIhaJO.js";
@@ -1787,4 +1787,4 @@ function sleep(ms) {
1787
1787
 
1788
1788
  //#endregion
1789
1789
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1790
- //# sourceMappingURL=ts-DgukRoEI.js.map
1790
+ //# sourceMappingURL=ts-DGIglR4L.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.123.0";
10
+ var version = "1.124.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-BqOr1YqC.js.map
218
+ //# sourceMappingURL=versionChecker-gaQkM2Hy.js.map
package/lab/ui/index.html CHANGED
@@ -2,7 +2,10 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content"
8
+ />
6
9
  <title>agent-yes · console</title>
7
10
  <!-- PWA: installable console under /w/ (manifest scope is /w/). The service
8
11
  worker is network-first so it never serves a stale wire protocol — the
@@ -35,6 +38,17 @@
35
38
  --pink: #f778ba;
36
39
  --red: #f85149;
37
40
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
41
+ /* Safe-area insets — non-zero only in a standalone PWA on a notched /
42
+ gesture-bar device (else 0, so these are harmless on desktop). Consumed
43
+ via max() so the surface keeps its normal padding when the inset is 0.
44
+ Needs <meta viewport ... viewport-fit=cover> to be reported at all. */
45
+ --sat: env(safe-area-inset-top);
46
+ --sar: env(safe-area-inset-right);
47
+ --sab: env(safe-area-inset-bottom);
48
+ --sal: env(safe-area-inset-left);
49
+ /* height the iOS soft keyboard overlays the viewport with (set from JS
50
+ via visualViewport; 0 on desktop / Android, which resizes instead). */
51
+ --kb: 0px;
38
52
  }
39
53
  /* Light theme — GitHub-light, the daylight counterpart of the dark palette
40
54
  above. prefers-color-scheme is re-evaluated by the browser the moment the
@@ -93,7 +107,8 @@
93
107
  grid-template-columns: var(--leftw, 42%) 1px 1fr;
94
108
  grid-template-rows: minmax(0, 1fr);
95
109
  height: 100vh;
96
- height: 100dvh;
110
+ /* subtract the iOS keyboard overlay so the composer stays above it */
111
+ height: calc(100dvh - var(--kb, 0px));
97
112
  }
98
113
 
99
114
  /* VSCode-style splitter: the visible divider stays 1px, but a ::before
@@ -125,7 +140,7 @@
125
140
  min-height: 0;
126
141
  }
127
142
  .head {
128
- padding: 16px 18px 10px;
143
+ padding: max(16px, var(--sat)) max(18px, var(--sar)) 10px max(18px, var(--sal));
129
144
  border-bottom: 1px solid var(--line);
130
145
  }
131
146
  h1 {
@@ -260,6 +275,40 @@
260
275
  background: var(--line);
261
276
  margin: 4px 2px;
262
277
  }
278
+ /* Terminal font-size stepper (a static row, not a hover/click target). */
279
+ .rmenufont {
280
+ justify-content: space-between;
281
+ cursor: default;
282
+ }
283
+ .rmenufont:hover {
284
+ background: none;
285
+ }
286
+ .fontstep {
287
+ display: inline-flex;
288
+ align-items: center;
289
+ gap: 8px;
290
+ }
291
+ .fontstep button {
292
+ width: 26px;
293
+ height: 26px;
294
+ background: var(--panel2);
295
+ border: 1px solid var(--line);
296
+ border-radius: 6px;
297
+ color: var(--fg);
298
+ font-size: 13px;
299
+ line-height: 1;
300
+ cursor: pointer;
301
+ }
302
+ .fontstep button:hover {
303
+ border-color: var(--accent);
304
+ color: var(--accent);
305
+ }
306
+ .fontstep #fontVal {
307
+ min-width: 18px;
308
+ text-align: center;
309
+ font-variant-numeric: tabular-nums;
310
+ color: var(--muted);
311
+ }
263
312
  /* Live performance HUD — opt-in via the ⋯ menu, hidden by default. */
264
313
  .perfhud {
265
314
  position: absolute;
@@ -627,6 +676,8 @@
627
676
  overflow-y: auto;
628
677
  flex: 1;
629
678
  min-height: 0;
679
+ /* clear the home-indicator / gesture bar so the last row stays tappable */
680
+ padding-bottom: var(--sab);
630
681
  }
631
682
  .row {
632
683
  padding: 11px 18px;
@@ -834,7 +885,7 @@
834
885
  position: relative; /* anchor for the absolutely-positioned perf HUD */
835
886
  }
836
887
  .rhead {
837
- padding: 14px 20px;
888
+ padding: max(14px, var(--sat)) max(20px, var(--sar)) 14px max(20px, var(--sal));
838
889
  border-bottom: 1px solid var(--line);
839
890
  display: flex;
840
891
  align-items: center;
@@ -842,6 +893,13 @@
842
893
  }
843
894
  .rhead .name {
844
895
  font-size: 15px;
896
+ /* shrink + ellipsis so a long agent title can't shove the live/⋯
897
+ controls off the edge on a narrow phone header */
898
+ flex: 0 1 auto;
899
+ min-width: 0;
900
+ overflow: hidden;
901
+ text-overflow: ellipsis;
902
+ white-space: nowrap;
845
903
  }
846
904
  /* back-to-list affordance — only surfaces in the single-column mobile view */
847
905
  .rback {
@@ -882,7 +940,7 @@
882
940
  flex: 1;
883
941
  min-height: 0;
884
942
  overflow: hidden;
885
- padding: 8px 10px;
943
+ padding: 8px max(10px, var(--sar)) 8px max(10px, var(--sal));
886
944
  background: var(--bg);
887
945
  }
888
946
  .log .xterm {
@@ -899,6 +957,96 @@
899
957
  color: var(--muted);
900
958
  font-size: 14px;
901
959
  }
960
+ /* On-screen key bar — hidden on desktop, flex on mobile (720px block).
961
+ Scrolls horizontally so it never wraps or steals height from the log. */
962
+ .keybar {
963
+ display: none;
964
+ flex: none;
965
+ gap: 6px;
966
+ align-items: center;
967
+ padding: 6px max(8px, var(--sar)) 6px max(8px, var(--sal));
968
+ border-top: 1px solid var(--line);
969
+ background: var(--panel);
970
+ overflow-x: auto;
971
+ -webkit-overflow-scrolling: touch;
972
+ scrollbar-width: none;
973
+ }
974
+ .keybar::-webkit-scrollbar {
975
+ display: none;
976
+ }
977
+ .keybar .kb {
978
+ flex: none;
979
+ min-width: 36px;
980
+ height: 36px;
981
+ padding: 0 11px;
982
+ background: var(--panel2);
983
+ border: 1px solid var(--line);
984
+ border-radius: 8px;
985
+ color: var(--fg);
986
+ font: 13px/1 var(--mono);
987
+ cursor: pointer;
988
+ -webkit-tap-highlight-color: transparent;
989
+ user-select: none;
990
+ }
991
+ .keybar .kb:active {
992
+ background: var(--line);
993
+ }
994
+ /* a sticky Ctrl/Alt that's armed for the next keystroke */
995
+ .keybar .kb.on {
996
+ background: var(--accent);
997
+ border-color: var(--accent);
998
+ color: #fff;
999
+ }
1000
+ /* Line composer — mobile-only. Carries the bottom safe-area inset so it
1001
+ clears the home indicator (completes the terminal pane's inset cover). */
1002
+ .composer {
1003
+ display: none;
1004
+ flex: none;
1005
+ gap: 8px;
1006
+ align-items: flex-end;
1007
+ padding: 8px max(10px, var(--sar)) max(8px, var(--sab)) max(10px, var(--sal));
1008
+ border-top: 1px solid var(--line);
1009
+ background: var(--panel);
1010
+ }
1011
+ .composer textarea {
1012
+ flex: 1;
1013
+ min-width: 0;
1014
+ resize: none;
1015
+ max-height: 120px;
1016
+ padding: 9px 11px;
1017
+ background: var(--bg);
1018
+ border: 1px solid var(--line);
1019
+ border-radius: 10px;
1020
+ color: var(--fg);
1021
+ /* ≥16px so iOS Safari doesn't auto-zoom the page when it gains focus */
1022
+ font:
1023
+ 16px/1.4 -apple-system,
1024
+ BlinkMacSystemFont,
1025
+ system-ui,
1026
+ sans-serif;
1027
+ }
1028
+ .composer textarea:focus {
1029
+ outline: none;
1030
+ border-color: var(--accent);
1031
+ }
1032
+ .composer .cmpsend {
1033
+ flex: none;
1034
+ height: 38px;
1035
+ padding: 0 14px;
1036
+ background: var(--accent);
1037
+ border: 1px solid var(--accent);
1038
+ border-radius: 10px;
1039
+ color: #fff;
1040
+ font:
1041
+ 600 13px/1 -apple-system,
1042
+ system-ui,
1043
+ sans-serif;
1044
+ cursor: pointer;
1045
+ -webkit-tap-highlight-color: transparent;
1046
+ }
1047
+ .composer .cmpsend:active {
1048
+ opacity: 0.85;
1049
+ }
902
1050
 
903
1051
  /* ---- mobile: collapse the two-pane desktop layout into a single column ----
904
1052
  The list and the terminal stack into the same cell; only one is visible at
@@ -937,7 +1085,7 @@
937
1085
  }
938
1086
 
939
1087
  .head {
940
- padding: 12px 14px 8px;
1088
+ padding: max(12px, var(--sat)) max(14px, var(--sar)) 8px max(14px, var(--sal));
941
1089
  }
942
1090
  h1 {
943
1091
  font-size: 17px;
@@ -961,7 +1109,8 @@
961
1109
  padding: 10px 14px;
962
1110
  }
963
1111
  .rhead {
964
- padding: 12px 14px;
1112
+ padding: max(12px, var(--sat)) max(14px, var(--sar)) 12px max(14px, var(--sal));
1113
+ gap: 8px;
965
1114
  }
966
1115
  .list .row,
967
1116
  .rooms .ritem,
@@ -972,6 +1121,15 @@
972
1121
  -webkit-tap-highlight-color: transparent;
973
1122
  }
974
1123
  }
1124
+ /* Touch input aids (key bar + composer) show on ANY touch device, not just
1125
+ narrow ones — so a tablet in the two-pane layout (typically no hardware
1126
+ keyboard) still gets Esc/Ctrl/arrows and a reliable line composer. */
1127
+ @media (pointer: coarse) {
1128
+ .keybar,
1129
+ .composer {
1130
+ display: flex;
1131
+ }
1132
+ }
975
1133
  </style>
976
1134
  <link
977
1135
  rel="stylesheet"
@@ -1051,6 +1209,15 @@
1051
1209
  <span class="rmenu">
1052
1210
  <button class="rmenubtn" id="rmenubtn" title="more" aria-label="more">⋯</button>
1053
1211
  <div class="rmenupanel" id="rmenupanel" hidden>
1212
+ <div class="rmenuitem rmenufont">
1213
+ <span>Font size</span>
1214
+ <span class="fontstep">
1215
+ <button type="button" id="fontDown" aria-label="smaller font">A−</button>
1216
+ <span id="fontVal">12</span>
1217
+ <button type="button" id="fontUp" aria-label="larger font">A+</button>
1218
+ </span>
1219
+ </div>
1220
+ <div class="rmenusep"></div>
1054
1221
  <label class="rmenuitem"
1055
1222
  ><input type="checkbox" id="perfToggle" /> <span>Perf HUD</span></label
1056
1223
  >
@@ -1067,6 +1234,40 @@
1067
1234
  ← pick an agent to tail its log; type directly into the terminal
1068
1235
  </div>
1069
1236
  </div>
1237
+ <!-- On-screen key bar — phones have no Esc/Tab/Ctrl/arrows, so agent TUIs
1238
+ are otherwise undriveable. Mobile-only (shown in the 720px block);
1239
+ posts raw sequences to the agent over the same /api/send wire. -->
1240
+ <div class="keybar" id="keybar" role="group" aria-label="terminal keys">
1241
+ <button class="kb" data-key="esc" title="Escape">Esc</button>
1242
+ <button class="kb kbmod" data-mod="ctrl" title="Ctrl — applies to the next key">
1243
+ Ctrl
1244
+ </button>
1245
+ <button class="kb kbmod" data-mod="alt" title="Alt — applies to the next key">Alt</button>
1246
+ <button class="kb" data-key="tab" title="Tab">Tab</button>
1247
+ <button class="kb" data-key="cc" title="Ctrl-C — interrupt">^C</button>
1248
+ <button class="kb" data-arrow="up" title="Up" aria-label="Up">↑</button>
1249
+ <button class="kb" data-arrow="down" title="Down" aria-label="Down">↓</button>
1250
+ <button class="kb" data-arrow="left" title="Left" aria-label="Left">←</button>
1251
+ <button class="kb" data-arrow="right" title="Right" aria-label="Right">→</button>
1252
+ <button class="kb" data-key="enter" title="Enter">⏎</button>
1253
+ </div>
1254
+ <!-- Line composer — the reliable path for typing a prompt on a phone:
1255
+ a normal textarea (no IME/autocorrect surprises that plague xterm's
1256
+ hidden input). Enter sends, Shift+Enter inserts a newline. -->
1257
+ <form class="composer" id="composer">
1258
+ <textarea
1259
+ id="cmpin"
1260
+ rows="1"
1261
+ placeholder="Message agent… ↵ to send"
1262
+ enterkeyhint="send"
1263
+ inputmode="text"
1264
+ autocapitalize="off"
1265
+ autocorrect="off"
1266
+ autocomplete="off"
1267
+ spellcheck="false"
1268
+ ></textarea>
1269
+ <button type="submit" class="cmpsend" title="Send to agent">Send</button>
1270
+ </form>
1070
1271
  <div class="perfhud" id="perfhud" hidden></div>
1071
1272
  </div>
1072
1273
  </div>
@@ -1117,6 +1318,28 @@
1117
1318
  let es = null; // live-tail subscription closer
1118
1319
  let term = null; // xterm.js Terminal rendering the raw PTY stream
1119
1320
  let fit = null;
1321
+ // Terminal font size (px) — adjustable from the ⋯ menu, persisted across
1322
+ // reloads, applied live to the open terminal and used by every new one.
1323
+ let termFontSize = 12;
1324
+ try {
1325
+ const n = parseInt(localStorage.getItem("ay.fontSize") || "", 10);
1326
+ if (n >= 8 && n <= 32) termFontSize = n;
1327
+ } catch {}
1328
+ function setTermFontSize(px) {
1329
+ termFontSize = Math.max(8, Math.min(32, px | 0));
1330
+ const v = $("fontVal");
1331
+ if (v) v.textContent = String(termFontSize);
1332
+ try {
1333
+ localStorage.setItem("ay.fontSize", String(termFontSize));
1334
+ } catch {}
1335
+ if (term) {
1336
+ term.options.fontSize = termFontSize;
1337
+ if (fit)
1338
+ try {
1339
+ fit.fit();
1340
+ } catch {}
1341
+ }
1342
+ }
1120
1343
 
1121
1344
  // xterm paints to a <canvas>, so unlike the CSS var() consumers it can't
1122
1345
  // ride prefers-color-scheme on its own — its theme is a JS object. Mirror
@@ -2020,6 +2243,159 @@
2020
2243
  await new Promise((r) => setTimeout(r, 200));
2021
2244
  await send("\r");
2022
2245
  }
2246
+
2247
+ // ---- On-screen key bar (mobile) -------------------------------------
2248
+ // A phone keyboard has no Esc/Tab/Ctrl/arrows, so agent TUIs (claude,
2249
+ // codex, vim…) are otherwise undriveable. The bar posts raw byte
2250
+ // sequences straight to the selected agent's stdin, and a sticky Ctrl/Alt
2251
+ // rewrites the NEXT character typed on the soft keyboard.
2252
+ let modCtrl = false,
2253
+ modAlt = false;
2254
+ // Send raw bytes to the currently-selected agent. Uses the live `sel`, so
2255
+ // it always hits the open agent even while an old terminal tears down mid
2256
+ // switch — the same /api/send wire the terminal's own input uses.
2257
+ function sendToSelected(data) {
2258
+ const e = sel ? entries.find((x) => x._key === sel) : null;
2259
+ if (!e || data == null) return;
2260
+ perfNote("in", typeof data === "string" ? data.length : (data?.byteLength ?? 0));
2261
+ txFor(e)
2262
+ .post("/api/send", { keyword: String(e.pid), msg: data, code: "none" })
2263
+ .catch(() => {});
2264
+ }
2265
+ // Apply a pending sticky Ctrl/Alt to ONE typed character (soft-keyboard
2266
+ // path only). Multi-char input — paste, an IME commit, an escape sequence —
2267
+ // passes through untouched. Ctrl folds @A–Z[\]^_ (case-insensitive) to
2268
+ // 0x00–0x1f; Alt/Meta is an ESC prefix. One-shot: clears after one char.
2269
+ function applyStickyMods(d) {
2270
+ if ((!modCtrl && !modAlt) || typeof d !== "string" || d.length !== 1) return d;
2271
+ let c = d;
2272
+ if (modCtrl) {
2273
+ const u = d.toUpperCase().charCodeAt(0);
2274
+ if (u >= 64 && u <= 95) c = String.fromCharCode(u - 64);
2275
+ else if (u === 32) c = "\x00"; // Ctrl-Space → NUL
2276
+ }
2277
+ if (modAlt) c = "\x1b" + c;
2278
+ modCtrl = modAlt = false;
2279
+ syncModButtons();
2280
+ return c;
2281
+ }
2282
+ function syncModButtons() {
2283
+ const kb = $("keybar");
2284
+ if (!kb) return;
2285
+ kb.querySelector('[data-mod="ctrl"]')?.classList.toggle("on", modCtrl);
2286
+ kb.querySelector('[data-mod="alt"]')?.classList.toggle("on", modAlt);
2287
+ }
2288
+ (function keybarBoot() {
2289
+ const kb = $("keybar");
2290
+ if (!kb) return;
2291
+ const SEQ = { esc: "\x1b", tab: "\t", enter: "\r", cc: "\x03" };
2292
+ const ARROW = { up: "A", down: "B", right: "C", left: "D" };
2293
+ // Don't let a button steal focus — that would drop the soft keyboard and
2294
+ // blur whatever the user is typing into (terminal or composer).
2295
+ kb.addEventListener("pointerdown", (ev) => {
2296
+ if (ev.target.closest("button")) ev.preventDefault();
2297
+ });
2298
+ kb.addEventListener("click", (ev) => {
2299
+ const b = ev.target.closest("button.kb");
2300
+ if (!b) return;
2301
+ if (b.dataset.mod) {
2302
+ // sticky toggle — armed for the next soft-keyboard character
2303
+ if (b.dataset.mod === "ctrl") modCtrl = !modCtrl;
2304
+ else modAlt = !modAlt;
2305
+ syncModButtons();
2306
+ return;
2307
+ }
2308
+ let seq = null;
2309
+ if (b.dataset.arrow) {
2310
+ const L = ARROW[b.dataset.arrow];
2311
+ if (modCtrl || modAlt) {
2312
+ // Ctrl/Alt + arrow → CSI modifier form ESC [ 1 ; <m> <L>, where
2313
+ // m = 1 + Alt(2) + Ctrl(4) (e.g. Ctrl-Left = ESC [ 1 ; 5 D for
2314
+ // word-wise motion). Modified cursor keys are always CSI, never
2315
+ // SS3, so DECCKM doesn't apply when a modifier is held.
2316
+ seq = "\x1b[1;" + (1 + (modAlt ? 2 : 0) + (modCtrl ? 4 : 0)) + L;
2317
+ } else {
2318
+ // Honour application-cursor-key mode (DECCKM): TUIs that enable it
2319
+ // expect SS3 (ESC O x) arrows, not CSI (ESC [ x). xterm tracks it.
2320
+ const app = term && term.modes && term.modes.applicationCursorKeysMode;
2321
+ seq = (app ? "\x1bO" : "\x1b[") + L;
2322
+ }
2323
+ } else if (b.dataset.key) {
2324
+ seq = SEQ[b.dataset.key];
2325
+ // Alt/Meta on a fixed key = ESC prefix (e.g. Alt+Enter). Ctrl on
2326
+ // Esc/Tab/Enter has no standard sequence, so it's left as the bare key.
2327
+ if (seq != null && modAlt) seq = "\x1b" + seq;
2328
+ }
2329
+ if (seq == null) return;
2330
+ // the armed modifier (if any) has now been folded into this key — clear it
2331
+ modCtrl = modAlt = false;
2332
+ syncModButtons();
2333
+ sendToSelected(seq);
2334
+ });
2335
+ })();
2336
+ // ---- Line composer (mobile) ----------------------------------------
2337
+ // The dependable way to type a prompt on a phone: a real textarea, immune
2338
+ // to the IME/autocorrect glitches that dog xterm's hidden input. Enter
2339
+ // sends text + a carriage return in one write (identical to typing then ↵);
2340
+ // Shift+Enter inserts a literal newline.
2341
+ (function composerBoot() {
2342
+ const form = $("composer"),
2343
+ ta = $("cmpin");
2344
+ if (!form || !ta) return;
2345
+ const grow = () => {
2346
+ ta.style.height = "auto";
2347
+ ta.style.height = Math.min(ta.scrollHeight, 120) + "px";
2348
+ };
2349
+ const sendLine = () => {
2350
+ let v = ta.value;
2351
+ if (!v) return;
2352
+ // An armed sticky Ctrl/Alt decorates a single-char line (e.g. Ctrl-D);
2353
+ // longer text can't carry it, but the modifier must still be cleared so
2354
+ // it doesn't silently leak into the next terminal keystroke.
2355
+ v = applyStickyMods(v);
2356
+ if (modCtrl || modAlt) {
2357
+ modCtrl = modAlt = false;
2358
+ syncModButtons();
2359
+ }
2360
+ sendToSelected(v + "\r");
2361
+ ta.value = "";
2362
+ grow();
2363
+ ta.focus(); // keep the soft keyboard up for the next line
2364
+ };
2365
+ ta.addEventListener("input", grow);
2366
+ form.addEventListener("submit", (ev) => {
2367
+ ev.preventDefault();
2368
+ sendLine();
2369
+ });
2370
+ // Enter sends; Shift+Enter inserts a newline; never fire mid-IME-commit
2371
+ // (the confirming Enter reports keyCode 229 / isComposing). `enterSeen`
2372
+ // lets the beforeinput fallback below know keydown already decided this
2373
+ // Enter (so it doesn't double-send, and Shift+Enter still makes a newline).
2374
+ let enterSeen = false;
2375
+ ta.addEventListener("keydown", (ev) => {
2376
+ enterSeen = false;
2377
+ if (ev.key === "Enter" && !ev.isComposing && ev.keyCode !== 229) {
2378
+ enterSeen = true;
2379
+ if (!ev.shiftKey) {
2380
+ ev.preventDefault();
2381
+ sendLine();
2382
+ }
2383
+ }
2384
+ });
2385
+ // Android (Gboard) commonly delivers Enter as keyCode 229 / key
2386
+ // "Unidentified", so the keydown above never matches it. beforeinput's
2387
+ // insertLineBreak fires reliably there; skip it when keydown already
2388
+ // saw the Enter (desktop/iOS, including the Shift+Enter newline case).
2389
+ ta.addEventListener("beforeinput", (ev) => {
2390
+ if (ev.inputType !== "insertLineBreak") return;
2391
+ if (enterSeen) {
2392
+ enterSeen = false;
2393
+ return;
2394
+ }
2395
+ ev.preventDefault();
2396
+ sendLine();
2397
+ });
2398
+ })();
2023
2399
  (function perfMenuBoot() {
2024
2400
  const btn = $("rmenubtn"),
2025
2401
  panel = $("rmenupanel"),
@@ -2037,6 +2413,10 @@
2037
2413
  if (cb) cb.addEventListener("change", () => setPerfHud(cb.checked));
2038
2414
  $("stopAgent")?.addEventListener("click", () => (closePanel(), stopAgent(false)));
2039
2415
  $("killAgent")?.addEventListener("click", () => (closePanel(), stopAgent(true)));
2416
+ // Font stepper — stays open so you can tap repeatedly; persists + refits.
2417
+ $("fontDown")?.addEventListener("click", () => setTermFontSize(termFontSize - 1));
2418
+ $("fontUp")?.addEventListener("click", () => setTermFontSize(termFontSize + 1));
2419
+ setTermFontSize(termFontSize); // sync the label with the restored value
2040
2420
  let saved = false;
2041
2421
  try {
2042
2422
  saved = localStorage.getItem("ay.perfHud") === "1";
@@ -2397,7 +2777,7 @@
2397
2777
  disableStdin: false,
2398
2778
  cursorBlink: true,
2399
2779
  scrollback: 5000,
2400
- fontSize: 12,
2780
+ fontSize: termFontSize,
2401
2781
  fontFamily: 'ui-monospace, "SF Mono", Menlo, monospace',
2402
2782
  theme: termTheme(),
2403
2783
  });
@@ -2463,10 +2843,11 @@
2463
2843
  // keyword with 400 (pid arrives as a number from /api/ls JSON).
2464
2844
  const kw = String(pid);
2465
2845
  const fwd = (d) => {
2466
- if (sel === e._key) {
2467
- perfNote("in", typeof d === "string" ? d.length : (d?.byteLength ?? 0));
2468
- tx.post("/api/send", { keyword: kw, msg: d, code: "none" }).catch(() => {});
2469
- }
2846
+ if (sel !== e._key) return; // ignore a stale terminal mid-switch
2847
+ // a soft-keyboard character may be decorated by an armed sticky Ctrl/Alt
2848
+ if (typeof d === "string") d = applyStickyMods(d);
2849
+ perfNote("in", typeof d === "string" ? d.length : (d?.byteLength ?? 0));
2850
+ tx.post("/api/send", { keyword: kw, msg: d, code: "none" }).catch(() => {});
2470
2851
  };
2471
2852
  term.onData(fwd);
2472
2853
  term.onBinary(fwd);
@@ -2527,11 +2908,51 @@
2527
2908
  const row = ev.target.closest(".row");
2528
2909
  if (row) select(row.dataset.key);
2529
2910
  });
2530
- // Mobile back button: return to the list pane. The tail keeps streaming in the
2911
+ // Mobile back: return to the list pane. The tail keeps streaming in the
2531
2912
  // background (selection unchanged), so reopening the agent is instant.
2532
- $("rback").addEventListener("click", () => {
2533
- document.querySelector(".app").classList.remove("show-detail");
2534
- });
2913
+ const goBack = () => document.querySelector(".app").classList.remove("show-detail");
2914
+ $("rback").addEventListener("click", goBack);
2915
+ // Swipe-right-from-the-left-edge → back, an alternative to the ‹ button.
2916
+ // Passive/read-only so it never fights xterm's own scroll or text
2917
+ // selection; armed only from the left edge, in the single-column view, and
2918
+ // never when a TUI has mouse tracking on (that touch belongs to the agent).
2919
+ (function swipeBackBoot() {
2920
+ const pane = document.querySelector(".right");
2921
+ if (!pane) return;
2922
+ let sx = 0,
2923
+ sy = 0,
2924
+ st = 0,
2925
+ armed = false;
2926
+ pane.addEventListener(
2927
+ "touchstart",
2928
+ (ev) => {
2929
+ armed = false;
2930
+ if (window.innerWidth > 720 || ev.touches.length !== 1) return;
2931
+ const t = ev.touches[0];
2932
+ armed = t.clientX <= 28; // left-edge start only
2933
+ sx = t.clientX;
2934
+ sy = t.clientY;
2935
+ st = ev.timeStamp;
2936
+ },
2937
+ { passive: true },
2938
+ );
2939
+ pane.addEventListener(
2940
+ "touchend",
2941
+ (ev) => {
2942
+ if (!armed) return;
2943
+ armed = false;
2944
+ const mt = term && term.modes && term.modes.mouseTrackingMode;
2945
+ if (mt && mt !== "none") return; // the TUI wants this touch
2946
+ const t = ev.changedTouches[0];
2947
+ if (!t) return;
2948
+ const dx = t.clientX - sx,
2949
+ dy = t.clientY - sy;
2950
+ // a quick, clearly-horizontal rightward flick (not a scroll/tap)
2951
+ if (dx > 45 && Math.abs(dx) > 2 * Math.abs(dy) && ev.timeStamp - st < 500) goBack();
2952
+ },
2953
+ { passive: true },
2954
+ );
2955
+ })();
2535
2956
  $("q").addEventListener("input", () => {
2536
2957
  try {
2537
2958
  localStorage.setItem("ay.filter", $("q").value);
@@ -2549,6 +2970,26 @@
2549
2970
  fit.fit();
2550
2971
  } catch {}
2551
2972
  });
2973
+ // iOS overlays the soft keyboard ON TOP of the layout viewport rather than
2974
+ // shrinking it, so a bottom-anchored composer would hide behind it. Track
2975
+ // the visual viewport and reserve the covered height as --kb (which .app
2976
+ // subtracts from its height), then refit the terminal. Android Chrome
2977
+ // resizes the layout viewport itself (interactive-widget=resizes-content),
2978
+ // so --kb stays ~0 there — this is harmless and idempotent on desktop too.
2979
+ if (window.visualViewport) {
2980
+ const vv = window.visualViewport;
2981
+ const onVV = () => {
2982
+ const kb = Math.max(0, Math.round(window.innerHeight - vv.height - vv.offsetTop));
2983
+ document.documentElement.style.setProperty("--kb", kb + "px");
2984
+ if (fit)
2985
+ try {
2986
+ fit.fit();
2987
+ } catch {}
2988
+ };
2989
+ vv.addEventListener("resize", onVV);
2990
+ vv.addEventListener("scroll", onVV);
2991
+ onVV();
2992
+ }
2552
2993
 
2553
2994
  // Step the selection up/down the (filtered) list — same order the left panel
2554
2995
  // renders. Clamps at the ends, scrolls the row into view.
@@ -3158,7 +3599,15 @@
3158
3599
  // composite key so it picks the right host when pids collide across rooms.
3159
3600
  const [, room, aid] = full;
3160
3601
  autoPid = room + "#" + aid;
3161
- autoPidExplicit = true;
3602
+ // A #room:agentId hash is BOTH our own persisted selection (select()
3603
+ // writes it so a refresh reopens) AND a shareable deep link — same
3604
+ // format, two intents. Disambiguate by navigation type: a reload is a
3605
+ // restore (on a phone, just re-highlight the row and stay on the list,
3606
+ // per mergeRender), while a fresh navigation is a deliberate "take me
3607
+ // to this agent" that opens even on mobile. (No nav entry → treat as
3608
+ // explicit, preserving the old always-open behaviour.)
3609
+ const nav = performance.getEntriesByType("navigation")[0];
3610
+ autoPidExplicit = nav ? nav.type !== "reload" : true;
3162
3611
  const r = loadRooms()[room];
3163
3612
  if (r) pending = { room, token: r.token, host: r.host };
3164
3613
  } else if (bare && loadRooms()[bare[1]]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.123.0",
3
+ "version": "1.124.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",
@@ -1,8 +0,0 @@
1
- import "./ts-DgukRoEI.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./versionChecker-BqOr1YqC.js";
4
- import "./pidStore-CGKIhaJO.js";
5
- import "./globalPidIndex-C7r2m6s7.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-B4O2cFlt.js";
7
-
8
- export { SUPPORTED_CLIS };