agent-yes 1.123.0 → 1.125.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-Dlf0U2sb.js";
2
+ import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-DY4oPzQl.js";
4
+ import "./pidStore-CGKIhaJO.js";
5
+ import "./globalPidIndex-C7r2m6s7.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-gldLvxxn.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-Dlf0U2sb.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-gldLvxxn.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-DY4oPzQl.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-C13_tHG5.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-DNtSBnm9.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-Dlf0U2sb.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-BqOr1YqC.js";
3
+ import "./versionChecker-DY4oPzQl.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-Dlf0U2sb.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import "./versionChecker-BqOr1YqC.js";
3
+ import "./versionChecker-DY4oPzQl.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-gldLvxxn.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-BB-l_J5d.js.map
@@ -1,13 +1,13 @@
1
- import "./ts-DgukRoEI.js";
1
+ import "./ts-Dlf0U2sb.js";
2
2
  import "./logger-B9h0djqx.js";
3
- import { r as getInstalledPackage } from "./versionChecker-BqOr1YqC.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-DY4oPzQl.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-gldLvxxn.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-C1vGCBlc.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-Ch_WYsPB.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-Ch_WYsPB.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-BMo2EhUJ.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-C1vGCBlc.js";
6
6
 
7
7
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -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-Ch_WYsPB.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-BMo2EhUJ.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-BB-l_J5d.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-C1vGCBlc.js.map
@@ -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-DY4oPzQl.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-Dlf0U2sb.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.125.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-DY4oPzQl.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,41 @@
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="stab" title="Shift-Tab (e.g. cycle modes)">⇧Tab</button>
1248
+ <button class="kb" data-key="cc" title="Ctrl-C — interrupt">^C</button>
1249
+ <button class="kb" data-arrow="up" title="Up" aria-label="Up">↑</button>
1250
+ <button class="kb" data-arrow="down" title="Down" aria-label="Down">↓</button>
1251
+ <button class="kb" data-arrow="left" title="Left" aria-label="Left">←</button>
1252
+ <button class="kb" data-arrow="right" title="Right" aria-label="Right">→</button>
1253
+ <button class="kb" data-key="enter" title="Enter">⏎</button>
1254
+ </div>
1255
+ <!-- Line composer — the reliable path for typing a prompt on a phone:
1256
+ a normal textarea (no IME/autocorrect surprises that plague xterm's
1257
+ hidden input). Enter sends, Shift+Enter inserts a newline. -->
1258
+ <form class="composer" id="composer">
1259
+ <textarea
1260
+ id="cmpin"
1261
+ rows="1"
1262
+ placeholder="Message agent… ↵ to send"
1263
+ enterkeyhint="send"
1264
+ inputmode="text"
1265
+ autocapitalize="off"
1266
+ autocorrect="off"
1267
+ autocomplete="off"
1268
+ spellcheck="false"
1269
+ ></textarea>
1270
+ <button type="submit" class="cmpsend" title="Send to agent">Send</button>
1271
+ </form>
1070
1272
  <div class="perfhud" id="perfhud" hidden></div>
1071
1273
  </div>
1072
1274
  </div>
@@ -1117,6 +1319,28 @@
1117
1319
  let es = null; // live-tail subscription closer
1118
1320
  let term = null; // xterm.js Terminal rendering the raw PTY stream
1119
1321
  let fit = null;
1322
+ // Terminal font size (px) — adjustable from the ⋯ menu, persisted across
1323
+ // reloads, applied live to the open terminal and used by every new one.
1324
+ let termFontSize = 12;
1325
+ try {
1326
+ const n = parseInt(localStorage.getItem("ay.fontSize") || "", 10);
1327
+ if (n >= 8 && n <= 32) termFontSize = n;
1328
+ } catch {}
1329
+ function setTermFontSize(px) {
1330
+ termFontSize = Math.max(8, Math.min(32, px | 0));
1331
+ const v = $("fontVal");
1332
+ if (v) v.textContent = String(termFontSize);
1333
+ try {
1334
+ localStorage.setItem("ay.fontSize", String(termFontSize));
1335
+ } catch {}
1336
+ if (term) {
1337
+ term.options.fontSize = termFontSize;
1338
+ if (fit)
1339
+ try {
1340
+ fit.fit();
1341
+ } catch {}
1342
+ }
1343
+ }
1120
1344
 
