agent-yes 1.244.2 → 1.244.3-beta.583.1

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-DE2iV6rV.js";
1
+ import { t as CLIS_CONFIG } from "./ts-DWtqCoKd.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-76JVIvo2.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-BX6n7wgh.js.map
@@ -1,11 +1,11 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import "./versionChecker-Ba8m9VTm.js";
3
- import "./ts-DE2iV6rV.js";
2
+ import "./versionChecker-D5YX0jkS.js";
3
+ import "./ts-DWtqCoKd.js";
4
4
  import "./todoAutomation-Buo457VO.js";
5
5
  import "./JsonlStore-CPGIR0Wy.js";
6
6
  import "./globalPidIndex-D9DOSd-c.js";
7
7
  import "./pidStore-6EZd5DUv.js";
8
8
  import "./messageLog-C-YIzhRj.js";
9
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-76JVIvo2.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BX6n7wgh.js";
10
10
 
11
11
  export { SUPPORTED_CLIS };
@@ -3,7 +3,7 @@ import "./globalPidIndex-D9DOSd-c.js";
3
3
  import "./messageLog-C-YIzhRj.js";
4
4
  import "./e2e-CEIdJGZ7.js";
5
5
  import "./configShared-9wVlbPyC.js";
6
- import { P as resolveOne } from "./subcommands-DHzOWlWy.js";
6
+ import { P as resolveOne } from "./subcommands-D33eq_nw.js";
7
7
  import "./webrtcLink-BMYneHbu.js";
8
8
  import "./remotes-BxQnNxQv.js";
9
9
  import { a as startShare } from "./share-BGl3wutJ.js";
