agent-yes 1.240.0 → 1.241.0-beta.577.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-BeOtNG6j.js";
1
+ import { t as CLIS_CONFIG } from "./ts-DliG4u7c.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-BihsAaFl.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-BJmQhrwr.js.map
@@ -1,11 +1,11 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import "./versionChecker-TPByzzJr.js";
3
- import "./ts-BeOtNG6j.js";
2
+ import "./versionChecker-3rLRupyR.js";
3
+ import "./ts-DliG4u7c.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-BihsAaFl.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BJmQhrwr.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-Czk07vLL.js";
6
+ import { P as resolveOne } from "./subcommands-Df0YzGWI.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-CjkZTTMx.js.map
232
+ //# sourceMappingURL=agentShare-BmCb5uvm.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-Czk07vLL.js";
6
+ import "./subcommands-Df0YzGWI.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-DH_-AZ4s.js";
10
+ import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-BeaM1WiV.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-Czk07vLL.js";
2
+ import { P as resolveOne } from "./subcommands-Df0YzGWI.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-Dpulvnp4.js");
75
+ const { resolveLocalServeUrl } = await import("./serve-cAgCoODf.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-DH_-AZ4s.js.map
190
+ //# sourceMappingURL=callback-BeaM1WiV.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-TPByzzJr.js";
5
- import { n as getRustBinary } from "./rustBinary-7V1Q4QRb.js";
4
+ import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-3rLRupyR.js";
5
+ import { n as getRustBinary } from "./rustBinary-DLFgyJEK.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-B5P-Bghi.js");
288
+ const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken } = await import("./subcommands-B01Mdojh.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-DE9GVUHq.js");
298
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C0ov3LoL.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-DE9GVUHq.js");
349
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C0ov3LoL.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-TPByzzJr.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-BeOtNG6j.js";
2
+ import "./versionChecker-3rLRupyR.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-DliG4u7c.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-Czk07vLL.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-Df0YzGWI.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-De_rjZaz.js.map
592
+ //# sourceMappingURL=notifyDaemon-DA1oJDeX.js.map
@@ -1,4 +1,4 @@
1
- import { r as getInstalledPackage } from "./versionChecker-TPByzzJr.js";
1
+ import { r as getInstalledPackage } from "./versionChecker-3rLRupyR.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-7V1Q4QRb.js.map
228
+ //# sourceMappingURL=rustBinary-DLFgyJEK.js.map
@@ -1,12 +1,12 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import "./versionChecker-TPByzzJr.js";
3
- import "./ts-BeOtNG6j.js";
2
+ import "./versionChecker-3rLRupyR.js";
3
+ import "./ts-DliG4u7c.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-BihsAaFl.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BJmQhrwr.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-DEA6itY2.js.map
156
+ //# sourceMappingURL=schedule-BTlOIQK0.js.map
@@ -1,7 +1,7 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
- import { r as getInstalledPackage } from "./versionChecker-TPByzzJr.js";
3
- import { t as findSpawnHiddenLauncher } from "./rustBinary-7V1Q4QRb.js";
4
- import { i as answerAsk, l as CLAUDE_SESSION_PIN_ENV, o as listAsks } from "./ts-BeOtNG6j.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-3rLRupyR.js";
3
+ import { t as findSpawnHiddenLauncher } from "./rustBinary-DLFgyJEK.js";
4
+ import { i as answerAsk, l as CLAUDE_SESSION_PIN_ENV, o as listAsks } from "./ts-DliG4u7c.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,17 +11,17 @@ 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-BihsAaFl.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-Czk07vLL.js";
14
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BJmQhrwr.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-Df0YzGWI.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-DH_-AZ4s.js";
19
+ import { n as isCallbackRevoked, r as loadCallbackSecretReadOnly } from "./callback-BeaM1WiV.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";
23
23
  import yargs from "yargs";