1121
1345
  // xterm paints to a <canvas>, so unlike the CSS var() consumers it can't
1122
1346
  // ride prefers-color-scheme on its own — its theme is a JS object. Mirror
@@ -2020,6 +2244,161 @@
2020
2244
  await new Promise((r) => setTimeout(r, 200));
2021
2245
  await send("\r");
2022
2246
  }
2247
+
2248
+ // ---- On-screen key bar (mobile) -------------------------------------
2249
+ // A phone keyboard has no Esc/Tab/Ctrl/arrows, so agent TUIs (claude,
2250
+ // codex, vim…) are otherwise undriveable. The bar posts raw byte
2251
+ // sequences straight to the selected agent's stdin, and a sticky Ctrl/Alt
2252
+ // rewrites the NEXT character typed on the soft keyboard.
2253
+ let modCtrl = false,
2254
+ modAlt = false;
2255
+ // Send raw bytes to the currently-selected agent. Uses the live `sel`, so
2256
+ // it always hits the open agent even while an old terminal tears down mid
2257
+ // switch — the same /api/send wire the terminal's own input uses.
2258
+ function sendToSelected(data) {
2259
+ const e = sel ? entries.find((x) => x._key === sel) : null;
2260
+ if (!e || data == null) return;
2261
+ perfNote("in", typeof data === "string" ? data.length : (data?.byteLength ?? 0));
2262
+ txFor(e)
2263
+ .post("/api/send", { keyword: String(e.pid), msg: data, code: "none" })
2264
+ .catch(() => {});
2265
+ }
2266
+ // Apply a pending sticky Ctrl/Alt to ONE typed character (soft-keyboard
2267
+ // path only). Multi-char input — paste, an IME commit, an escape sequence —
2268
+ // passes through untouched. Ctrl folds @A–Z[\]^_ (case-insensitive) to
2269
+ // 0x00–0x1f; Alt/Meta is an ESC prefix. One-shot: clears after one char.
2270
+ function applyStickyMods(d) {
2271
+ if ((!modCtrl && !modAlt) || typeof d !== "string" || d.length !== 1) return d;
2272
+ let c = d;
2273
+ if (modCtrl) {
2274
+ const u = d.toUpperCase().charCodeAt(0);
2275
+ if (u >= 64 && u <= 95) c = String.fromCharCode(u - 64);
2276
+ else if (u === 32) c = "\x00"; // Ctrl-Space → NUL
2277
+ }
2278
+ if (modAlt) c = "\x1b" + c;
2279
+ modCtrl = modAlt = false;
2280
+ syncModButtons();
2281
+ return c;
2282
+ }
2283
+ function syncModButtons() {
2284
+ const kb = $("keybar");
2285
+ if (!kb) return;
2286
+ kb.querySelector('[data-mod="ctrl"]')?.classList.toggle("on", modCtrl);
2287
+ kb.querySelector('[data-mod="alt"]')?.classList.toggle("on", modAlt);
2288
+ }
2289
+ (function keybarBoot() {
2290
+ const kb = $("keybar");
2291
+ if (!kb) return;
2292
+ // stab = Shift-Tab → CBT (ESC [ Z); back-tab / reverse field nav, and
2293
+ // what Claude Code's TUI reads to cycle permission modes.
2294
+ const SEQ = { esc: "\x1b", tab: "\t", stab: "\x1b[Z", enter: "\r", cc: "\x03" };
2295
+ const ARROW = { up: "A", down: "B", right: "C", left: "D" };
2296
+ // Don't let a button steal focus — that would drop the soft keyboard and
2297
+ // blur whatever the user is typing into (terminal or composer).
2298
+ kb.addEventListener("pointerdown", (ev) => {
2299
+ if (ev.target.closest("button")) ev.preventDefault();
2300
+ });
2301
+ kb.addEventListener("click", (ev) => {
2302
+ const b = ev.target.closest("button.kb");
2303
+ if (!b) return;
2304
+ if (b.dataset.mod) {
2305
+ // sticky toggle — armed for the next soft-keyboard character
2306
+ if (b.dataset.mod === "ctrl") modCtrl = !modCtrl;
2307
+ else modAlt = !modAlt;
2308
+ syncModButtons();
2309
+ return;
2310
+ }
2311
+ let seq = null;
2312
+ if (b.dataset.arrow) {
2313
+ const L = ARROW[b.dataset.arrow];
2314
+ if (modCtrl || modAlt) {
2315
+ // Ctrl/Alt + arrow → CSI modifier form ESC [ 1 ; <m> <L>, where
2316
+ // m = 1 + Alt(2) + Ctrl(4) (e.g. Ctrl-Left = ESC [ 1 ; 5 D for
2317
+ // word-wise motion). Modified cursor keys are always CSI, never
2318
+ // SS3, so DECCKM doesn't apply when a modifier is held.
2319
+ seq = "\x1b[1;" + (1 + (modAlt ? 2 : 0) + (modCtrl ? 4 : 0)) + L;
2320
+ } else {
2321
+ // Honour application-cursor-key mode (DECCKM): TUIs that enable it
2322
+ // expect SS3 (ESC O x) arrows, not CSI (ESC [ x). xterm tracks it.
2323
+ const app = term && term.modes && term.modes.applicationCursorKeysMode;
2324
+ seq = (app ? "\x1bO" : "\x1b[") + L;
2325
+ }
2326
+ } else if (b.dataset.key) {
2327
+ seq = SEQ[b.dataset.key];
2328
+ // Alt/Meta on a fixed key = ESC prefix (e.g. Alt+Enter). Ctrl on
2329
+ // Esc/Tab/Enter has no standard sequence, so it's left as the bare key.
2330
+ if (seq != null && modAlt) seq = "\x1b" + seq;
2331
+ }
2332
+ if (seq == null) return;
2333
+ // the armed modifier (if any) has now been folded into this key — clear it
2334
+ modCtrl = modAlt = false;
2335
+ syncModButtons();
2336
+ sendToSelected(seq);
2337
+ });
2338
+ })();
2339
+ // ---- Line composer (mobile) ----------------------------------------
2340
+ // The dependable way to type a prompt on a phone: a real textarea, immune
2341
+ // to the IME/autocorrect glitches that dog xterm's hidden input. Enter
2342
+ // sends text + a carriage return in one write (identical to typing then ↵);
2343
+ // Shift+Enter inserts a literal newline.
2344
+ (function composerBoot() {
2345
+ const form = $("composer"),
2346
+ ta = $("cmpin");
2347
+ if (!form || !ta) return;
2348
+ const grow = () => {
2349
+ ta.style.height = "auto";
2350
+ ta.style.height = Math.min(ta.scrollHeight, 120) + "px";
2351
+ };
2352
+ const sendLine = () => {
2353
+ let v = ta.value;
2354
+ if (!v) return;
2355
+ // An armed sticky Ctrl/Alt decorates a single-char line (e.g. Ctrl-D);
2356
+ // longer text can't carry it, but the modifier must still be cleared so
2357
+ // it doesn't silently leak into the next terminal keystroke.
2358
+ v = applyStickyMods(v);
2359
+ if (modCtrl || modAlt) {
2360
+ modCtrl = modAlt = false;
2361
+ syncModButtons();
2362
+ }
2363
+ sendToSelected(v + "\r");
2364
+ ta.value = "";
2365
+ grow();
2366
+ ta.focus(); // keep the soft keyboard up for the next line
2367
+ };
2368
+ ta.addEventListener("input", grow);
2369
+ form.addEventListener("submit", (ev) => {
2370
+ ev.preventDefault();
2371
+ sendLine();
2372
+ });
2373
+ // Enter sends; Shift+Enter inserts a newline; never fire mid-IME-commit
2374
+ // (the confirming Enter reports keyCode 229 / isComposing). `enterSeen`
2375
+ // lets the beforeinput fallback below know keydown already decided this
2376
+ // Enter (so it doesn't double-send, and Shift+Enter still makes a newline).
2377
+ let enterSeen = false;
2378
+ ta.addEventListener("keydown", (ev) => {
2379
+ enterSeen = false;
2380
+ if (ev.key === "Enter" && !ev.isComposing && ev.keyCode !== 229) {
2381
+ enterSeen = true;
2382
+ if (!ev.shiftKey) {
2383
+ ev.preventDefault();
2384
+ sendLine();
2385
+ }
2386
+ }
2387
+ });
2388
+ // Android (Gboard) commonly delivers Enter as keyCode 229 / key
2389
+ // "Unidentified", so the keydown above never matches it. beforeinput's
2390
+ // insertLineBreak fires reliably there; skip it when keydown already
2391
+ // saw the Enter (desktop/iOS, including the Shift+Enter newline case).
2392
+ ta.addEventListener("beforeinput", (ev) => {
2393
+ if (ev.inputType !== "insertLineBreak") return;
2394
+ if (enterSeen) {
2395
+ enterSeen = false;
2396
+ return;
2397
+ }
2398
+ ev.preventDefault();
2399
+ sendLine();
2400
+ });
2401
+ })();
2023
2402
  (function perfMenuBoot() {
2024
2403
  const btn = $("rmenubtn"),
2025
2404
  panel = $("rmenupanel"),
@@ -2037,6 +2416,10 @@
2037
2416
  if (cb) cb.addEventListener("change", () => setPerfHud(cb.checked));
2038
2417
  $("stopAgent")?.addEventListener("click", () => (closePanel(), stopAgent(false)));
2039
2418
  $("killAgent")?.addEventListener("click", () => (closePanel(), stopAgent(true)));
2419
+ // Font stepper — stays open so you can tap repeatedly; persists + refits.
2420
+ $("fontDown")?.addEventListener("click", () => setTermFontSize(termFontSize - 1));
2421
+ $("fontUp")?.addEventListener("click", () => setTermFontSize(termFontSize + 1));
2422
+ setTermFontSize(termFontSize); // sync the label with the restored value
2040
2423
  let saved = false;
2041
2424
  try {
2042
2425
  saved = localStorage.getItem("ay.perfHud") === "1";
@@ -2397,7 +2780,7 @@
2397
2780
  disableStdin: false,
2398
2781
  cursorBlink: true,
2399
2782
  scrollback: 5000,
2400
- fontSize: 12,
2783
+ fontSize: termFontSize,
2401
2784
  fontFamily: 'ui-monospace, "SF Mono", Menlo, monospace',
2402
2785
  theme: termTheme(),
2403
2786
  });
@@ -2463,10 +2846,11 @@
2463
2846
  // keyword with 400 (pid arrives as a number from /api/ls JSON).
2464
2847
  const kw = String(pid);
2465
2848
  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
- }
2849
+ if (sel !== e._key) return; // ignore a stale terminal mid-switch
2850
+ // a soft-keyboard character may be decorated by an armed sticky Ctrl/Alt
2851
+ if (typeof d === "string") d = applyStickyMods(d);
2852
+ perfNote("in", typeof d === "string" ? d.length : (d?.byteLength ?? 0));
2853
+ tx.post("/api/send", { keyword: kw, msg: d, code: "none" }).catch(() => {});
2470
2854
  };
