agent-yes 1.303.0 → 1.303.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.
Files changed (74) hide show
  1. package/dist/{SUPPORTED_CLIS-BvWBFlrN.js → SUPPORTED_CLIS-ByO5twge.js} +3 -3
  2. package/dist/{SUPPORTED_CLIS-CqmE-ESB.js → SUPPORTED_CLIS-Dq9Lq9WJ.js} +2 -2
  3. package/dist/{agentShare-Gb4vxJzE.js → agentShare-suB6yPgO.js} +7 -6
  4. package/dist/{askCli-J9hHcgJz.js → askCli-BU3CNCLU.js} +2 -2
  5. package/dist/{callback-CuIY-75l.js → callback-Ctj3_Wxp.js} +5 -5
  6. package/dist/{callback-ZzRPkYjf.js → callback-D7qfh_7p.js} +7 -6
  7. package/dist/{callbackCore-DXVwvq0X.js → callbackCore-BesbQ6-Z.js} +1 -1
  8. package/dist/{callbackCore-BVEHd6Yt.js → callbackCore-DSJWhqAe.js} +1 -1
  9. package/dist/{channels-BLdkzCbv.js → channels-j5nWuzlQ.js} +2 -2
  10. package/dist/cli.js +11 -11
  11. package/dist/{cmdDsh-CymghOa0.js → cmdDsh-2LP0grim.js} +1 -1
  12. package/dist/{cmdPs-DSKYjOKS.js → cmdPs-wc1j85fi.js} +7 -6
  13. package/dist/{cwdConflictWarn-DXh7qJz6.js → cwdConflictWarn-skQSMwEh.js} +1 -1
  14. package/dist/{cwdPassthroughHint-DADidTOP.js → cwdPassthroughHint-BUbWMoz0.js} +1 -1
  15. package/dist/{expose-ZFn10b_l.js → expose-Dawq1LxC.js} +1 -1
  16. package/dist/{forkNested-DD_4xEL8.js → forkNested-CavHyWzA.js} +1 -1
  17. package/dist/{hist-Dy-JVuUY.js → hist-Cs0KFSUW.js} +1 -1
  18. package/dist/index.js +2 -2
  19. package/dist/{initMsg-BGBIVi_Q.js → initMsg-CcTqFtsp.js} +1 -1
  20. package/dist/{initMsg-NleE4YLm.js → initMsg-D9KJnfgC.js} +1 -1
  21. package/dist/{needsInput-D__56HYf.js → needsInput-hRiWFjGy.js} +1 -1
  22. package/dist/{nodeRuntime-oANVaOqi.js → nodeRuntime-DGMQRTGT.js} +1 -1
  23. package/dist/{notifyDaemon-C_gTWqYZ.js → notifyDaemon-C7GlfJ1E.js} +6 -5
  24. package/dist/{notifyStore-D9cNjXFG.js → notifyStore-C4pH_-zJ.js} +1 -1
  25. package/dist/{notifyStore-DI4iKFut.js → notifyStore-DIfkfKB2.js} +1 -1
  26. package/dist/{openBrowser-B6ZyKxfP.js → openBrowser-1cIo4dJL.js} +1 -1
  27. package/dist/{parentLink-qUr7dRdh.js → parentLink-B58V1RVd.js} +1 -1
  28. package/dist/{parentLink-CB8Nc-dr.js → parentLink-DoEWVGld.js} +1 -1
  29. package/dist/{parentPingLoop-BhVR1EQm.js → parentPingLoop-C73bbmCE.js} +6 -6
  30. package/dist/{procStats-xvUthnOx.js → procStats-BvmrBrOs.js} +1 -1
  31. package/dist/{remotes-COBEhmlr.js → remotes-DevcBeLl.js} +5 -4
  32. package/dist/{remotes-C36aI8Ty.js → remotes-MfdV9M8u.js} +2 -2
  33. package/dist/roomReplyAlias-DFqg_6IU.js +58 -0
  34. package/dist/roomReplyAlias-DuEkSSDI.js +6 -0
  35. package/dist/{rustBinary-eOcvZ5yS.js → rustBinary-7VK7oLlW.js} +24 -3
  36. package/dist/rustBinary-BV4LxMMn.js +4 -0
  37. package/dist/{schedule-E2QlsME5.js → schedule-Dp0qkNO1.js} +6 -6
  38. package/dist/{serve-BWy7zL7j.js → serve-BpOIIOVc.js} +48 -41
  39. package/dist/{serveRust-BgY5Te5E.js → serveRust-CbBrcUrg.js} +2 -2
  40. package/dist/{setup-CD9-RyhT.js → setup-DdIBk6Iv.js} +4 -4
  41. package/dist/{share-530eCWft.js → share-DrS3LYqf.js} +1 -1
  42. package/dist/{share-Jl8Eia9b.js → share-w5oRbvlj.js} +1 -1
  43. package/dist/{shareCmd-Drxb-SjM.js → shareCmd-DDENkBW2.js} +10 -10
  44. package/dist/{spawnGate-CsQ4_N5h.js → spawnGate-DeQBTX-o.js} +1 -1
  45. package/dist/{spawnGate-SX13EqI3.js → spawnGate-lr3tvA6U.js} +2 -2
  46. package/dist/{subcommands-C627cuOl.js → subcommands-DmiswRy4.js} +5 -4
  47. package/dist/{subcommands-Cc6bmEnB.js → subcommands-DxgvDmZX.js} +36 -31
  48. package/dist/{systemPathLink-eWUYHNXm.js → systemPathLink-SjUzgSiq.js} +1 -1
  49. package/dist/{terminal-BddOL2fx.js → terminal-C2h7tpe0.js} +2 -2
  50. package/dist/{todoCli-B62mo9af.js → todoCli-BWm5nZE0.js} +2 -2
  51. package/dist/{todoIdentity-CY1UkEs7.js → todoIdentity-BvXZKWJI.js} +1 -1
  52. package/dist/{tray-DJtC0yib.js → tray-BeRSozJz.js} +1 -1
  53. package/dist/trayApp-C4TAvRvU.js +5 -0
  54. package/dist/{trayApp-D0UYVT6n.js → trayApp-QQjqhDv1.js} +2 -2
  55. package/dist/{ts-Bm6FhYS4.js → ts-6LF7f4fx.js} +5 -5
  56. package/dist/{versionChecker-CFyZKym2.js → versionChecker-Bc62HNf6.js} +2 -2
  57. package/dist/{webrtcLink-CHLgPKWj.js → webrtcLink-SsInrVHU.js} +1 -1
  58. package/dist/{webrtcLink-bWxiCBTV.js → webrtcLink-va_gAR77.js} +1 -1
  59. package/dist/{webrtcRemote-RmkbzY6Y.js → webrtcRemote-BlGyNZDC.js} +2 -2
  60. package/dist/{widget-C6-P26Ue.js → widget-Crb5zeHL.js} +3 -3
  61. package/dist/{workspaceConfig-CWGh8o36.js → workspaceConfig-BLWB5Wu9.js} +1 -1
  62. package/dist/{ws-DmiSL50p.js → ws-BRhPG1Vy.js} +6 -5
  63. package/dist/{ws-DCcdDRXH.js → ws-DIbwNz8Z.js} +3 -3
  64. package/package.json +1 -1
  65. package/ts/buildEnvelope.bun.spec.ts +10 -0
  66. package/ts/buildEnvelope.spec.ts +10 -0
  67. package/ts/remotes.ts +9 -1
  68. package/ts/roomReplyAlias.spec.ts +21 -0
  69. package/ts/roomReplyAlias.ts +67 -0
  70. package/ts/rustBinary.ts +19 -0
  71. package/ts/serve.ts +13 -5
  72. package/ts/subcommands.ts +12 -6
  73. package/dist/rustBinary-Ep0x7WrS.js +0 -4
  74. package/dist/trayApp-CdLbJK5F.js +0 -5