24
- import { appendFile, mkdir, open, readFile, rename, rm, stat, unlink, writeFile } from "fs/promises";
24
+ import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, unlink, writeFile } from "fs/promises";
25
25
  import { arch, cpus, freemem, homedir, hostname, loadavg, platform, totalmem, uptime, userInfo } from "os";
26
26
  import path from "path";
27
27
  import { execFileSync } from "node:child_process";
@@ -1720,6 +1720,46 @@ Options:
1720
1720
  };
1721
1721
  const presence = /* @__PURE__ */ new Map();
1722
1722
  const PRESENCE_TTL_MS = 12e3;
1723
+ const CAP_TTL_MS = 12e3;
1724
+ const daemonTag = `d${process.pid}`;
1725
+ const capsDir = (pid) => path.join(agentYesHome(), "caps", String(pid));
1726
+ const capFile = (pid, key) => path.join(capsDir(pid), key.replace(/[^\w.:-]/g, "_"));
1727
+ const publishCap = async (pid, viewer, cap, role) => {
1728
+ try {
1729
+ await mkdir(capsDir(pid), { recursive: true });
1730
+ await writeFile(capFile(pid, `${daemonTag}:${viewer}`), `${cap.cols} ${cap.rows} ${Date.now()} ${role}\n`);
1731
+ } catch {}
1732
+ };
1733
+ const withdrawCap = async (pid, viewer) => {
1734
+ await unlink(capFile(pid, `${daemonTag}:${viewer}`)).catch(() => {});
1735
+ };
1736
+ const readSharedCaps = async (pid) => {
1737
+ let files;
1738
+ try {
1739
+ files = await readdir(capsDir(pid));
1740
+ } catch {
1741
+ return [];
1742
+ }
1743
+ const now = Date.now();
1744
+ const caps = [];
1745
+ for (const f of files) {
1746
+ const fp = path.join(capsDir(pid), f);
1747
+ try {
1748
+ const parts = (await readFile(fp, "utf-8")).trim().split(/\s+/);
1749
+ const ts = Number(parts[2]);
1750
+ if (!Number.isFinite(ts) || now - ts > CAP_TTL_MS) {
1751
+ await unlink(fp).catch(() => {});
1752
+ continue;
1753
+ }
1754
+ const cap = sanitizeCap({
1755
+ cols: Number(parts[0]),
1756
+ rows: Number(parts[1])
1757
+ });
1758
+ if (cap) caps.push(cap);
1759
+ } catch {}
1760
+ }
1761
+ return caps;
1762
+ };
1723
1763
  const negoApplied = /* @__PURE__ */ new Map();
1724
1764
  const negoTimers = /* @__PURE__ */ new Map();
1725
1765
  const negoCapsGoneAt = /* @__PURE__ */ new Map();
@@ -1740,22 +1780,29 @@ Options:
1740
1780
  }
1741
1781
  };