2471
2855
  term.onData(fwd);
2472
2856
  term.onBinary(fwd);
@@ -2527,11 +2911,51 @@
2527
2911
  const row = ev.target.closest(".row");
2528
2912
  if (row) select(row.dataset.key);
2529
2913
  });
2530
- // Mobile back button: return to the list pane. The tail keeps streaming in the
2914
+ // Mobile back: return to the list pane. The tail keeps streaming in the
2531
2915
  // background (selection unchanged), so reopening the agent is instant.
2532
- $("rback").addEventListener("click", () => {
2533
- document.querySelector(".app").classList.remove("show-detail");
2534
- });
2916
+ const goBack = () => document.querySelector(".app").classList.remove("show-detail");
2917
+ $("rback").addEventListener("click", goBack);
2918
+ // Swipe-right-from-the-left-edge → back, an alternative to the ‹ button.
2919
+ // Passive/read-only so it never fights xterm's own scroll or text
2920
+ // selection; armed only from the left edge, in the single-column view, and
2921
+ // never when a TUI has mouse tracking on (that touch belongs to the agent).
2922
+ (function swipeBackBoot() {
2923
+ const pane = document.querySelector(".right");
2924
+ if (!pane) return;
2925
+ let sx = 0,
2926
+ sy = 0,
2927
+ st = 0,
2928
+ armed = false;
2929
+ pane.addEventListener(
2930
+ "touchstart",
2931
+ (ev) => {
2932
+ armed = false;
2933
+ if (window.innerWidth > 720 || ev.touches.length !== 1) return;
2934
+ const t = ev.touches[0];
2935
+ armed = t.clientX <= 28; // left-edge start only
2936
+ sx = t.clientX;
2937
+ sy = t.clientY;
2938
+ st = ev.timeStamp;
2939
+ },
2940
+ { passive: true },
2941
+ );
2942
+ pane.addEventListener(
2943
+ "touchend",
2944
+ (ev) => {
2945
+ if (!armed) return;
2946
+ armed = false;
2947
+ const mt = term && term.modes && term.modes.mouseTrackingMode;
2948
+ if (mt && mt !== "none") return; // the TUI wants this touch
2949
+ const t = ev.changedTouches[0];
2950
+ if (!t) return;
2951
+ const dx = t.clientX - sx,
2952
+ dy = t.clientY - sy;
2953
+ // a quick, clearly-horizontal rightward flick (not a scroll/tap)
2954
+ if (dx > 45 && Math.abs(dx) > 2 * Math.abs(dy) && ev.timeStamp - st < 500) goBack();
2955
+ },
2956
+ { passive: true },
2957
+ );
2958
+ })();
2535
2959
  $("q").addEventListener("input", () => {
2536
2960
  try {
2537
2961
  localStorage.setItem("ay.filter", $("q").value);
@@ -2549,6 +2973,26 @@
2549
2973
  fit.fit();
2550
2974
  } catch {}
2551
2975
  });