@@ -229,4 +229,4 @@ function transformEvent(rawEvent, agentId, forwarded) {
229
229
 
230
230
  //#endregion
231
231
  export { createScopedShare, listShares, revokeAllShares, revokeShare };
232
- //# sourceMappingURL=agentShare-BokFN5yh.js.map
232
+ //# sourceMappingURL=agentShare-CZX8swiZ.js.map
@@ -13013,6 +13013,7 @@ var AyTerminal = class {
13013
13013
  capTimer = null;
13014
13014
  capHeartbeat = null;
13015
13015
  lastCap = null;
13016
+ panelEl;
13016
13017
  constructor(info) {
13017
13018
  const o = typeof info === "string" || typeof info === "number" ? { pid: info } : info;
13018
13019
  if (o.pid === void 0 || o.pid === null || o.pid === "") throw new Error("AyTerminal: need a pid/keyword");
@@ -13076,10 +13077,22 @@ var AyTerminal = class {
13076
13077
  this.sendCap(this.lastCap.cols, this.lastCap.rows);
13077
13078
  }, 5e3);
13078
13079
  }
13079
- /** True while this widget's terminal grid is actually rendered (panel open & visible). */
13080
+ /**
13081
+ * True while this widget's panel is actually on-screen. Checks the PANEL element
13082
+ * itself — its computed `display` and bounding rect — NOT the xterm grid: xterm
13083
+ * keeps a non-zero `cols/rows` (and its `.xterm` node a cached box) even when an
13084
+ * ancestor is `display:none`, so measuring the grid falsely reads "visible" while
13085
+ * collapsed (grocy's ③). The panel's own `display:none`/0×0 rect is unambiguous.
13086
+ */
13080
13087
  isPanelVisible() {
13081
- const el = this.term?.element;
13082
- return !!(el && el.offsetWidth && el.offsetHeight);
13088
+ const p = this.panelEl;
13089
+ if (!p) return false;
13090
+ const g = globalThis;
13091
+ try {
13092
+ if (g.getComputedStyle?.(p)?.display === "none") return false;
13093
+ } catch {}
13094
+ const r = p.getBoundingClientRect?.();
13095
+ return !!(r && r.width > 0 && r.height > 0);
13083
13096
  }
13084
13097
  /** Withdraw this widget's size cap (closed/minimized) → the daemon re-negotiates without it. */
13085
13098
  withdrawCap() {
@@ -13215,8 +13228,13 @@ var AyTerminal = class {
13215
13228
  const wrap = root.getElementById("wrap");
13216
13229
  const titleEl = root.getElementById("title");
13217
13230
  this.badgeEl = root.getElementById("badge");
13231
+ this.panelEl = panel;
13218
13232
  if (this.transparent) panel.classList.add("transparent");
13219
13233
  const reflow = () => {
13234
+ if (!this.readOnly && !this.isPanelVisible()) {
13235
+ if (this.capHeartbeat || this.lastCap) this.withdrawCap();
13236
+ return;
13237
+ }
13220
13238
  const xt = inner.querySelector(".xterm");
13221
13239
  if (!xt || !xt.offsetWidth || !xt.offsetHeight) {
13222
13240
  if (!this.readOnly && (this.capHeartbeat || this.lastCap)) this.withdrawCap();
@@ -13238,6 +13256,9 @@ var AyTerminal = class {
13238
13256
  try {
13239
13257
  new g.ResizeObserver(() => reflow()).observe(wrap);
13240
13258
  } catch {}
13259
+ try {
13260
+ new g.IntersectionObserver(() => reflow()).observe(panel);
13261
+ } catch {}
13241
13262
  g.addEventListener?.("resize", reflow);
13242
13263
  (async () => {
13243
13264
  await this.start(inner);
@@ -13524,4 +13545,4 @@ const XTERM_CSS = `
13524
13545
 
13525
13546
  //#endregion
13526
13547
  export { AyTerminal as t };
13527
- //# sourceMappingURL=browser-CNdIibfn.js.map
13548
+ //# sourceMappingURL=browser-DV0CLEwt.js.map
@@ -3,10 +3,10 @@ import "./globalPidIndex-D9DOSd-c.js";
3
3
  import "./messageLog-C-YIzhRj.js";
4
4
  import "./e2e-CEIdJGZ7.js";
5
5
  import "./configShared-9wVlbPyC.js";
6
- import "./subcommands-DHzOWlWy.js";
6
+ import "./subcommands-D33eq_nw.js";
7
7
  import "./webrtcLink-BMYneHbu.js";
8
8
  import "./remotes-BxQnNxQv.js";
9
9
  import "./callbackCore-Dd28_cBM.js";
10
- import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-CfpP3G3E.js";
10
+ import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-D4o-uhJ4.js";
11
11
 
12
12
  export { cmdCallback };
@@ -1,5 +1,5 @@
1
1
  import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
2
- import { P as resolveOne } from "./subcommands-DHzOWlWy.js";
2
+ import { P as resolveOne } from "./subcommands-D33eq_nw.js";
3
3
  import { a as mintCapability, o as parseExpires } from "./callbackCore-Dd28_cBM.js";
4
4
  import { randomBytes } from "node:crypto";
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -72,7 +72,7 @@ async function resolveBase(flag) {
72
72
  if (flag) return flag;
73
73
  if (process.env.AGENT_YES_CALLBACK_BASE) return process.env.AGENT_YES_CALLBACK_BASE;
74
74
  try {
75
- const { resolveLocalServeUrl } = await import("./serve-CeFGSWkZ.js");
75
+ const { resolveLocalServeUrl } = await import("./serve-DPkqCb2w.js");
76
76
  const url = await resolveLocalServeUrl();
77
77
  if (url) return url;
78
78
  } catch {}
@@ -187,4 +187,4 @@ async function cmdCallback(rest) {
187
187
 
188
188
  //#endregion
189
189
  export { loadOrCreateCallbackSecret as i, isCallbackRevoked as n, loadCallbackSecretReadOnly as r, cmdCallback as t };
190
- //# sourceMappingURL=callback-CfpP3G3E.js.map
190
+ //# sourceMappingURL=callback-D4o-uhJ4.js.map
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
3
3
  import { n as logger } from "./logger-CDIsZ-Pp.js";
4
- import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-Ba8m9VTm.js";
5
- import { n as getRustBinary } from "./rustBinary-CK3EucYy.js";
4
+ import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-D5YX0jkS.js";
5
+ import { n as getRustBinary } from "./rustBinary-vHEy7gil.js";
6
6
  import { argv } from "process";
7
7
  import { spawn } from "child_process";
8
8
  import ms from "ms";
@@ -285,7 +285,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
285
285
  const rawArg = process.argv[2];
286
286
  const managerCommands = !invokedCliName(process.argv);
287
287
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
288
- const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken } = await import("./subcommands-CPRjp_FY.js");
288
+ const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken } = await import("./subcommands-hy7ih8Z2.js");
289
289
  if (isHelpFlag && process.argv.length === 3) {
290
290
  await cmdHelp(managerCommands);
291
291
  process.exit(0);
@@ -295,7 +295,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
295
295
  process.exit(code ?? 0);
296
296
  }
297
297
  {
298
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-wtZAWIqV.js");
298
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DP4rEjQn.js");
299
299
  if (isUnknownManagerToken(rawArg, managerCommands, SUPPORTED_CLIS)) {
300
300
  process.stderr.write(`ay: unknown subcommand or CLI '${rawArg}'.\n See 'ay help' for subcommands. To run an agent, name a CLI:\n 'ay <cli> …' (e.g. 'ay claude …') or 'ay --cli <cli> …'.\n`);
301
301
  process.exit(1);
@@ -346,7 +346,7 @@ if (config.useRust) {
346
346
  }
347
347
  }
348
348
  if (rustBinary) {
349
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-wtZAWIqV.js");
349
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DP4rEjQn.js");
350
350
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
351
351
  if (config.verbose) {
352
352
  console.log(`[rust] Using binary: ${rustBinary}`);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import "./versionChecker-Ba8m9VTm.js";
3
- import { a as hasTodoStore, c as AgentContext, i as answerAsk, n as agentYes, o as listAsks, r as config, s as listAsksForProject, t as CLIS_CONFIG } from "./ts-DE2iV6rV.js";
2
+ import "./versionChecker-D5YX0jkS.js";
3
+ import { a as hasTodoStore, c as AgentContext, i as answerAsk, n as agentYes, o as listAsks, r as config, s as listAsksForProject, t as CLIS_CONFIG } from "./ts-DWtqCoKd.js";
4
4
  import { _ as isKnownKind, a as renderTree, b as statesOf, c as monitorHint, d as openStore, f as DONE_STATE, g as initialState, h as canTransition, i as renderDigest, l as CycleError, m as ORPHANED_STATE, o as unblockedTasks, p as LIFECYCLES, r as openBlockers, s as describeBlock, t as reconcileTodos, u as TodoStore, v as nextStates, x as removeControlCharacters, y as requiredGate } from "./todoAutomation-Buo457VO.js";
5
5
  import "./JsonlStore-CPGIR0Wy.js";
6
6
  import "./globalPidIndex-D9DOSd-c.js";
@@ -3,7 +3,7 @@ import "./globalPidIndex-D9DOSd-c.js";
3
3
  import "./messageLog-C-YIzhRj.js";
4
4
  import "./e2e-CEIdJGZ7.js";
5
5
  import "./configShared-9wVlbPyC.js";
6
- import { $ as shouldStealLock, C as listRecords, J as hostId, K as appendEvent, Q as readInbox, X as listInboxParents, Z as liveWatchers, _ as isPidAlive, c as deriveLiveState, et as daemonLockDir, j as renderLogTailLines, nt as notifyDir, q as gcInboxes, tt as daemonLockOwnerPath } from "./subcommands-DHzOWlWy.js";
6
+ import { $ as shouldStealLock, C as listRecords, J as hostId, K as appendEvent, Q as readInbox, X as listInboxParents, Z as liveWatchers, _ as isPidAlive, c as deriveLiveState, et as daemonLockDir, j as renderLogTailLines, nt as notifyDir, q as gcInboxes, tt as daemonLockOwnerPath } from "./subcommands-D33eq_nw.js";
7
7
  import "./webrtcLink-BMYneHbu.js";
8
8
  import "./remotes-BxQnNxQv.js";
9
9
  import { mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
@@ -589,4 +589,4 @@ async function ensureDaemon() {
589
589
 
590
590
  //#endregion
591
591
  export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
592
- //# sourceMappingURL=notifyDaemon-2Wt04RUC.js.map
592
+ //# sourceMappingURL=notifyDaemon-BtZ9JXNc.js.map
@@ -1,4 +1,4 @@
1
- import { r as getInstalledPackage } from "./versionChecker-Ba8m9VTm.js";
1
+ import { r as getInstalledPackage } from "./versionChecker-D5YX0jkS.js";
2
2
  import { execFileSync } from "child_process";
3
3
  import { existsSync, mkdirSync, unlinkSync } from "fs";
4
4
  import { chmod, copyFile } from "fs/promises";
@@ -225,4 +225,4 @@ async function getRustBinary(options = {}) {
225
225
 
226
226
  //#endregion
227
227
  export { getRustBinary as n, findSpawnHiddenLauncher as t };
228
- //# sourceMappingURL=rustBinary-CK3EucYy.js.map
228
+ //# sourceMappingURL=rustBinary-vHEy7gil.js.map
@@ -1,12 +1,12 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import "./versionChecker-Ba8m9VTm.js";
3
- import "./ts-DE2iV6rV.js";
2
+ import "./versionChecker-D5YX0jkS.js";
3
+ import "./ts-DWtqCoKd.js";
4
4
  import "./todoAutomation-Buo457VO.js";
5
5
  import "./JsonlStore-CPGIR0Wy.js";
6
6
  import "./globalPidIndex-D9DOSd-c.js";
7
7
  import "./pidStore-6EZd5DUv.js";
8
8
  import "./messageLog-C-YIzhRj.js";
9
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-76JVIvo2.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BX6n7wgh.js";
10
10
  import { d as resolveSpawnCwd } from "./workspaceConfig-DOHygA2x.js";
11
11
  import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-BtL6Oo1o.js";
12
12
  import { createHash } from "node:crypto";
@@ -153,4 +153,4 @@ async function cmdSchedule(rest) {
153
153
 
154
154
  //#endregion
155
155
  export { cmdSchedule };
156
- //# sourceMappingURL=schedule-D_gxvnkf.js.map
156
+ //# sourceMappingURL=schedule-QWsOlF5S.js.map
@@ -1,7 +1,7 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import { r as getInstalledPackage } from "./versionChecker-Ba8m9VTm.js";
3
- import { t as findSpawnHiddenLauncher } from "./rustBinary-CK3EucYy.js";
4
- import { i as answerAsk, l as CLAUDE_SESSION_PIN_ENV, o as listAsks } from "./ts-DE2iV6rV.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-D5YX0jkS.js";
3
+ import { t as findSpawnHiddenLauncher } from "./rustBinary-vHEy7gil.js";
4
+ import { i as answerAsk, l as CLAUDE_SESSION_PIN_ENV, o as listAsks } from "./ts-DWtqCoKd.js";
5
5
  import { t as agentYesHome$1 } from "./agentYesHome-CtmsY6I-.js";
6
6
  import { x as removeControlCharacters } from "./todoAutomation-Buo457VO.js";
7
7
  import "./JsonlStore-CPGIR0Wy.js";
@@ -11,12 +11,12 @@ import { r as recordInbox } from "./messageLog-C-YIzhRj.js";
11
11
  import { t as pgidForWrapper } from "./reaper-DP49VCKx.js";
12
12
  import "./e2e-CEIdJGZ7.js";
13
13
  import "./configShared-9wVlbPyC.js";
14
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-76JVIvo2.js";
15
- import { A as recentReadEdges, C as listRecords, D as readNotes, G as writeToIpc, M as renderRawLog, N as renderRawLogLines, O as readPtysize, P as resolveOne, Y as isTransientLockMkdirError, f as extractNeedsInput, j as renderLogTailLines, k as recentMessageEdges, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, rt as TYPING_BADGE, u as extractBadges, x as isUserTyping, z as snapshotStatus } from "./subcommands-DHzOWlWy.js";
14
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BX6n7wgh.js";
15
+ import { A as recentReadEdges, C as listRecords, D as readNotes, G as writeToIpc, M as renderRawLog, N as renderRawLogLines, O as readPtysize, P as resolveOne, Y as isTransientLockMkdirError, f as extractNeedsInput, j as renderLogTailLines, k as recentMessageEdges, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, rt as TYPING_BADGE, u as extractBadges, x as isUserTyping, z as snapshotStatus } from "./subcommands-D33eq_nw.js";
16
16
  import "./webrtcLink-BMYneHbu.js";
17
17
  import "./remotes-BxQnNxQv.js";
18
18
  import { i as frameVisitorMessage, s as verifyCapability, t as MAX_CALLBACK_MSG_BYTES } from "./callbackCore-Dd28_cBM.js";
19
- import { n as isCallbackRevoked, r as loadCallbackSecretReadOnly } from "./callback-CfpP3G3E.js";
19
+ import { n as isCallbackRevoked, r as loadCallbackSecretReadOnly } from "./callback-D4o-uhJ4.js";
20
20
  import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-DOHygA2x.js";
21
21
  import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-BtL6Oo1o.js";
22
22
  import { r as spawnRejectionReason } from "./spawnGate-BxgS_GlF.js";
@@ -2294,7 +2294,7 @@ Options:
2294
2294
  }
2295
2295
  });
2296
2296
  if (req.method === "GET" && p === "/api/ws") {
2297
- const ws = await import("./ws-CZEJdPBM.js");
2297
+ const ws = await import("./ws-D4iUZSx3.js");
2298
2298
  try {
2299
2299
  await ws.loadProvision();
2300
2300
  } catch (e) {
@@ -2314,7 +2314,7 @@ Options:
2314
2314
  if (req.method === "GET" && p === "/api/ws/status") {
2315
2315
  const dirRaw = url.searchParams.get("path");
2316
2316
  if (!dirRaw) return new Response("missing ?path=<workspace dir>", { status: 400 });
2317
- const ws = await import("./ws-CZEJdPBM.js");
2317
+ const ws = await import("./ws-D4iUZSx3.js");
2318
2318
  let prov;
2319
2319
  try {
2320
2320
  prov = await ws.loadProvision();
@@ -2769,19 +2769,38 @@ Options:
2769
2769
  if (!cols || !rows) return new Response("missing cols/rows", { status: 400 });
2770
2770
  try {
2771
2771
  const record = await resolveOne(keyword, defaultOpts());
2772
- process.stderr.write(`[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize auth=${authResult.kind} origin=${req.headers.get("origin") ?? "-"} ref=${req.headers.get("referer") ?? "-"} ua=${(req.headers.get("user-agent") ?? "-").slice(0, 80)}\n`);
2773
- const ayHome = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
2774
- const winsizeDir = path.join(ayHome, "winsize");
2775
- await mkdir(winsizeDir, { recursive: true });
2776
- await writeFile(path.join(winsizeDir, String(record.pid)), `${cols} ${rows} ${Date.now()}\n`);
2777
- try {
2778
- process.kill(record.pid, "SIGWINCH");
2779
- } catch {}
2772
+ const originTrace = `auth=${authResult.kind} origin=${req.headers.get("origin") ?? "-"} ref=${req.headers.get("referer") ?? "-"} ua=${(req.headers.get("user-agent") ?? "-").slice(0, 80)}`;
2773
+ if (body.force === true && authResult.kind === "master") {
2774
+ process.stderr.write(`[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize-FORCED ${originTrace}\n`);
2775
+ const file = winsizePathFor(record.pid);
2776
+ await mkdir(path.dirname(file), { recursive: true });
2777
+ await writeFile(file, `${cols} ${rows} ${Date.now()}\n`);
2778
+ try {
2779
+ process.kill(record.pid, "SIGWINCH");
2780
+ } catch {}
2781
+ return Response.json({
2782
+ ok: true,
2783
+ pid: record.pid,
2784
+ cols,
2785
+ rows,
2786
+ forced: true
2787
+ });
2788
+ }
2789
+ const cap = sanitizeCap({
2790
+ cols,
2791
+ rows
2792
+ });
2793
+ if (!cap) return new Response("missing cols/rows", { status: 400 });
2794
+ const viewer = (body.viewer ? String(body.viewer).slice(0, 64) : "") || (authResult.kind === "scoped" ? `api:${authResult.scope.pid}` : "api-resize");
2795
+ process.stderr.write(`[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize-cap viewer=${viewer} ${originTrace}\n`);
2796
+ await publishCap(record.pid, viewer, cap, "viewer");
2797
+ scheduleNego(record.pid);
2780
2798
  return Response.json({
2781
2799
  ok: true,
2782
2800
  pid: record.pid,
2783
2801
  cols,
2784
- rows
2802
+ rows,
2803
+ mode: "cap"
2785
2804
  });
2786
2805
  } catch (e) {
2787
2806
  return new Response(e.message, { status: 404 });
@@ -3045,7 +3064,7 @@ Options:
3045
3064
  const perm = body.perm ?? "r";
3046
3065
  if (perm !== "r" && perm !== "rw") return new Response(`invalid perm ${perm} (want r or rw)`, { status: 400 });
3047
3066
  try {
3048
- const { createScopedShare } = await import("./agentShare-BokFN5yh.js");
3067
+ const { createScopedShare } = await import("./agentShare-CZX8swiZ.js");
3049
3068
  const share = await createScopedShare({
3050
3069
  agent: body.agent,
3051
3070
  perm,
@@ -3060,12 +3079,12 @@ Options:
3060
3079
  }
3061
3080
  }
3062
3081
  if (req.method === "GET" && p === "/api/shares") {
3063
- const { listShares } = await import("./agentShare-BokFN5yh.js");
3082
+ const { listShares } = await import("./agentShare-CZX8swiZ.js");
3064
3083
  return Response.json(listShares());
3065
3084
  }
3066
3085
  const revokeM = /^\/api\/share\/([^/]+)$/.exec(p);
3067
3086
  if (req.method === "DELETE" && revokeM) {
3068
- const { revokeShare } = await import("./agentShare-BokFN5yh.js");
3087
+ const { revokeShare } = await import("./agentShare-CZX8swiZ.js");
3069
3088
  const ok = revokeShare(decodeURIComponent(revokeM[1]));
3070
3089
  return new Response(ok ? "revoked" : "no such share", { status: ok ? 200 : 404 });
3071
3090
  }
@@ -3367,7 +3386,7 @@ Options:
3367
3386
  const shutdown = (resolve) => {
3368
3387
  if (heartbeat) clearInterval(heartbeat);
3369
3388
  closeShare?.();
3370
- import("./agentShare-BokFN5yh.js").then((m) => m.revokeAllShares()).catch(() => {});
3389
+ import("./agentShare-CZX8swiZ.js").then((m) => m.revokeAllShares()).catch(() => {});
3371
3390
  server?.stop();
3372
3391
  Promise.resolve(releaseHostLock?.()).catch(() => {}).then(resolve);
3373
3392
  };
@@ -3380,4 +3399,4 @@ Options:
3380
3399
 
3381
3400
  //#endregion
3382
3401
  export { cmdServe, loadTokenReadOnly, mintScopedTermToken, resolveDaemonHttpBase, resolveLocalServeUrl };
3383
- //# sourceMappingURL=serve-CeFGSWkZ.js.map
3402
+ //# sourceMappingURL=serve-DPkqCb2w.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-CeFGSWkZ.js");
35
+ const { cmdServe } = await import("./serve-DPkqCb2w.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-BMZ5CUKG.js.map
45
+ //# sourceMappingURL=setup-BiJ3zr48.js.map
@@ -1267,31 +1267,31 @@ async function runSubcommand(argv) {
1267
1267
  return cmdCh(rest);
1268
1268
  }
1269
1269
  case "term": {
1270
- const { cmdTerm } = await import("./terminal-DmqEOsjR.js");
1270
+ const { cmdTerm } = await import("./terminal-BYQofNj7.js");
1271
1271
  return cmdTerm(rest);
1272
1272
  }
1273
1273
  case "widget": {
1274
- const { cmdWidget } = await import("./widget-CtAC5BHV.js");
1274
+ const { cmdWidget } = await import("./widget-MTX-ryLQ.js");
1275
1275
  return cmdWidget(rest);
1276
1276
  }
1277
1277
  case "mint": {
1278
- const { cmdMint } = await import("./widget-CtAC5BHV.js");
1278
+ const { cmdMint } = await import("./widget-MTX-ryLQ.js");
1279
1279
  return cmdMint(rest);
1280
1280
  }
1281
1281
  case "serve": {
1282
- const { cmdServe } = await import("./serve-CeFGSWkZ.js");
1282
+ const { cmdServe } = await import("./serve-DPkqCb2w.js");
1283
1283
  return cmdServe(rest);
1284
1284
  }
1285
1285
  case "setup": {
1286
- const { cmdSetup } = await import("./setup-BMZ5CUKG.js");
1286
+ const { cmdSetup } = await import("./setup-BiJ3zr48.js");
1287
1287
  return cmdSetup(rest);
1288
1288
  }
1289
1289
  case "ws": {
1290
- const { cmdWs } = await import("./ws-CZEJdPBM.js");
1290
+ const { cmdWs } = await import("./ws-D4iUZSx3.js");
1291
1291
  return cmdWs(rest);
1292
1292
  }
1293
1293
  case "schedule": {
1294
- const { cmdSchedule } = await import("./schedule-D_gxvnkf.js");
1294
+ const { cmdSchedule } = await import("./schedule-QWsOlF5S.js");
1295
1295
  return cmdSchedule(rest);
1296
1296
  }
1297
1297
  case "remote": {
@@ -1303,7 +1303,7 @@ async function runSubcommand(argv) {
1303
1303
  return cmdExpose(rest);
1304
1304
  }
1305
1305
  case "callback": {
1306
- const { cmdCallback } = await import("./callback-CvbnQmfJ.js");
1306
+ const { cmdCallback } = await import("./callback-CGfVdTEu.js");
1307
1307
  return cmdCallback(rest);
1308
1308
  }
1309
1309
  case "reap":
@@ -4342,7 +4342,7 @@ async function cmdNotify(rest) {
4342
4342
  }
4343
4343
  const ensure = async () => {
4344
4344
  if (!argv["ensure-daemon"]) return;
4345
- const { ensureDaemon } = await import("./notifyDaemon-2Wt04RUC.js");
4345
+ const { ensureDaemon } = await import("./notifyDaemon-BtZ9JXNc.js");
4346
4346
  await ensureDaemon().catch(() => null);
4347
4347
  };
4348
4348
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4416,7 +4416,7 @@ async function cmdNotifyCursor(args) {
4416
4416
  }
4417
4417
  async function cmdNotifyd(rest) {
4418
4418
  const sub = rest[0] ?? "status";
4419
- const daemon = await import("./notifyDaemon-2Wt04RUC.js");
4419
+ const daemon = await import("./notifyDaemon-BtZ9JXNc.js");
4420
4420
  switch (sub) {
4421
4421
  case "run": return daemon.runDaemon();
4422
4422
  case "once": return daemon.runDaemon({ once: true });
@@ -4443,4 +4443,4 @@ async function cmdNotifyd(rest) {
4443
4443
 
4444
4444
  //#endregion
4445
4445
  export { shouldStealLock as $, recentReadEdges as A, stdinActivityPath as B, listRecords as C, readNotes as D, readAgentPtysize as E, resolveReadWindow as F, writeToIpc as G, submitAndConfirm as H, resolveResumeArgs as I, hostId as J, appendEvent as K, restartHintLines as L, renderRawLog as M, renderRawLogLines as N, readPtysize as O, resolveOne as P, readInbox as Q, runSubcommand as R, lastStdinAt as S, menuSelectKeys as T, waitForLogQuiet as U, stopTipForCli as V, writeKeysPaced as W, listInboxParents as X, isTransientLockMkdirError as Y, liveWatchers as Z, isPidAlive as _, cmdHelp as a, isUnknownManagerToken as b, deriveLiveState as c, extractMenu as d, daemonLockDir as et, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, renderLogTailLines as j, recentMessageEdges as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, notifyDir as nt, controlCodeFromName as o, extractTaskCounts as p, gcInboxes as q, TYPING_WINDOW_MS as r, TYPING_BADGE as rt, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, daemonLockOwnerPath as tt, extractBadges as u, isSlashCommand as v, matchKeyword as w, isUserTyping as x, isSubcommand as y, snapshotStatus as z };
4446
- //# sourceMappingURL=subcommands-DHzOWlWy.js.map
4446
+ //# sourceMappingURL=subcommands-D33eq_nw.js.map
@@ -3,7 +3,7 @@ import "./globalPidIndex-D9DOSd-c.js";
3
3
  import "./messageLog-C-YIzhRj.js";
4
4
  import "./e2e-CEIdJGZ7.js";
5
5
  import "./configShared-9wVlbPyC.js";
6
- import { A as recentReadEdges, B as stdinActivityPath, C as listRecords, D as readNotes, E as readAgentPtysize, F as resolveReadWindow, G as writeToIpc, H as submitAndConfirm, I as resolveResumeArgs, L as restartHintLines, M as renderRawLog, N as renderRawLogLines, O as readPtysize, P as resolveOne, R as runSubcommand, S as lastStdinAt, T as menuSelectKeys, U as waitForLogQuiet, V as stopTipForCli, W as writeKeysPaced, _ as isPidAlive, a as cmdHelp, b as isUnknownManagerToken, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as renderLogTailLines, k as recentMessageEdges, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as matchKeyword, x as isUserTyping, y as isSubcommand, z as snapshotStatus } from "./subcommands-DHzOWlWy.js";
6
+ import { A as recentReadEdges, B as stdinActivityPath, C as listRecords, D as readNotes, E as readAgentPtysize, F as resolveReadWindow, G as writeToIpc, H as submitAndConfirm, I as resolveResumeArgs, L as restartHintLines, M as renderRawLog, N as renderRawLogLines, O as readPtysize, P as resolveOne, R as runSubcommand, S as lastStdinAt, T as menuSelectKeys, U as waitForLogQuiet, V as stopTipForCli, W as writeKeysPaced, _ as isPidAlive, a as cmdHelp, b as isUnknownManagerToken, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as renderLogTailLines, k as recentMessageEdges, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as matchKeyword, x as isUserTyping, y as isSubcommand, z as snapshotStatus } from "./subcommands-D33eq_nw.js";
7
7
  import "./webrtcLink-BMYneHbu.js";
8
8
  import "./remotes-BxQnNxQv.js";
9
9
 
@@ -98,7 +98,7 @@ async function cmdTermMint(args) {
98
98
  if (flags.ro && flags.interactive) throw new Error("--ro and --interactive are mutually exclusive");
99
99
  const ttlSec = parseTtlSec(typeof flags.ttl === "string" ? flags.ttl : "15m");
100
100
  const canSend = flags.interactive === true;
101
- const { mintScopedTermToken } = await import("./serve-CeFGSWkZ.js");
101
+ const { mintScopedTermToken } = await import("./serve-DPkqCb2w.js");
102
102
  const r = await mintScopedTermToken(pid, {
103
103
  ttlSec,
104
104
  canSend
@@ -135,4 +135,4 @@ async function cmdTerm(args) {
135
135
 
136
136
  //#endregion
137
137
  export { cmdTerm };
138
- //# sourceMappingURL=terminal-DmqEOsjR.js.map
138
+ //# sourceMappingURL=terminal-BYQofNj7.js.map
package/dist/terminal.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as AyTerminal } from "./browser-CNdIibfn.js";
1
+ import { t as AyTerminal } from "./browser-DV0CLEwt.js";
2
2
 
3
3
  export { AyTerminal, AyTerminal as default };
@@ -1,6 +1,6 @@
1
1
  import { n as __esmMin, r as __exportAll } from "./chunk-cZBX9HZv.js";
2
2
  import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-Ba8m9VTm.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-D5YX0jkS.js";
4
4
  import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
5
5
  import { d as openStore, p as LIFECYCLES, x as removeControlCharacters } from "./todoAutomation-Buo457VO.js";
6
6
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DBoONoM-.js";
@@ -2027,4 +2027,4 @@ function sleep(ms) {
2027
2027
 
2028
2028
  //#endregion
2029
2029
  export { hasTodoStore as a, AgentContext as c, answerAsk as i, CLAUDE_SESSION_PIN_ENV as l, agentYes as n, listAsks as o, config as r, listAsksForProject as s, CLIS_CONFIG as t };
2030
- //# sourceMappingURL=ts-DE2iV6rV.js.map
2030
+ //# sourceMappingURL=ts-DWtqCoKd.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.244.2";
10
+ var version = "1.244.3-beta.583.1";
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-Ba8m9VTm.js.map
218
+ //# sourceMappingURL=versionChecker-D5YX0jkS.js.map
@@ -28,7 +28,7 @@ function parseFlags(args, known) {
28
28
  }
29
29
  /** Resolve the local daemon base URL + token (flags override discovery). */
30
30
  async function daemonTarget(flags) {
31
- const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-CeFGSWkZ.js");
31
+ const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-DPkqCb2w.js");
32
32
  const base = typeof flags.base === "string" ? flags.base : await resolveDaemonHttpBase();
33
33
  if (!base) throw new Error("no running ay serve daemon found — start `ay serve` or pass --base <url> (e.g. http://127.0.0.1:PORT)");
34
34
  const token = typeof flags.token === "string" ? flags.token : await loadTokenReadOnly() ?? "";
@@ -186,7 +186,7 @@ async function cmdMint(args) {
186
186
  const bad = caps.filter((c) => !KNOWN_CAPS.has(c));
187
187
  if (bad.length) throw new Error(`unknown cap(s): ${bad.join(", ")} (valid: ${[...KNOWN_CAPS].join(", ")})`);
188
188
  const ttlSec = parseTtlSec(typeof flags.ttl === "string" ? flags.ttl : "15m");
189
- const { mintScopedTermToken } = await import("./serve-CeFGSWkZ.js");
189
+ const { mintScopedTermToken } = await import("./serve-DPkqCb2w.js");
190
190
  const r = await mintScopedTermToken(target, {
191
191
  ttlSec,
192
192
  caps
@@ -202,4 +202,4 @@ async function cmdMint(args) {
202
202
 
203
203
  //#endregion
204
204
  export { cmdMint, cmdWidget };
205
- //# sourceMappingURL=widget-CtAC5BHV.js.map
205
+ //# sourceMappingURL=widget-MTX-ryLQ.js.map
package/dist/widgets.js CHANGED
@@ -2,7 +2,7 @@ import "./e2e-CEIdJGZ7.js";
2
2
  import "./channels-BiGUYTuz.js";
3
3
  import "./peer-B5JKF4BP.js";
4
4
  import { t as AyChannel } from "./browser-B3dOptjL.js";
5
- import { t as AyTerminal } from "./browser-CNdIibfn.js";
5
+ import { t as AyTerminal } from "./browser-DV0CLEwt.js";
6
6
  import { AyWidget } from "./widget.js";
7
7
 
8
8
  export { AyChannel, AyTerminal, AyWidget };
@@ -3,7 +3,7 @@ import "./globalPidIndex-D9DOSd-c.js";
3
3
  import "./messageLog-C-YIzhRj.js";
4
4
  import "./e2e-CEIdJGZ7.js";
5
5
  import "./configShared-9wVlbPyC.js";
6
- import { C as listRecords } from "./subcommands-DHzOWlWy.js";
6
+ import { C as listRecords } from "./subcommands-D33eq_nw.js";
7
7
  import "./webrtcLink-BMYneHbu.js";
8
8
  import "./remotes-BxQnNxQv.js";
9
9
  import { i as getProvisionRoot } from "./workspaceConfig-DOHygA2x.js";
@@ -418,4 +418,4 @@ async function cmdWs(args) {
418
418
 
419
419
  //#endregion
420
420
  export { WS_JSON_SCHEMA, cmdWs, collectWorkspaces, isPathInside, loadProvision, workspaceStatus };
421
- //# sourceMappingURL=ws-CZEJdPBM.js.map
421
+ //# sourceMappingURL=ws-D4iUZSx3.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.244.2",
3
+ "version": "1.244.3-beta.583.1",
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
@@ -3547,13 +3547,25 @@ export async function cmdServe(rest: string[]): Promise<number> {
3547
3547
  }
3548
3548
  }
3549
3549
 
3550
- // POST /api/resize/:keyword body {cols, rows} — drive the agent's PTY size.
3551
- // Mirrors `ay attach`: write ~/.agent-yes/winsize/<pid> then SIGWINCH; the
3552
- // agent's resize listener picks it up and reflows its TUI to that width.
3550
+ // POST /api/resize/:keyword body {cols, rows, force?, viewer?} — size the agent.
3551
+ //
3552
+ // DEFAULT (3d): report {cols,rows} as a size CAP into the negotiation (publishCap
3553
+ // + scheduleNego) — the smallest-client-wins path every other viewer uses — NOT a
3554
+ // raw winsize write. A raw write is last-writer-wins: a secondary view (console
3555
+ // tab, widget) sizing the shared PTY to its own window clobbered the agent's
3556
+ // terminal and every other viewer. As a cap, this caller's size is just one input
3557
+ // to the min; a one-shot cap fades on TTL if not renewed (a live viewer renews via
3558
+ // its presence heartbeat, so its size stays without any raw push).
3559
+ //
3560
+ // FORCE escape hatch — TRIPLE-LOCKED: master token AND body.force===true, traced
3561
+ // src=api-resize-FORCED. The operator's diagnostic override for when negotiation
3562
+ // misbehaves (kept deliberately usable). A scoped token can NEVER force — force:true
3563
+ // from a scoped token silently falls through to a cap report (no raw write, no
3564
+ // privilege leak).
3553
3565
  const resizeM = /^\/api\/resize\/(.+)$/.exec(p);
3554
3566
  if (req.method === "POST" && resizeM) {
3555
3567
  const keyword = decodeURIComponent(resizeM[1]!);
3556
- let body: { cols?: number; rows?: number };
3568
+ let body: { cols?: number; rows?: number; force?: boolean; viewer?: string };
3557
3569
  try {
3558
3570
  body = (await req.json()) as typeof body;
3559
3571
  } catch {
@@ -3564,28 +3576,37 @@ export async function cmdServe(rest: string[]): Promise<number> {
3564
3576
  if (!cols || !rows) return new Response("missing cols/rows", { status: 400 });
3565
3577
  try {
3566
3578
  const record = await resolveOne(keyword, defaultOpts());
3567
- // Trace the SOURCE of every PTY resize: a secondary/observer view (a console
3568
- // tab, a widget) that force-resizes the shared PTY to its own window can
3569
- // clobber the agent's terminal (last-writer-wins), and without this it's
3570
- // impossible to tell who did it. Logs auth kind + origin/referer/UA.
3579
+ const originTrace =
3580
+ `auth=${authResult.kind} origin=${req.headers.get("origin") ?? "-"} ` +
3581
+ `ref=${req.headers.get("referer") ?? "-"} ` +
3582
+ `ua=${(req.headers.get("user-agent") ?? "-").slice(0, 80)}`;
3583
+ // FORCE: master + force:true → raw winsize write that BYPASSES negotiation.
3584
+ if (body.force === true && authResult.kind === "master") {
3585
+ process.stderr.write(
3586
+ `[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize-FORCED ${originTrace}\n`,
3587
+ );
3588
+ const file = winsizePathFor(record.pid);
3589
+ await mkdir(path.dirname(file), { recursive: true });
3590
+ await writeFile(file, `${cols} ${rows} ${Date.now()}\n`);
3591
+ try {
3592
+ process.kill(record.pid, "SIGWINCH");
3593
+ } catch {
3594
+ /* agent gone */
3595
+ }
3596
+ return Response.json({ ok: true, pid: record.pid, cols, rows, forced: true });
3597
+ }
3598
+ // DEFAULT: report a cap into the negotiation (no raw clobber).
3599
+ const cap = sanitizeCap({ cols, rows });
3600
+ if (!cap) return new Response("missing cols/rows", { status: 400 });
3601
+ const viewer =
3602
+ (body.viewer ? String(body.viewer).slice(0, 64) : "") ||
3603
+ (authResult.kind === "scoped" ? `api:${authResult.scope.pid}` : "api-resize");
3571
3604
  process.stderr.write(
3572
- `[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize auth=${authResult.kind} ` +
3573
- `origin=${req.headers.get("origin") ?? "-"} ref=${req.headers.get("referer") ?? "-"} ` +
3574
- `ua=${(req.headers.get("user-agent") ?? "-").slice(0, 80)}\n`,
3575
- );
3576
- const ayHome = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
3577
- const winsizeDir = path.join(ayHome, "winsize");
3578
- await mkdir(winsizeDir, { recursive: true });
3579
- await writeFile(
3580
- path.join(winsizeDir, String(record.pid)),
3581
- `${cols} ${rows} ${Date.now()}\n`,
3605
+ `[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize-cap viewer=${viewer} ${originTrace}\n`,
3582
3606
  );
3583
- try {
3584
- process.kill(record.pid, "SIGWINCH");
3585
- } catch {
3586
- /* agent gone */
3587
- }
3588
- return Response.json({ ok: true, pid: record.pid, cols, rows });
3607
+ await publishCap(record.pid, viewer, cap, "viewer");
3608
+ scheduleNego(record.pid);
3609
+ return Response.json({ ok: true, pid: record.pid, cols, rows, mode: "cap" });
3589
3610
  } catch (e) {
3590
3611
  return new Response((e as Error).message, { status: 404 });
3591
3612
  }
@@ -61,6 +61,7 @@ export class AyTerminal {
61
61
  private capTimer: any = null; // debounce for cap reports on drag-resize
62
62
  private capHeartbeat: any = null; // keeps the cap renewed while the panel is open
63
63
  private lastCap: { cols: number; rows: number } | null = null;
64
+ private panelEl?: any; // the #panel element — the thing that goes display:none on collapse
64
65
 
65
66
  constructor(info: string | number | AyTerminalInfo) {
66
67
  const o: AyTerminalInfo =
@@ -135,10 +136,24 @@ export class AyTerminal {
135
136
  }, 5000);
136
137
  }
137
138
 
138
- /** True while this widget's terminal grid is actually rendered (panel open & visible). */
139
+ /**
140
+ * True while this widget's panel is actually on-screen. Checks the PANEL element
141
+ * itself — its computed `display` and bounding rect — NOT the xterm grid: xterm
142
+ * keeps a non-zero `cols/rows` (and its `.xterm` node a cached box) even when an
143
+ * ancestor is `display:none`, so measuring the grid falsely reads "visible" while
144
+ * collapsed (grocy's ③). The panel's own `display:none`/0×0 rect is unambiguous.
145
+ */
139
146
  private isPanelVisible(): boolean {
140
- const el = this.term?.element as any; // the .xterm root
141
- return !!(el && el.offsetWidth && el.offsetHeight);
147
+ const p = this.panelEl;
148
+ if (!p) return false;
149
+ const g = globalThis as any;
150
+ try {
151
+ if (g.getComputedStyle?.(p)?.display === "none") return false;
152
+ } catch {
153
+ /* no getComputedStyle — fall through to the rect check */
154
+ }
155
+ const r = p.getBoundingClientRect?.();
156
+ return !!(r && r.width > 0 && r.height > 0);
142
157
  }
143
158
 
144
159
  /** Withdraw this widget's size cap (closed/minimized) → the daemon re-negotiates without it. */
@@ -294,6 +309,7 @@ export class AyTerminal {
294
309
  const wrap = root.getElementById("wrap")!;
295
310
  const titleEl = root.getElementById("title")!;
296
311
  this.badgeEl = root.getElementById("badge");
312
+ this.panelEl = panel; // used by isPanelVisible() to detect collapse
297
313
  if (this.transparent) panel.classList.add("transparent");
298
314
 
299
315
  // Reflow: read-only CSS-scales the native grid to fit; interactive fits the
@@ -305,13 +321,18 @@ export class AyTerminal {
305
321
  // agent's own/other viewers' TUI (taku's "xterm looks weird"). Interactive only
306
322
  // adds keystroke input (start(), /api/send), not a resize.
307
323
  const reflow = () => {
324
+ // Collapsed/hidden interactive panel → withdraw our cap and DON'T re-arm the
325
+ // heartbeat. Gate on the PANEL's own visibility (isPanelVisible), because the
326
+ // wrap ResizeObserver does NOT reliably fire when an ancestor goes display:none,
327
+ // and the xterm grid stays non-zero while hidden — so neither a grid measure nor
328
+ // the ResizeObserver alone catches a collapse (grocy's ③). Guard on an active cap
329
+ // so transient 0-size layout ticks on mount don't spam the daemon.
330
+ if (!this.readOnly && !this.isPanelVisible()) {
331
+ if (this.capHeartbeat || this.lastCap) this.withdrawCap();
332
+ return;
333
+ }
308
334
  const xt = inner.querySelector(".xterm") as any;
309
335
  if (!xt || !xt.offsetWidth || !xt.offsetHeight) {
310
- // The panel just went invisible (minimized/collapsed/hidden — the wrap
311
- // ResizeObserver fires this on collapse). Withdraw our cap so a hidden panel
312
- // stops constraining the shared PTY, no matter WHICH gesture hid it (the wired
313
- // min button, a page hiding the host, a max/min quirk). Guard on an active cap
314
- // so transient 0-size layout ticks on mount don't spam the daemon.
315
336
  if (!this.readOnly && (this.capHeartbeat || this.lastCap)) this.withdrawCap();
316
337
  return;
317
338
  }
@@ -351,6 +372,16 @@ export class AyTerminal {
351
372
  } catch {
352
373
  /* no ResizeObserver — fall back to the window listener below */
353
374
  }
375
+ // Reflow on VISIBILITY changes too. A ResizeObserver does NOT reliably fire when
376
+ // an element (or an ancestor — e.g. a page hiding our host) goes display:none, so
377
+ // a collapse can slip past it and leave the cap pinned (grocy's ③). An
378
+ // IntersectionObserver DOES fire when the panel leaves/enters the viewport or is
379
+ // hidden — that reflow then sees isPanelVisible() false and withdraws the cap.
380
+ try {
381
+ new g.IntersectionObserver(() => reflow()).observe(panel);
382
+ } catch {
383
+ /* no IntersectionObserver — the 5s heartbeat self-check still catches it */
384
+ }
354
385
  g.addEventListener?.("resize", reflow);
355
386
 
356
387
  void (async () => {