1742
1782
  const winsizePathFor = (pid) => path.join(process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes"), "winsize", String(pid));
1743
- const liveCapsFor = (pid) => {
1744
- const now = Date.now();
1745
- const caps = [];
1746
- for (const v of presence.values()) if (now - v.ts <= PRESENCE_TTL_MS && String(v.agent) === String(pid) && v.cap) caps.push(v.cap);
1747
- return caps;
1748
- };
1749
1783
  const applyNego = async (pid) => {
1750
1784
  negoTimers.delete(pid);
1751
- const eff = negotiateSize(liveCapsFor(pid));
1785
+ const caps = await readSharedCaps(pid);
1786
+ const eff = negotiateSize(caps);
1752
1787
  const file = winsizePathFor(pid);
1753
1788
  const prev = negoApplied.get(pid);
1754
1789
  try {
1755
1790
  if (eff) {
1756
1791
  negoCapsGoneAt.delete(pid);
1757
1792
  if (prev && Math.abs(prev.cols - eff.cols) <= 1 && Math.abs(prev.rows - eff.rows) <= 1) return;
1793
+ try {
1794
+ const curr = (await readFile(file, "utf-8")).trim().split(/\s+/);
1795
+ if (Number(curr[0]) === eff.cols && Number(curr[1]) === eff.rows) {
1796
+ negoApplied.set(pid, {
1797
+ cols: eff.cols,
1798
+ rows: eff.rows,
1799
+ content: ""
1800
+ });
1801
+ return;
1802
+ }
1803
+ } catch {}
1758
1804
  const content = `${eff.cols} ${eff.rows} ${Date.now()}\n`;
1805
+ process.stderr.write(`[api/resize] pid=${pid} ${eff.cols}x${eff.rows} src=presence-nego daemon=${daemonTag} caps=${caps.length}\n`);
1759
1806
  await mkdir(path.dirname(file), { recursive: true });
1760
1807
  await writeFile(file, content);
1761
1808
  negoApplied.set(pid, {
@@ -2246,7 +2293,7 @@ Options:
2246
2293
  }
2247
2294
  });
2248
2295
  if (req.method === "GET" && p === "/api/ws") {
2249
- const ws = await import("./ws-C6kc9L4J.js");
2296
+ const ws = await import("./ws-BRhR6R4r.js");
2250
2297
  try {
2251
2298
  await ws.loadProvision();
2252
2299
  } catch (e) {
@@ -2266,7 +2313,7 @@ Options:
2266
2313
  if (req.method === "GET" && p === "/api/ws/status") {
2267
2314
  const dirRaw = url.searchParams.get("path");
2268
2315
  if (!dirRaw) return new Response("missing ?path=<workspace dir>", { status: 400 });
2269
- const ws = await import("./ws-C6kc9L4J.js");
2316
+ const ws = await import("./ws-BRhR6R4r.js");
2270
2317
  let prov;
2271
2318
  try {
2272
2319
  prov = await ws.loadProvision();
@@ -2721,7 +2768,7 @@ Options:
2721
2768
  if (!cols || !rows) return new Response("missing cols/rows", { status: 400 });
2722
2769
  try {
2723
2770
  const record = await resolveOne(keyword, defaultOpts());
2724
- process.stderr.write(`[api/resize] pid=${record.pid} ${cols}x${rows} auth=${authResult.kind} origin=${req.headers.get("origin") ?? "-"} ref=${req.headers.get("referer") ?? "-"} ua=${(req.headers.get("user-agent") ?? "-").slice(0, 80)}\n`);
2771
+ 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`);
2725
2772
  const ayHome = process.env.AGENT_YES_HOME ?? path.join(homedir(), ".agent-yes");
2726
2773
  const winsizeDir = path.join(ayHome, "winsize");
2727
2774
  await mkdir(winsizeDir, { recursive: true });
@@ -2777,6 +2824,7 @@ Options:
2777
2824
  const viewer = String(b.viewer ?? "").slice(0, 64);
2778
2825
  if (!viewer) return new Response("missing viewer", { status: 400 });
2779
2826
  const prevAgent = presence.get(viewer)?.agent;
2827
+ const cap = sanitizeCap(b.cap);
2780
2828
  if (b.agent == null) presence.delete(viewer);
2781
2829
  else presence.set(viewer, {
2782
2830
  viewer,
@@ -2784,10 +2832,15 @@ Options:
2784
2832
  cols: Math.max(0, Math.floor(Number(b.cols) || 0)),
2785
2833
  rows: Math.max(0, Math.floor(Number(b.rows) || 0)),
2786
2834
  sel: typeof b.sel === "string" ? b.sel.slice(0, 200) : null,
2787
- cap: sanitizeCap(b.cap),
2835
+ cap,
2788
2836
  ts: Date.now()
2789
2837
  });
2790
- if (b.agent != null) scheduleNego(Number(b.agent));
2838
+ if (prevAgent != null && String(prevAgent) !== String(b.agent ?? "")) await withdrawCap(Number(prevAgent), viewer);
2839
+ if (b.agent != null) {
2840
+ if (cap) await publishCap(Number(b.agent), viewer, cap, "viewer");
2841
+ else await withdrawCap(Number(b.agent), viewer);
2842
+ scheduleNego(Number(b.agent));
2843
+ }
2791
2844
  if (prevAgent != null && String(prevAgent) !== String(b.agent ?? "")) scheduleNego(Number(prevAgent));
2792
2845
  return new Response(null, { status: 204 });
2793
2846
  }
@@ -2990,7 +3043,7 @@ Options:
2990
3043
  const perm = body.perm ?? "r";
2991
3044
  if (perm !== "r" && perm !== "rw") return new Response(`invalid perm ${perm} (want r or rw)`, { status: 400 });
2992
3045
  try {
2993
- const { createScopedShare } = await import("./agentShare-CjkZTTMx.js");
3046
+ const { createScopedShare } = await import("./agentShare-BmCb5uvm.js");
2994
3047
  const share = await createScopedShare({
2995
3048
  agent: body.agent,
2996
3049
  perm,
@@ -3005,12 +3058,12 @@ Options:
3005
3058
  }
3006
3059
  }
3007
3060
  if (req.method === "GET" && p === "/api/shares") {
3008
- const { listShares } = await import("./agentShare-CjkZTTMx.js");
3061
+ const { listShares } = await import("./agentShare-BmCb5uvm.js");
3009
3062
  return Response.json(listShares());
3010
3063
  }
3011
3064
  const revokeM = /^\/api\/share\/([^/]+)$/.exec(p);
3012
3065
  if (req.method === "DELETE" && revokeM) {
3013
- const { revokeShare } = await import("./agentShare-CjkZTTMx.js");
3066
+ const { revokeShare } = await import("./agentShare-BmCb5uvm.js");
3014
3067
  const ok = revokeShare(decodeURIComponent(revokeM[1]));
3015
3068
  return new Response(ok ? "revoked" : "no such share", { status: ok ? 200 : 404 });
3016
3069
  }
@@ -3312,7 +3365,7 @@ Options:
3312
3365
  const shutdown = (resolve) => {
3313
3366
  if (heartbeat) clearInterval(heartbeat);
3314
3367
  closeShare?.();
3315
- import("./agentShare-CjkZTTMx.js").then((m) => m.revokeAllShares()).catch(() => {});
3368
+ import("./agentShare-BmCb5uvm.js").then((m) => m.revokeAllShares()).catch(() => {});
3316
3369
  server?.stop();
3317
3370
  Promise.resolve(releaseHostLock?.()).catch(() => {}).then(resolve);
3318
3371
  };
@@ -3325,4 +3378,4 @@ Options:
3325
3378
 
3326
3379
  //#endregion
3327
3380
  export { cmdServe, loadTokenReadOnly, mintScopedTermToken, resolveDaemonHttpBase, resolveLocalServeUrl };
3328
- //# sourceMappingURL=serve-Dpulvnp4.js.map
3381
+ //# sourceMappingURL=serve-cAgCoODf.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-Dpulvnp4.js");
35
+ const { cmdServe } = await import("./serve-cAgCoODf.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-BgDTXYyw.js.map
45
+ //# sourceMappingURL=setup-DI1whrkx.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-Czk07vLL.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-Df0YzGWI.js";
7
7
  import "./webrtcLink-BMYneHbu.js";
8
8
  import "./remotes-BxQnNxQv.js";
9
9
 
@@ -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-SGR6J9Oy.js");
1270
+ const { cmdTerm } = await import("./terminal-D6UWoddY.js");
1271
1271
  return cmdTerm(rest);
1272
1272
  }
1273
1273
  case "widget": {
1274
- const { cmdWidget } = await import("./widget-t8yNtHu7.js");
1274
+ const { cmdWidget } = await import("./widget-CT5jasig.js");
1275
1275
  return cmdWidget(rest);
1276
1276
  }
1277
1277
  case "mint": {
1278
- const { cmdMint } = await import("./widget-t8yNtHu7.js");
1278
+ const { cmdMint } = await import("./widget-CT5jasig.js");
1279
1279
  return cmdMint(rest);
1280
1280
  }
1281
1281
  case "serve": {
1282
- const { cmdServe } = await import("./serve-Dpulvnp4.js");
1282
+ const { cmdServe } = await import("./serve-cAgCoODf.js");
1283
1283
  return cmdServe(rest);
1284
1284
  }
1285
1285
  case "setup": {
1286
- const { cmdSetup } = await import("./setup-BgDTXYyw.js");
1286
+ const { cmdSetup } = await import("./setup-DI1whrkx.js");
1287
1287
  return cmdSetup(rest);
1288
1288
  }
1289
1289
  case "ws": {
1290
- const { cmdWs } = await import("./ws-C6kc9L4J.js");
1290
+ const { cmdWs } = await import("./ws-BRhR6R4r.js");
1291
1291
  return cmdWs(rest);
1292
1292
  }
1293
1293
  case "schedule": {
1294
- const { cmdSchedule } = await import("./schedule-DEA6itY2.js");
1294
+ const { cmdSchedule } = await import("./schedule-BTlOIQK0.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-DNKDn40d.js");
1306
+ const { cmdCallback } = await import("./callback-BDNEYwYa.js");
1307
1307
  return cmdCallback(rest);
1308
1308
  }
1309
1309
  case "reap":
@@ -3711,6 +3711,7 @@ async function cmdAttach(rest) {
3711
3711
  const rows = process.stdout.rows ?? 24;
3712
3712
  try {
3713
3713
  await writeFile(winsizePath, `${cols} ${rows} ${Date.now()}\n`);
3714
+ process.stderr.write(`[api/resize] pid=${record.pid} ${cols}x${rows} src=ay-attach\n`);
3714
3715
  try {
3715
3716
  process.kill(record.pid, "SIGWINCH");
3716
3717
  } catch {}
@@ -4341,7 +4342,7 @@ async function cmdNotify(rest) {
4341
4342
  }
4342
4343
  const ensure = async () => {
4343
4344
  if (!argv["ensure-daemon"]) return;
4344
- const { ensureDaemon } = await import("./notifyDaemon-De_rjZaz.js");
4345
+ const { ensureDaemon } = await import("./notifyDaemon-DA1oJDeX.js");
4345
4346
  await ensureDaemon().catch(() => null);
4346
4347
  };
4347
4348
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4415,7 +4416,7 @@ async function cmdNotifyCursor(args) {
4415
4416
  }
4416
4417
  async function cmdNotifyd(rest) {
4417
4418
  const sub = rest[0] ?? "status";
4418
- const daemon = await import("./notifyDaemon-De_rjZaz.js");
4419
+ const daemon = await import("./notifyDaemon-DA1oJDeX.js");
4419
4420
  switch (sub) {
4420
4421
  case "run": return daemon.runDaemon();
4421
4422
  case "once": return daemon.runDaemon({ once: true });
@@ -4442,4 +4443,4 @@ async function cmdNotifyd(rest) {
4442
4443
 
4443
4444
  //#endregion
4444
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 };
4445
- //# sourceMappingURL=subcommands-Czk07vLL.js.map
4446
+ //# sourceMappingURL=subcommands-Df0YzGWI.js.map
@@ -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-Dpulvnp4.js");
101
+ const { mintScopedTermToken } = await import("./serve-cAgCoODf.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-SGR6J9Oy.js.map
138
+ //# sourceMappingURL=terminal-D6UWoddY.js.map
@@ -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-TPByzzJr.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-3rLRupyR.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-BeOtNG6j.js.map
2030
+ //# sourceMappingURL=ts-DliG4u7c.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.240.0";
10
+ var version = "1.241.0-beta.577.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-TPByzzJr.js.map
218
+ //# sourceMappingURL=versionChecker-3rLRupyR.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-Dpulvnp4.js");
31
+ const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-cAgCoODf.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-Dpulvnp4.js");
189
+ const { mintScopedTermToken } = await import("./serve-cAgCoODf.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-t8yNtHu7.js.map
205
+ //# sourceMappingURL=widget-CT5jasig.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 { C as listRecords } from "./subcommands-Czk07vLL.js";
6
+ import { C as listRecords } from "./subcommands-Df0YzGWI.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-C6kc9L4J.js.map
421
+ //# sourceMappingURL=ws-BRhR6R4r.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.240.0",
3
+ "version": "1.241.0-beta.577.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
@@ -1,4 +1,4 @@
1
- import { appendFile, mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
1
+ import { appendFile, mkdir, open, readdir, readFile, stat, unlink, writeFile } from "fs/promises";
2
2
  import { existsSync, renameSync, watch, writeFileSync } from "node:fs";
3
3
  import { execFileSync } from "node:child_process";
4
4
  import { fileURLToPath } from "node:url";
@@ -2092,6 +2092,63 @@ export async function cmdServe(rest: string[]): Promise<number> {
2092
2092
  >();
2093
2093
  const PRESENCE_TTL_MS = 12_000;
2094
2094
 
2095
+ // ── Shared cap store (3a: multi-daemon size negotiation) ────────────────────
2096
+ // Presence is in-memory PER DAEMON, so multiple `ay serve` processes (the HTTP
2097
+ // daemon + each `--webrtc share` daemon) used to negotiate independently and
2098
+ // fight over the single winsize file (cross-process last-writer-wins). Instead
2099
+ // every daemon publishes each viewer's size cap to a SHARED on-disk store —
2100
+ // ~/.agent-yes/caps/<pid>/<daemon>:<viewer> — and negotiation reads the WHOLE
2101
+ // store (all daemons' viewers + the local-tty cap, 3b). Deterministic min +
2102
+ // read-before-write means every daemon converges on the same winsize instead of
2103
+ // clobbering each other. Lock-free: one writer per file (a daemon owns its own
2104
+ // viewers' entries), short TTL prunes tabs that vanished without a clean clear.
2105
+ const CAP_TTL_MS = 12_000;
2106
+ const daemonTag = `d${process.pid}`; // unique per daemon process
2107
+ const capsDir = (pid: number) => path.join(agentYesHome(), "caps", String(pid));
2108
+ const capFile = (pid: number, key: string) =>
2109
+ path.join(capsDir(pid), key.replace(/[^\w.:-]/g, "_"));
2110
+ const publishCap = async (pid: number, viewer: string, cap: SizeCap, role: string) => {
2111
+ try {
2112
+ await mkdir(capsDir(pid), { recursive: true });
2113
+ await writeFile(
2114
+ capFile(pid, `${daemonTag}:${viewer}`),
2115
+ `${cap.cols} ${cap.rows} ${Date.now()} ${role}\n`,
2116
+ );
2117
+ } catch {
2118
+ /* best-effort */
2119
+ }
2120
+ };
2121
+ const withdrawCap = async (pid: number, viewer: string) => {
2122
+ await unlink(capFile(pid, `${daemonTag}:${viewer}`)).catch(() => {});
2123
+ };
2124
+ // Read every live cap for a pid across ALL daemons; prune expired files.
2125
+ const readSharedCaps = async (pid: number): Promise<SizeCap[]> => {
2126
+ let files: string[];
2127
+ try {
2128
+ files = await readdir(capsDir(pid));
2129
+ } catch {
2130
+ return [];
2131
+ }
2132
+ const now = Date.now();
2133
+ const caps: SizeCap[] = [];
2134
+ for (const f of files) {
2135
+ const fp = path.join(capsDir(pid), f);
2136
+ try {
2137
+ const parts = (await readFile(fp, "utf-8")).trim().split(/\s+/);
2138
+ const ts = Number(parts[2]);
2139
+ if (!Number.isFinite(ts) || now - ts > CAP_TTL_MS) {
2140
+ await unlink(fp).catch(() => {}); // stale (its writer vanished) — prune
2141
+ continue;
2142
+ }
2143
+ const cap = sanitizeCap({ cols: Number(parts[0]), rows: Number(parts[1]) });
2144
+ if (cap) caps.push(cap);
2145
+ } catch {
2146
+ /* unreadable/racing — skip */
2147
+ }
2148
+ }
2149
+ return caps;
2150
+ };
2151
+
2095
2152
  // ── PTY size negotiation — tmux's "smallest client wins" ──────────────────
2096
2153
  // Writable viewers report their readable capacity (`cap`) alongside their
2097
2154
  // presence; we resize the agent's PTY to the elementwise min across live caps
@@ -2137,17 +2194,12 @@ export async function cmdServe(rest: string[]): Promise<number> {
2137
2194
  "winsize",
2138
2195
  String(pid),
2139
2196
  );
2140
- const liveCapsFor = (pid: number): SizeCap[] => {
2141
- const now = Date.now();
2142
- const caps: SizeCap[] = [];
2143
- for (const v of presence.values())
2144
- if (now - v.ts <= PRESENCE_TTL_MS && String(v.agent) === String(pid) && v.cap)
2145
- caps.push(v.cap);
2146
- return caps;
2147
- };
2148
2197
  const applyNego = async (pid: number) => {
2149
2198
  negoTimers.delete(pid);
2150
- const eff = negotiateSize(liveCapsFor(pid));
2199
+ // Negotiate over the SHARED cap store — every daemon's viewers (+ the local
2200
+ // tty cap, 3b) — so all daemons compute the same min and converge.
2201
+ const caps = await readSharedCaps(pid);
2202
+ const eff = negotiateSize(caps);
2151
2203
  const file = winsizePathFor(pid);
2152
2204
  const prev = negoApplied.get(pid);
2153
2205
  try {
@@ -2158,7 +2210,26 @@ export async function cmdServe(rest: string[]): Promise<number> {
2158
2210
  // SIGWINCH reflow on every re-visit is exactly the churn users notice.
2159
2211
  if (prev && Math.abs(prev.cols - eff.cols) <= 1 && Math.abs(prev.rows - eff.rows) <= 1)
2160
2212
  return;
2213
+ // Cross-daemon convergence: if the winsize file ALREADY holds this size (a
2214
+ // sibling daemon negotiated the same min), do NOT rewrite/re-SIGWINCH — else
2215
+ // two daemons thrash the PTY with same-size, different-timestamp writes.
2216
+ try {
2217
+ const curr = (await readFile(file, "utf-8")).trim().split(/\s+/);
2218
+ if (Number(curr[0]) === eff.cols && Number(curr[1]) === eff.rows) {
2219
+ negoApplied.set(pid, { cols: eff.cols, rows: eff.rows, content: "" });
2220
+ return;
2221
+ }
2222
+ } catch {
2223
+ /* no winsize file yet — fall through and write it */
2224
+ }
2161
2225
  const content = `${eff.cols} ${eff.rows} ${Date.now()}\n`;
2226
+ // Trace the negotiated resize + this daemon's identity (multi-daemon topology:
2227
+ // the HTTP daemon and each --webrtc share daemon each negotiate off the shared
2228
+ // store; the trace shows which one wrote, so a resize-fight is diagnosable).
2229
+ process.stderr.write(
2230
+ `[api/resize] pid=${pid} ${eff.cols}x${eff.rows} src=presence-nego ` +
2231
+ `daemon=${daemonTag} caps=${caps.length}\n`,
2232
+ );
2162
2233
  await mkdir(path.dirname(file), { recursive: true });
2163
2234
  await writeFile(file, content);
2164
2235
  negoApplied.set(pid, { ...eff, content });
@@ -3488,7 +3559,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
3488
3559
  // clobber the agent's terminal (last-writer-wins), and without this it's
3489
3560
  // impossible to tell who did it. Logs auth kind + origin/referer/UA.
3490
3561
  process.stderr.write(
3491
- `[api/resize] pid=${record.pid} ${cols}x${rows} auth=${authResult.kind} ` +
3562
+ `[api/resize] pid=${record.pid} ${cols}x${rows} src=api-resize auth=${authResult.kind} ` +
3492
3563
  `origin=${req.headers.get("origin") ?? "-"} ref=${req.headers.get("referer") ?? "-"} ` +
3493
3564
  `ua=${(req.headers.get("user-agent") ?? "-").slice(0, 80)}\n`,
3494
3565
  );
@@ -3572,6 +3643,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
3572
3643
  // The agent this viewer WAS on — if the entry moves (agent switch) or
3573
3644
  // clears, that agent's negotiated size must be recomputed (grow-back).
3574
3645
  const prevAgent = presence.get(viewer)?.agent;
3646
+ const cap = sanitizeCap(b.cap);
3575
3647
  if (b.agent == null) presence.delete(viewer);
3576
3648
  else
3577
3649
  presence.set(viewer, {
@@ -3580,10 +3652,19 @@ export async function cmdServe(rest: string[]): Promise<number> {
3580
3652
  cols: Math.max(0, Math.floor(Number(b.cols) || 0)),
3581
3653
  rows: Math.max(0, Math.floor(Number(b.rows) || 0)),
3582
3654
  sel: typeof b.sel === "string" ? b.sel.slice(0, 200) : null,
3583
- cap: sanitizeCap(b.cap),
3655
+ cap,
3584
3656
  ts: Date.now(),
3585
3657
  });
3586
- if (b.agent != null) scheduleNego(Number(b.agent));
3658
+ // Mirror this viewer's SIZE cap into the shared cap store so sibling daemons
3659
+ // negotiate off it too. A cleared/agent-switched/cap-less viewer withdraws its
3660
+ // cap (from the old agent) so its size stops counting immediately, not on TTL.
3661
+ if (prevAgent != null && String(prevAgent) !== String(b.agent ?? ""))
3662
+ await withdrawCap(Number(prevAgent), viewer);
3663
+ if (b.agent != null) {
3664
+ if (cap) await publishCap(Number(b.agent), viewer, cap, "viewer");
3665
+ else await withdrawCap(Number(b.agent), viewer);
3666
+ scheduleNego(Number(b.agent));
3667
+ }
3587
3668
  if (prevAgent != null && String(prevAgent) !== String(b.agent ?? ""))
3588
3669
  scheduleNego(Number(prevAgent));
3589
3670
  return new Response(null, { status: 204 });
package/ts/subcommands.ts CHANGED
@@ -4054,6 +4054,10 @@ async function cmdAttach(rest: string[]): Promise<number> {
4054
4054
  const rows = process.stdout.rows ?? 24;
4055
4055
  try {
4056
4056
  await writeFile(winsizePath, `${cols} ${rows} ${Date.now()}\n`);
4057
+ // Trace the source (mirrors the daemon's /api/resize log) — `ay attach` also
4058
+ // resizes the shared PTY to its terminal, so it must be visible when hunting
4059
+ // a resize-fight. Goes to this attach process's stderr (not the daemon log).
4060
+ process.stderr.write(`[api/resize] pid=${record.pid} ${cols}x${rows} src=ay-attach\n`);
4057
4061
  try {
4058
4062
  process.kill(record.pid, "SIGWINCH");
4059
4063
  } catch {