2976
+ // iOS overlays the soft keyboard ON TOP of the layout viewport rather than
2977
+ // shrinking it, so a bottom-anchored composer would hide behind it. Track
2978
+ // the visual viewport and reserve the covered height as --kb (which .app
2979
+ // subtracts from its height), then refit the terminal. Android Chrome
2980
+ // resizes the layout viewport itself (interactive-widget=resizes-content),
2981
+ // so --kb stays ~0 there — this is harmless and idempotent on desktop too.
2982
+ if (window.visualViewport) {
2983
+ const vv = window.visualViewport;
2984
+ const onVV = () => {
2985
+ const kb = Math.max(0, Math.round(window.innerHeight - vv.height - vv.offsetTop));
2986
+ document.documentElement.style.setProperty("--kb", kb + "px");
2987
+ if (fit)
2988
+ try {
2989
+ fit.fit();
2990
+ } catch {}
2991
+ };
2992
+ vv.addEventListener("resize", onVV);
2993
+ vv.addEventListener("scroll", onVV);
2994
+ onVV();
2995
+ }
2552
2996
 
2553
2997
  // Step the selection up/down the (filtered) list — same order the left panel
2554
2998
  // renders. Clamps at the ends, scrolls the row into view.
@@ -3158,7 +3602,15 @@
3158
3602
  // composite key so it picks the right host when pids collide across rooms.
3159
3603
  const [, room, aid] = full;
3160
3604
  autoPid = room + "#" + aid;
3161
- autoPidExplicit = true;
3605
+ // A #room:agentId hash is BOTH our own persisted selection (select()
3606
+ // writes it so a refresh reopens) AND a shareable deep link — same
3607
+ // format, two intents. Disambiguate by navigation type: a reload is a
3608
+ // restore (on a phone, just re-highlight the row and stay on the list,
3609
+ // per mergeRender), while a fresh navigation is a deliberate "take me
3610
+ // to this agent" that opens even on mobile. (No nav entry → treat as
3611
+ // explicit, preserving the old always-open behaviour.)
3612
+ const nav = performance.getEntriesByType("navigation")[0];
3613
+ autoPidExplicit = nav ? nav.type !== "reload" : true;
3162
3614
  const r = loadRooms()[room];
3163
3615
  if (r) pending = { room, token: r.token, host: r.host };
3164
3616
  } 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.125.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 };