@@ -3,7 +3,7 @@ import { a as renderTree, c as describeBlock, i as renderDigest, n as buildTreeJ
3
3
  import "./JsonlStore-BpYZZTap.js";
4
4
  import { a as readGlobalPids } from "./globalPidIndex-CRB5KtyL.js";
5
5
  import { a as LIFECYCLES, l as isKnownKind, r as openStore, t as CycleError } from "./todoStore-C9RTz0Vs.js";
6
- import { a as resolveTodoRoot, i as resolveSelf, n as SELF_OWNER, r as ownerLiveness, t as NO_OWNER } from "./todoIdentity-CY1UkEs7.js";
6
+ import { a as resolveTodoRoot, i as resolveSelf, n as SELF_OWNER, r as ownerLiveness, t as NO_OWNER } from "./todoIdentity-BvXZKWJI.js";
7
7
  import yargs from "yargs";
8
8
 
9
9
  //#region ts/todoCli.ts
@@ -583,4 +583,4 @@ async function runTodoSubcommand(rest0) {
583
583
 
584
584
  //#endregion
585
585
  export { runTodoSubcommand };
586
- //# sourceMappingURL=todoCli-B62mo9af.js.map
586
+ //# sourceMappingURL=todoCli-BWm5nZE0.js.map
@@ -135,4 +135,4 @@ function ownerLiveness(owner, agents) {
135
135
 
136
136
  //#endregion
137
137
  export { resolveTodoRoot as a, resolveSelf as i, SELF_OWNER as n, ownerLiveness as r, NO_OWNER as t };
138
- //# sourceMappingURL=todoIdentity-CY1UkEs7.js.map
138
+ //# sourceMappingURL=todoIdentity-BvXZKWJI.js.map
@@ -187,4 +187,4 @@ async function startTray() {
187
187
 
188
188
  //#endregion
189
189
  export { ensureTray, startTray };
190
- //# sourceMappingURL=tray-DJtC0yib.js.map
190
+ //# sourceMappingURL=tray-BeRSozJz.js.map
@@ -0,0 +1,5 @@
1
+ import "./versionChecker-Bc62HNf6.js";
2
+ import "./rustBinary-7VK7oLlW.js";
3
+ import { a as trayHiddenMarker, i as launchTray, n as hasDesktop, r as isTrayHidden, t as cmdTray } from "./trayApp-QQjqhDv1.js";
4
+
5
+ export { cmdTray };
@@ -1,4 +1,4 @@
1
- import { c as findTrayLauncher } from "./rustBinary-eOcvZ5yS.js";
1
+ import { l as findTrayLauncher } from "./rustBinary-7VK7oLlW.js";
2
2
  import { t as agentYesHome } from "./agentYesHome-JmJC28YX.js";
3
3
  import { existsSync } from "node:fs";
4
4
  import path from "node:path";
@@ -97,4 +97,4 @@ async function cmdTray(args) {
97
97
 
98
98
  //#endregion
99
99
  export { trayHiddenMarker as a, launchTray as i, hasDesktop as n, isTrayHidden as r, cmdTray as t };
100
- //# sourceMappingURL=trayApp-D0UYVT6n.js.map
100
+ //# sourceMappingURL=trayApp-QQjqhDv1.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 { i as getInstalledPackage } from "./versionChecker-CFyZKym2.js";
3
+ import { i as getInstalledPackage } from "./versionChecker-Bc62HNf6.js";
4
4
  import { t as agentYesHome } from "./agentYesHome-JmJC28YX.js";
5
5
  import { u as removeControlCharacters } from "./todoAutomation-TTp_Qz9G.js";
6
6
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DMznYh45.js";
@@ -1613,10 +1613,10 @@ async function agentYes({ cli, cliArgs = [], skipPermissions = false, prompt, ro
1613
1613
  })();
1614
1614
  const originalPrompt = prompt;
1615
1615
  if (prompt && inheritedParentPid) try {
1616
- const { resolveSpawner } = await import("./parentLink-qUr7dRdh.js");
1616
+ const { resolveSpawner } = await import("./parentLink-B58V1RVd.js");
1617
1617
  const spawner = await resolveSpawner(inheritedParentPid);
1618
1618
  if (spawner) {
1619
- const { buildInitMsg } = await import("./initMsg-NleE4YLm.js");
1619
+ const { buildInitMsg } = await import("./initMsg-D9KJnfgC.js");
1620
1620
  const { formatIdentity } = await import("./identity-DA7YI9km.js");
1621
1621
  const identity = formatIdentity({
1622
1622
  cwd: spawner.cwd,
@@ -2086,7 +2086,7 @@ async function agentYes({ cli, cliArgs = [], skipPermissions = false, prompt, ro
2086
2086
  }, 100);
2087
2087
  const cleanupHeartbeat = () => clearInterval(heartbeatInterval);
2088
2088
  shell.onExit(cleanupHeartbeat);
2089
- const { startParentPingLoop } = await import("./parentPingLoop-BhVR1EQm.js");
2089
+ const { startParentPingLoop } = await import("./parentPingLoop-C73bbmCE.js");
2090
2090
  const parentPingLoop = startParentPingLoop({
2091
2091
  parentPid,
2092
2092
  selfWrapperPid: process.pid,
@@ -2349,4 +2349,4 @@ function sleep(ms) {
2349
2349
 
2350
2350
  //#endregion
2351
2351
  export { hasTodoStore as a, permissionBadge as c, answerAsk as i, AgentContext as l, agentYes as n, listAsks as o, config as r, listAsksForProject as s, CLIS_CONFIG as t, CLAUDE_SESSION_PIN_ENV as u };
2352
- //# sourceMappingURL=ts-Bm6FhYS4.js.map
2352
+ //# sourceMappingURL=ts-6LF7f4fx.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.303.0";
10
+ var version = "1.303.1";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -285,4 +285,4 @@ async function displayVersion() {
285
285
 
286
286
  //#endregion
287
287
  export { versionString as a, getInstalledPackage as i, compareVersions as n, displayVersion as r, checkAndAutoUpdate as t };
288
- //# sourceMappingURL=versionChecker-CFyZKym2.js.map
288
+ //# sourceMappingURL=versionChecker-Bc62HNf6.js.map
@@ -1,4 +1,4 @@
1
1
  import "./e2e-BdLf8DvY.js";
2
- import { a as parseWebrtcLink, i as isWebrtcSpec, n as SIGNAL_SUBPROTOCOL, o as toWebrtcUrl, r as hasJoinSecret, t as DEFAULT_SIGHOST } from "./webrtcLink-bWxiCBTV.js";
2
+ import { a as parseWebrtcLink, i as isWebrtcSpec, n as SIGNAL_SUBPROTOCOL, o as toWebrtcUrl, r as hasJoinSecret, t as DEFAULT_SIGHOST } from "./webrtcLink-va_gAR77.js";
3
3
 
4
4
  export { parseWebrtcLink };
@@ -87,4 +87,4 @@ function isWebrtcSpec(spec) {
87
87
 
88
88
  //#endregion
89
89
  export { parseWebrtcLink as a, isWebrtcSpec as i, SIGNAL_SUBPROTOCOL as n, toWebrtcUrl as o, hasJoinSecret as r, DEFAULT_SIGHOST as t };
90
- //# sourceMappingURL=webrtcLink-bWxiCBTV.js.map
90
+ //# sourceMappingURL=webrtcLink-va_gAR77.js.map
@@ -1,5 +1,5 @@
1
1
  import { a as computeTranscriptHash, c as open, d as randomHex, f as seal, l as packEnvelope, n as FLAG_CONFIRM, o as deriveAuthToken, p as unpackEnvelope, s as deriveDirKeys } from "./e2e-BdLf8DvY.js";
2
- import { a as parseWebrtcLink, i as isWebrtcSpec, n as SIGNAL_SUBPROTOCOL } from "./webrtcLink-bWxiCBTV.js";
2
+ import { a as parseWebrtcLink, i as isWebrtcSpec, n as SIGNAL_SUBPROTOCOL } from "./webrtcLink-va_gAR77.js";
3
3
  import { hostname } from "os";
4
4
  import { RTCPeerConnection } from "node-datachannel/polyfill";
5
5
 
@@ -680,4 +680,4 @@ async function startWebrtcBridge(link, opts = {}) {
680
680
 
681
681
  //#endregion
682
682
  export { startWebrtcBridge };
683
- //# sourceMappingURL=webrtcRemote-RmkbzY6Y.js.map
683
+ //# sourceMappingURL=webrtcRemote-BlGyNZDC.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-BWy7zL7j.js");
31
+ const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-BpOIIOVc.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-BWy7zL7j.js");
189
+ const { mintScopedTermToken } = await import("./serve-BpOIIOVc.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-C6-P26Ue.js.map
205
+ //# sourceMappingURL=widget-Crb5zeHL.js.map
@@ -215,4 +215,4 @@ function allowsSkipPermissions() {
215
215
 
216
216
  //#endregion
217
217
  export { getProvisionRoot as a, getWorkspaceRoot as c, isProvisionAllowed as d, resolveSpawnCwd as f, getProvisionHook as i, hasProvisionHook as l, getMaxAgents as n, getSpawnHook as o, setWorkspaceRoot as p, getMinFreeMb as r, getSpawnWaitMs as s, allowsSkipPermissions as t, hasSpawnHook as u };
218
- //# sourceMappingURL=workspaceConfig-CWGh8o36.js.map
218
+ //# sourceMappingURL=workspaceConfig-BLWB5Wu9.js.map
@@ -5,10 +5,11 @@ import "./pidStore-433kvh9D.js";
5
5
  import "./messageLog-Dq_ka-Bt.js";
6
6
  import "./e2e-BdLf8DvY.js";
7
7
  import "./configShared-De4bR5hH.js";
8
- import "./subcommands-Cc6bmEnB.js";
9
- import "./notifyStore-DI4iKFut.js";
10
- import "./webrtcLink-bWxiCBTV.js";
11
- import "./remotes-COBEhmlr.js";
12
- import { a as isPathInside, c as walkWorkspaces, i as importProvisionModule, l as workspaceStatus, n as cmdWs, o as loadProvision, r as collectWorkspaces, s as resolveOperand, t as WS_JSON_SCHEMA } from "./ws-DCcdDRXH.js";
8
+ import "./webrtcLink-va_gAR77.js";
9
+ import "./remotes-DevcBeLl.js";
10
+ import "./roomReplyAlias-DFqg_6IU.js";
11
+ import "./subcommands-DxgvDmZX.js";
12
+ import "./notifyStore-DIfkfKB2.js";
13
+ import { a as isPathInside, c as walkWorkspaces, i as importProvisionModule, l as workspaceStatus, n as cmdWs, o as loadProvision, r as collectWorkspaces, s as resolveOperand, t as WS_JSON_SCHEMA } from "./ws-DIbwNz8Z.js";
13
14
 
14
15
  export { WS_JSON_SCHEMA, cmdWs, collectWorkspaces, isPathInside, loadProvision, walkWorkspaces, workspaceStatus };
@@ -1,5 +1,5 @@
1
- import { V as listRecords } from "./subcommands-Cc6bmEnB.js";
2
- import { a as getProvisionRoot } from "./workspaceConfig-CWGh8o36.js";
1
+ import { V as listRecords } from "./subcommands-DxgvDmZX.js";
2
+ import { a as getProvisionRoot } from "./workspaceConfig-BLWB5Wu9.js";
3
3
  import { existsSync } from "fs";
4
4
  import { lstat, opendir } from "fs/promises";
5
5
  import path from "path";
@@ -424,4 +424,4 @@ async function cmdWs(args) {
424
424
 
425
425
  //#endregion
426
426
  export { isPathInside as a, walkWorkspaces as c, importProvisionModule as i, workspaceStatus as l, cmdWs as n, loadProvision as o, collectWorkspaces as r, resolveOperand as s, WS_JSON_SCHEMA as t };
427
- //# sourceMappingURL=ws-DCcdDRXH.js.map
427
+ //# sourceMappingURL=ws-DIbwNz8Z.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.303.0",
3
+ "version": "1.303.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",
@@ -59,4 +59,14 @@ describe("buildEnvelope", () => {
59
59
  expect(prefix).toContain("deadbeef");
60
60
  expect(suffix).toContain("deadbeef");
61
61
  });
62
+
63
+ it("names the shared room in the reply route, for the receiver to fill in its alias", () => {
64
+ // Sent through a room this machine hosts in: the receiver reaches us as
65
+ // <its alias for the room>:<our member name>:<agent>.
66
+ const { prefix } = buildEnvelope({
67
+ ...base,
68
+ remote: { senderHost: "box", room: { id: "r1", member: "worker-d" } },
69
+ });
70
+ expect(prefix).toContain(`reply: ay send <alias-for-room-r1>:worker-d:24db968a3ab1 "..."`);
71
+ });
62
72
  });
@@ -59,4 +59,14 @@ describe("buildEnvelope", () => {
59
59
  expect(prefix).toContain("deadbeef");
60
60
  expect(suffix).toContain("deadbeef");
61
61
  });
62
+
63
+ it("names the shared room in the reply route, for the receiver to fill in its alias", () => {
64
+ // Sent through a room this machine hosts in: the receiver reaches us as
65
+ // <its alias for the room>:<our member name>:<agent>.
66
+ const { prefix } = buildEnvelope({
67
+ ...base,
68
+ remote: { senderHost: "box", room: { id: "r1", member: "worker-d" } },
69
+ });
70
+ expect(prefix).toContain(`reply: ay send <alias-for-room-r1>:worker-d:24db968a3ab1 "..."`);
71
+ });
62
72
  });
package/ts/remotes.ts CHANGED
@@ -20,6 +20,8 @@ export interface ResolvedRemote {
20
20
  keyword?: string;
21
21
  /** Stable, human-readable name for display — never the loopback bridge URL. */
22
22
  label: string;
23
+ /** The share room, when this remote is one (a WebRTC link). */
24
+ room?: string;
23
25
  }
24
26
 
25
27
  /** A display label for a WebRTC share link: the room id, never the local bridge. */
@@ -174,7 +176,13 @@ async function resolveWebrtc(
174
176
  // In a room with several hosts the keyword also picks the host
175
177
  // (`<alias>:<host>:<agent>`, or the one host with a matching agent).
176
178
  const bridge = await startWebrtcBridge(link, { keyword });
177
- return { url: bridge.baseUrl, token: bridge.token, keyword: bridge.keyword, label };
179
+ return {
180
+ url: bridge.baseUrl,
181
+ token: bridge.token,
182
+ keyword: bridge.keyword,
183
+ label,
184
+ room: parseWebrtcLink(link)?.room,
185
+ };
178
186
  }
179
187
 
180
188
  // ---------------------------------------------------------------------------
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { fillRoomAlias, roomAliasPlaceholder } from "./roomReplyAlias.ts";
3
+
4
+ const fleet = (room: string) => (room === "r1" ? "fleet" : undefined);
5
+
6
+ describe("fillRoomAlias", () => {
7
+ it("fills the receiver's alias for the room into the header only", () => {
8
+ const msg =
9
+ `<ay-msg ab from claude — reply: ay send ${roomAliasPlaceholder("r1")}:worker-d:fc59 "...">\n` +
10
+ `body mentions ${roomAliasPlaceholder("r1")} literally\n</ay-msg ab>`;
11
+ const out = fillRoomAlias(msg, fleet);
12
+ expect(out.split("\n")[0]).toContain(`reply: ay send fleet:worker-d:fc59 "..."`);
13
+ expect(out).toContain(`body mentions <alias-for-room-r1> literally`);
14
+ });
15
+
16
+ it("leaves unknown rooms and non-envelope text alone", () => {
17
+ const msg = `<ay-msg ab — reply: ay send <alias-for-room-r2>:m:1 "...">\nhi`;
18
+ expect(fillRoomAlias(msg, fleet)).toBe(msg);
19
+ expect(fillRoomAlias("<alias-for-room-r1>", fleet)).toBe("<alias-for-room-r1>");
20
+ });
21
+ });
@@ -0,0 +1,67 @@
1
+ // The reply route of a message that crossed a shared room.
2
+ //
3
+ // The sender builds the <ay-msg …> header (subcommands.ts buildEnvelope) but
4
+ // cannot know what the RECEIVER calls the room, so its reply hint names the
5
+ // room instead: `ay send <alias-for-room-<room id>>:<member>:<agent>`. The
6
+ // receiving host knows its own aliases (~/.agent-yes/remotes.yaml) and fills
7
+ // the placeholder in before the message reaches the agent, so the agent gets a
8
+ // command it can run as is (`ay send fleet:worker-d:<agent>`). The Rust daemon
9
+ // does the same in rs/src/serve/reply_alias.rs.
10
+
11
+ import { readFile } from "fs/promises";
12
+ import { hostname } from "os";
13
+ import path from "path";
14
+ import { agentYesHome } from "./agentYesHome.ts";
15
+ import { readRemotes } from "./remotes.ts";
16
+ import { parseWebrtcLink } from "./webrtcLink.ts";
17
+
18
+ export function roomAliasPlaceholder(room: string): string {
19
+ return `<alias-for-room-${room}>`;
20
+ }
21
+
22
+ const PLACEHOLDER = /<alias-for-room-([A-Za-z0-9_-]{1,64})>/g;
23
+
24
+ /**
25
+ * `msg` with the room placeholders in its envelope header replaced by the
26
+ * receiver's alias for that room. Only the first line, and only when it is an
27
+ * <ay-msg …> header: the body is the sender's text and is never rewritten.
28
+ */
29
+ export function fillRoomAlias(msg: string, aliasFor: (room: string) => string | undefined): string {
30
+ if (!msg.startsWith("<ay-msg ")) return msg;
31
+ const nl = msg.indexOf("\n");
32
+ const head = nl < 0 ? msg : msg.slice(0, nl);
33
+ const filled = head.replace(PLACEHOLDER, (all, room: string) => aliasFor(room) ?? all);
34
+ return filled === head ? msg : filled + (nl < 0 ? "" : msg.slice(nl));
35
+ }
36
+
37
+ /** This machine's aliases for share rooms: room id → alias. */
38
+ export async function roomAliases(): Promise<Map<string, string>> {
39
+ const out = new Map<string, string>();
40
+ for (const [alias, cfg] of await readRemotes().catch(() => new Map())) {
41
+ const room = parseWebrtcLink(String(cfg?.url ?? ""))?.room;
42
+ if (room && !out.has(room)) out.set(room, alias);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ /**
48
+ * Sender side: `{id, member}` when this machine hosts in `room` right now (its
49
+ * ayrs holds `~/.agent-yes/.share-host-<room>.pid`, see rs/src/serve/share.rs),
50
+ * so a reply addressed `<room alias>:<member>:<agent>` reaches us. Undefined
51
+ * otherwise — a machine that only views the room cannot be replied to through it.
52
+ */
53
+ export async function hostedRoomMember(
54
+ room: string | undefined,
55
+ ): Promise<{ id: string; member: string } | undefined> {
56
+ if (!room) return undefined;
57
+ try {
58
+ const lock = path.join(agentYesHome(), `.share-host-${room}.pid`);
59
+ const pid = parseInt(await readFile(lock, "utf-8"), 10);
60
+ if (!(pid > 0)) return undefined;
61
+ process.kill(pid, 0);
62
+ } catch (e) {
63
+ if ((e as NodeJS.ErrnoException)?.code !== "EPERM") return undefined;
64
+ }
65
+ // The name ayrs announces (rs/src/serve/room_member.rs member_name).
66
+ return { id: room, member: process.env.AY_NAME?.trim() || hostname() };
67
+ }
package/ts/rustBinary.ts CHANGED
@@ -453,6 +453,25 @@ export function devBuildInfo(
453
453
  return { rsDir: m[1]!, isRelease: m[2] === "release" };
454
454
  }
455
455
 
456
+ /**
457
+ * findAyrsBinary, downloading this version's release bundle (which carries
458
+ * ayrs) when it is missing. The bundle used to arrive only with the first
459
+ * `ay <cli>` run, so on a fresh install `ay serve` either fell back to the
460
+ * TypeScript server without saying why it had no ayrs, or — for what only
461
+ * ayrs can do (`--join`, `join-secret`) — failed outright.
462
+ */
463
+ export async function ensureAyrsBinary(verbose = false): Promise<string | undefined> {
464
+ const found = findAyrsBinary();
465
+ if (found) return found;
466
+ try {
467
+ process.stderr.write(`ay serve: fetching the Rust daemon (ayrs) for this version…\n`);
468
+ await getRustBinary({ verbose, forceDownload: !!findRustBinary() });
469
+ } catch (err) {
470
+ if (verbose) console.error(`[rust] ${err instanceof Error ? err.message : String(err)}`);
471
+ }
472
+ return findAyrsBinary();
473
+ }
474
+
456
475
  /**
457
476
  * Check if a binary path is inside a git repo (dev build), and rebuild if outdated.
458
477
  * Returns the same path if up-to-date or rebuilt, undefined if rebuild failed.
package/ts/serve.ts CHANGED
@@ -1917,8 +1917,8 @@ export async function cmdServe(rest: string[]): Promise<number> {
1917
1917
  const { planRustServe } = await import("./serveRust.ts");
1918
1918
  const plan = planRustServe(rest);
1919
1919
  if (plan.kind === "rust-only") {
1920
- const { findAyrsBinary } = await import("./rustBinary.ts");
1921
- const ayrs = findAyrsBinary();
1920
+ const { ensureAyrsBinary } = await import("./rustBinary.ts");
1921
+ const ayrs = await ensureAyrsBinary();
1922
1922
  if (ayrs) return runAyrsServe(ayrs, plan.argv);
1923
1923
  process.stderr.write(
1924
1924
  `ay serve: ${plan.feature} needs the Rust daemon (ayrs), which was not found ` +
@@ -1927,8 +1927,8 @@ export async function cmdServe(rest: string[]): Promise<number> {
1927
1927
  return 1;
1928
1928
  }
1929
1929
  if (plan.kind === "rust") {
1930
- const { findAyrsBinary } = await import("./rustBinary.ts");
1931
- const ayrs = findAyrsBinary();
1930
+ const { ensureAyrsBinary } = await import("./rustBinary.ts");
1931
+ const ayrs = await ensureAyrsBinary();
1932
1932
  if (ayrs) return runAyrsServe(ayrs, plan.argv);
1933
1933
  process.stderr.write(
1934
1934
  `ay serve: ayrs binary not found — running the TypeScript server ` +
@@ -4057,7 +4057,15 @@ export async function cmdServe(rest: string[]): Promise<number> {
4057
4057
  } catch {
4058
4058
  return new Response("invalid JSON body", { status: 400 });
4059
4059
  }
4060
- const { keyword, msg = "", code = "enter", from = null, raw = false } = body;
4060
+ const { keyword, code = "enter", from = null, raw = false } = body;
4061
+ let msg = body.msg ?? "";
4062
+ // A message from a shared room names the room, not our alias for it, in
4063
+ // its reply hint; fill ours in (ts/roomReplyAlias.ts).
4064
+ if (!raw && msg.startsWith("<ay-msg ") && msg.includes("<alias-for-room-")) {
4065
+ const { fillRoomAlias, roomAliases } = await import("./roomReplyAlias.ts");
4066
+ const aliases = await roomAliases();
4067
+ msg = fillRoomAlias(msg, (room) => aliases.get(room));
4068
+ }
4061
4069
  if (!keyword || typeof keyword !== "string") {
4062
4070
  return new Response("missing keyword", { status: 400 });
4063
4071
  }
package/ts/subcommands.ts CHANGED
@@ -20,6 +20,7 @@ import { homedir } from "os";
20
20
  import path from "path";
21
21
  import { type GlobalPidRecord, readGlobalPids, updateGlobalPidStatus } from "./globalPidIndex.ts";
22
22
  import { formatIdentity, localHost, localUser } from "./identity.ts";
23
+ import { hostedRoomMember, roomAliasPlaceholder } from "./roomReplyAlias.ts";
23
24
  import { buildAgentForest, flattenForest } from "./agentTree.ts";
24
25
  import { parseTaskCounts, type TaskCounts } from "./todoParse.ts";
25
26
  import { agentYesHome } from "./agentYesHome.ts";
@@ -837,14 +838,19 @@ export function buildEnvelope(opts: {
837
838
  replyTarget: string | number;
838
839
  via: SenderVia;
839
840
  /** Set when the message crossed a host boundary; the sender's own label for
840
- * the far side is deliberately NOT used — it is meaningless to the receiver. */
841
- remote?: { senderHost: string } | null;
841
+ * the far side is deliberately NOT used — it is meaningless to the receiver.
842
+ * `room`: the message went through a shared room this machine hosts in, as
843
+ * `member`; the receiver fills in its alias for the room (roomReplyAlias.ts). */
844
+ remote?: { senderHost: string; room?: { id: string; member: string } } | null;
842
845
  }): { prefix: string; suffix: string } {
843
846
  const attrib = envelopeAttribution(opts.via);
844
847
  const via = opts.remote ? " via remote" : "";
845
- const reply = opts.remote
846
- ? `reply: ay send <your-remote-alias-for-${opts.remote.senderHost}>:${opts.replyTarget} "..."`
847
- : `reply: ay send ${opts.replyTarget} "..."`;
848
+ const room = opts.remote?.room;
849
+ const reply = room
850
+ ? `reply: ay send ${roomAliasPlaceholder(room.id)}:${room.member}:${opts.replyTarget} "..."`
851
+ : opts.remote
852
+ ? `reply: ay send <your-remote-alias-for-${opts.remote.senderHost}>:${opts.replyTarget} "..."`
853
+ : `reply: ay send ${opts.replyTarget} "..."`;
848
854
  return {
849
855
  prefix: `<ay-msg ${opts.nonce} from ${opts.cli}${attrib}${via} ${opts.identity} — ${reply}>\n`,
850
856
  suffix: `\n</ay-msg ${opts.nonce}>`,
@@ -2060,7 +2066,7 @@ async function runRemoteSend(
2060
2066
  replyTarget: sender.agent.agent_id || sender.agent.pid,
2061
2067
  via: sender.via,
2062
2068
  // The receiver needs a route back to OUR host, not our label for theirs.
2063
- remote: { senderHost: localHost() },
2069
+ remote: { senderHost: localHost(), room: await hostedRoomMember(remote.room) },
2064
2070
  });
2065
2071
  wire = prefix + msg + suffix;
2066
2072
  }
@@ -1,4 +0,0 @@
1
- import "./versionChecker-CFyZKym2.js";
2
- import { a as findAyrsBinary, c as findTrayLauncher, d as getBinaryName, f as getDownloadUrl, i as ensureExecutable, l as gcOldBinaryDirs, m as hasValidMacSignature, n as devBuildInfo, o as findRustBinary, p as getRustBinary, r as downloadBinary, s as findSpawnHiddenLauncher, t as chmodExtractedBinaries, u as getBinDir } from "./rustBinary-eOcvZ5yS.js";
3
-
4
- export { findAyrsBinary, gcOldBinaryDirs };
@@ -1,5 +0,0 @@
1
- import "./versionChecker-CFyZKym2.js";
2
- import "./rustBinary-eOcvZ5yS.js";
3
- import { a as trayHiddenMarker, i as launchTray, n as hasDesktop, r as isTrayHidden, t as cmdTray } from "./trayApp-D0UYVT6n.js";
4
-
5
- export { cmdTray };