agent-yes 1.267.2 → 1.269.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 (108) hide show
  1. package/README.md +73 -0
  2. package/dist/{SUPPORTED_CLIS-CpWKDCtL.js → SUPPORTED_CLIS-Bne04-66.js} +2 -2
  3. package/dist/SUPPORTED_CLIS-CdyjhpZP.js +12 -0
  4. package/dist/{agent-yes.config-DzNini0E.js → agent-yes.config-2xKNJM5Y.js} +2 -2
  5. package/dist/{agentShare-D0reU6LW.js → agentShare-BI126Ej9.js} +8 -8
  6. package/dist/askCli-Bbxt9Umz.js +272 -0
  7. package/dist/{browser-DSduVZeZ.js → browser-CorYp8Yu.js} +1 -1
  8. package/dist/{browser-B7eYrBy7.js → browser-_2ndf80G.js} +5 -5
  9. package/dist/callback-BnUbjH7p.js +13 -0
  10. package/dist/{callback-Dlqwh9n7.js → callback-DI2r3NrV.js} +5 -5
  11. package/dist/{callbackCore-K99x3C75.js → callbackCore-Bbe7rpN2.js} +1 -1
  12. package/dist/{callbackCore-DcptaZwH.js → callbackCore-C0Cg9RbS.js} +1 -1
  13. package/dist/{channels-DQaZbare.js → channels-3gz9gHP7.js} +6 -6
  14. package/dist/{channels-yE1YUHCl.js → channels-CkaCyvEp.js} +2 -2
  15. package/dist/channels.js +5 -5
  16. package/dist/cli.js +11 -11
  17. package/dist/{cmdPs-D8gsRFVV.js → cmdPs-CVnlflxP.js} +7 -7
  18. package/dist/{configShared-DtHMBCl7.js → configShared-DYxlvrH0.js} +1 -1
  19. package/dist/{cwdConflictWarn-BpweoAiy.js → cwdConflictWarn-Buy3r_qc.js} +1 -1
  20. package/dist/{cwdPassthroughHint-BlRuOGo_.js → cwdPassthroughHint-BlWO-h5w.js} +1 -1
  21. package/dist/{e2e-CUMZC53I.js → e2e-OLfcNxiv.js} +1 -1
  22. package/dist/{expose-DyiZ5AU0.js → expose-Bfo-dZdy.js} +1 -1
  23. package/dist/{forkNested-B_4c-TQk.js → forkNested-jc2ER9xn.js} +1 -1
  24. package/dist/{hist-BoZPJ2ah.js → hist-DiOYRXuD.js} +1 -1
  25. package/dist/{html2canvas-pro.esm-cq4l_ZIw.js → html2canvas-pro.esm-D19oTQeV.js} +1 -1
  26. package/dist/identity-BmDYOuZE.js +3 -0
  27. package/dist/identity-CLaJ9-uE.js +109 -0
  28. package/dist/index.js +4 -3
  29. package/dist/{initMsg-D8cmdY76.js → initMsg-Bat5PUDf.js} +1 -1
  30. package/dist/{initMsg-BKqWDYFk.js → initMsg-CTAIqBWF.js} +8 -4
  31. package/dist/{needsInput-103zhZ90.js → needsInput-DhfIwxse.js} +1 -1
  32. package/dist/{nodeRuntime-QihGFQbI.js → nodeRuntime-CKc1BN5J.js} +1 -1
  33. package/dist/{notifyDaemon-TKtSYtEO.js → notifyDaemon-Cf4KHDOr.js} +7 -7
  34. package/dist/{notifyStore-CEDiVyis.js → notifyStore-0hlFPa6z.js} +1 -1
  35. package/dist/{notifyStore-Cbw2ZJ0X.js → notifyStore-Dus2D1XA.js} +1 -1
  36. package/dist/{openBrowser-Cs46NiH6.js → openBrowser-Dmg2Oi4q.js} +1 -1
  37. package/dist/{parentLink-CTgZZCgw.js → parentLink-BwX4jaGD.js} +1 -1
  38. package/dist/{parentLink-CQrn998L.js → parentLink-DS-XflaJ.js} +1 -1
  39. package/dist/{parentPingLoop-BEvJ2ElR.js → parentPingLoop-Bxd3dXWO.js} +6 -6
  40. package/dist/{peer-DIl5xtx8.js → peer-Dr4zvtcd.js} +3 -3
  41. package/dist/peer-u1AUAZeA.js +4 -0
  42. package/dist/{remotes-M8FbM92D.js → remotes-BiftUp09.js} +3 -3
  43. package/dist/{remotes-DdAfueOu.js → remotes-IzQHupeo.js} +3 -3
  44. package/dist/{rustBinary-B9FEbqTu.js → rustBinary-B2lNFJSe.js} +2 -2
  45. package/dist/{schedule-Bmh5uaDr.js → schedule-D1CNvuKT.js} +8 -7
  46. package/dist/{serve-BqRNIxeq.js → serve-BaHCO0bU.js} +49 -42
  47. package/dist/{setup-GYkQYxlf.js → setup-B8EL0SWL.js} +4 -4
  48. package/dist/{share-Db_zb6gH.js → share-DTPZgbfH.js} +2 -2
  49. package/dist/{share-NUhi-Wdd.js → share-RgJUhzqG.js} +2 -2
  50. package/dist/{spawnGate-CZlMs6uV.js → spawnGate-C6YKlnmb.js} +1 -1
  51. package/dist/{spawnGate-DN4wNXHq.js → spawnGate-CS3oFdJ9.js} +2 -2
  52. package/dist/{store-BW5NBouz.js → store-C7I2CAu9.js} +1 -1
  53. package/dist/{subcommands-DJpVXIb6.js → subcommands-03ghe-aZ.js} +192 -157
  54. package/dist/{subcommands-DLrJfG4D.js → subcommands-BS4lnTrb.js} +6 -6
  55. package/dist/{systemPathLink-BHv1I6wh.js → systemPathLink-DGVcx0fg.js} +1 -1
  56. package/dist/{terminal-CRLPUaKF.js → terminal-DrCP-200.js} +2 -2
  57. package/dist/terminal.js +1 -1
  58. package/dist/todoAutomation-BETGOik9.js +302 -0
  59. package/dist/{todoCli-BYGZ7_C5.js → todoCli-DUoxA1dh.js} +164 -37
  60. package/dist/todoIdentity-BffBxRHA.js +138 -0
  61. package/dist/{todoAutomation-BQRW614S.js → todoStore-Wk8U4zJ8.js} +63 -280
  62. package/dist/{tray-CzGs9-ch.js → tray-Bxq_sfvU.js} +1 -1
  63. package/dist/trayApp-BDMzlxRo.js +5 -0
  64. package/dist/{trayApp-DLMUu2sB.js → trayApp-DC4qjut7.js} +2 -2
  65. package/dist/{ts-ClwhahH9.js → ts-D0Pk-WJg.js} +14 -8
  66. package/dist/{versionChecker-ZQYirfMp.js → versionChecker-V6ceVsu9.js} +2 -2
  67. package/dist/{webrtcLink-DyCj380i.js → webrtcLink-B72AQ2eb.js} +2 -2
  68. package/dist/{webrtcRemote-Bl8eowma.js → webrtcRemote-D68dkqFp.js} +3 -3
  69. package/dist/{widget-DtZ1WXkf.js → widget-BM03OkC1.js} +3 -3
  70. package/dist/widget.js +1 -1
  71. package/dist/widgets.js +5 -5
  72. package/dist/{workspaceConfig-CBKlgA_q.js → workspaceConfig-EK7XNsgR.js} +1 -1
  73. package/dist/{ws-CiR2nOD3.js → ws-6l7Wb_0r.js} +7 -7
  74. package/dist/{ws-Ba372Tym.js → ws-pRFrqms9.js} +3 -3
  75. package/lab/ui/console-logic.js +64 -0
  76. package/lab/ui/index.html +58 -4
  77. package/package.json +1 -1
  78. package/ts/askCli.spec.ts +304 -0
  79. package/ts/askCli.ts +378 -0
  80. package/ts/identity.spec.ts +80 -1
  81. package/ts/identity.ts +16 -9
  82. package/ts/index.ts +6 -1
  83. package/ts/initMsg.spec.ts +22 -14
  84. package/ts/initMsg.ts +7 -2
  85. package/ts/ipcLock.spec.ts +107 -0
  86. package/ts/ipcLock.ts +130 -0
  87. package/ts/serve.ts +68 -17
  88. package/ts/subcommandMirror.spec.ts +76 -0
  89. package/ts/subcommands.spec.ts +115 -10
  90. package/ts/subcommands.ts +108 -36
  91. package/ts/todoAutomation.spec.ts +64 -0
  92. package/ts/todoAutomation.ts +25 -2
  93. package/ts/todoBlock.spec.ts +31 -1
  94. package/ts/todoBlock.ts +33 -1
  95. package/ts/todoCli.spec.ts +199 -4
  96. package/ts/todoCli.ts +266 -42
  97. package/ts/todoIdentity.spec.ts +85 -0
  98. package/ts/todoIdentity.ts +76 -0
  99. package/ts/todoLifecycle.spec.ts +13 -1
  100. package/ts/todoLifecycle.ts +35 -1
  101. package/ts/todoRoot.spec.ts +117 -0
  102. package/ts/todoRoot.ts +90 -0
  103. package/ts/todoStore.spec.ts +25 -0
  104. package/ts/todoStore.ts +36 -2
  105. package/dist/SUPPORTED_CLIS-DDs0xUvV.js +0 -11
  106. package/dist/callback-Bgehh-IW.js +0 -13
  107. package/dist/peer-Cvntlw1i.js +0 -4
  108. package/dist/trayApp-CoD89Y1_.js +0 -5
package/README.md CHANGED
@@ -204,6 +204,79 @@ cy stop <keyword> # graceful shutdown (claude/codex: /exit)
204
204
  so subcommands work whether the target agent is the TS or Rust runtime.
205
205
  Detailed reference (Japanese): [`docs/cy-subcommands.md`](./docs/cy-subcommands.md).
206
206
 
207
+ ### Shared task list across agents (`ay todo`)
208
+
209
+ A lifecycle-tracked task list that every agent working on a repo reads and
210
+ writes. The store is one append-only file at `<repo>/.agent-yes/todos.jsonl`,
211
+ resolved from the repo's **common root** — so agents running in different git
212
+ worktrees of the same repo share one list rather than each keeping a private
213
+ one. Pass `--root <dir>` (or set `AGENT_YES_TODO_ROOT`) to point somewhere else.
214
+
215
+ ```bash
216
+ ay todo # help: every verb, with descriptions
217
+ ay todo new fix the flaky test --kind code # owner defaults to the calling agent
218
+ ay todo ls # OWNER column marks dead agents: lane-3(exited)
219
+ ay todo ls --owner me # just my tasks
220
+ ay todo claim T4 # take it over (refuses to steal from a live agent)
221
+ ay todo claim T4 --force # take it anyway
222
+ ay todo block T4 --type waiting-on-agent --agent lane-2
223
+ ay todo dep add T5 T4 # T5 waits for T4 (cycles rejected)
224
+ ay todo digest # per-tag board + "unblocked, resume these"
225
+ ay todo reconcile # orphan tasks whose owner agent exited, clear stale blocks
226
+ ```
227
+
228
+ Ownership is cross-referenced against the same agent registry `ay ls` reads, so
229
+ "who owns this and are they still alive?" is answerable in one command:
230
+ `--owner me` resolves to the calling agent's registry id, `ls` annotates each
231
+ owner with that agent's live status, and `reconcile` orphans tasks whose owner
232
+ exited (listing idle agents as reassignment candidates). Use `--owner none` to
233
+ create a task nobody owns, and `--format json` for machine-readable output —
234
+ JSON keeps `owner` verbatim and reports liveness in a separate `ownerLiveness`
235
+ field, so filters that match on `owner` keep working.
236
+
237
+ Tasks move through a lifecycle per `--kind`, and gated transitions require
238
+ **independent verification**: whoever did the work cannot approve it. See
239
+ `ts/todoStore.ts` for the gate model.
240
+
241
+ ### Ask another agent a question (`ay ask`)
242
+
243
+ `ay send` delivers a question but leaves no trace of it: if the asker moves on
244
+ and the other agent dies, wedges, or simply never replies, nothing records that
245
+ an answer is owed. `ay ask` delivers the same way — returning immediately — and
246
+ also writes a task carrying **both** parties, so an unanswered question is
247
+ self-describing.
248
+
249
+ ```bash
250
+ ay ask lane-b "does the cache need invalidating before deploy?"
251
+ # asked lane-b → T4
252
+ # monitor the answer: ay todo get T4 --root "/path/to/repo"
253
+ # monitor the answerer: ay status lane-b
254
+
255
+ ay answer T4 --root "/path/to/repo" "yes — bump CACHE_VERSION first"
256
+ ```
257
+
258
+ The task stores the asker as its `owner` and the answering agent as a
259
+ `waiting-on-answer` block, so `ay todo ls` shows both, each with that agent's
260
+ live status:
261
+
262
+ ```
263
+ ID STATE KIND OWNER WAITING-ON SUMMARY
264
+ T4 pending question lane-a(active) lane-b(exited) does the cache need inv…
265
+ ```
266
+
267
+ That is the failure mode a bare send cannot report: **whoever died holding the
268
+ question is visible in one command.** `ay todo reconcile` says the same thing in
269
+ words (`T4: lane-b exited without answering`) and orphans the task if the
270
+ *asker* is the one that died — without ever closing the question itself, since
271
+ the answer is still owed.
272
+
273
+ Answering is gated on independent verification, which the store enforces: the
274
+ validator must differ from the task's owner, so an asker can never quietly mark
275
+ its own question answered. Targets are agents on this machine (same registry
276
+ `ay ls` reads). `ay ask` respects `ay send`'s recency guard — pass `--force` to
277
+ skip it; `ay answer` replies without it, since its recipient comes off the task
278
+ record rather than from a typed keyword.
279
+
207
280
  ### Docker Usage
208
281
 
209
282
  You can run `agent-yes` in a Docker container with all AI CLI tools pre-installed.
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-ClwhahH9.js";
1
+ import { t as CLIS_CONFIG } from "./ts-D0Pk-WJg.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-CpWKDCtL.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-Bne04-66.js.map
@@ -0,0 +1,12 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import "./versionChecker-V6ceVsu9.js";
3
+ import "./ts-D0Pk-WJg.js";
4
+ import "./todoAutomation-BETGOik9.js";
5
+ import "./JsonlStore-CPGIR0Wy.js";
6
+ import "./globalPidIndex-Dq4KY2IV.js";
7
+ import "./pidStore-DJqSZPJi.js";
8
+ import "./messageLog-BYFwioFY.js";
9
+ import "./todoStore-Wk8U4zJ8.js";
10
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bne04-66.js";
11
+
12
+ export { SUPPORTED_CLIS };
@@ -1,5 +1,5 @@
1
1
  import { n as logger } from "./logger-CDIsZ-Pp.js";
2
- import { n as normalizeAgentYesConfig, t as loadSharedCliDefaults } from "./configShared-DtHMBCl7.js";
2
+ import { n as normalizeAgentYesConfig, t as loadSharedCliDefaults } from "./configShared-DYxlvrH0.js";
3
3
  import path from "node:path";
4
4
  import os from "node:os";
5
5
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
@@ -226,4 +226,4 @@ async function getDefaultConfig() {
226
226
 
227
227
  //#endregion
228
228
  export { agent_yes_config_default as default };
229
- //# sourceMappingURL=agent-yes.config-DzNini0E.js.map
229
+ //# sourceMappingURL=agent-yes.config-2xKNJM5Y.js.map
@@ -1,13 +1,13 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-Dq4KY2IV.js";
3
3
  import "./messageLog-BYFwioFY.js";
4
- import "./e2e-CUMZC53I.js";
5
- import "./configShared-DtHMBCl7.js";
6
- import { B as resolveOne } from "./subcommands-DJpVXIb6.js";
7
- import "./notifyStore-Cbw2ZJ0X.js";
8
- import "./webrtcLink-DyCj380i.js";
9
- import "./remotes-DdAfueOu.js";
10
- import { a as startShare } from "./share-NUhi-Wdd.js";
4
+ import "./e2e-OLfcNxiv.js";
5
+ import "./configShared-DYxlvrH0.js";
6
+ import { B as resolveOne } from "./subcommands-03ghe-aZ.js";
7
+ import "./notifyStore-Dus2D1XA.js";
8
+ import "./webrtcLink-B72AQ2eb.js";
9
+ import "./remotes-IzQHupeo.js";
10
+ import { a as startShare } from "./share-RgJUhzqG.js";
11
11
 
12
12
  //#region ts/agentShare.ts
13
13
  const MAX_SHARES = 8;
@@ -230,4 +230,4 @@ function transformEvent(rawEvent, agentId, forwarded) {
230
230
 
231
231
  //#endregion
232
232
  export { createScopedShare, listShares, revokeAllShares, revokeShare };
233
- //# sourceMappingURL=agentShare-D0reU6LW.js.map
233
+ //# sourceMappingURL=agentShare-BI126Ej9.js.map
@@ -0,0 +1,272 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import "./JsonlStore-CPGIR0Wy.js";
3
+ import "./globalPidIndex-Dq4KY2IV.js";
4
+ import { r as openStore } from "./todoStore-Wk8U4zJ8.js";
5
+ import { t as formatIdentity } from "./identity-CLaJ9-uE.js";
6
+ import { a as resolveTodoRoot, i as resolveSelf } from "./todoIdentity-BffBxRHA.js";
7
+ import yargs from "yargs";
8
+ import { randomBytes } from "crypto";
9
+
10
+ //#region ts/askCli.ts
11
+ /**
12
+ * `ay ask <agent> <question>` and `ay answer <task> <text>` — one agent asking
13
+ * another a question, with the question itself tracked as a task.
14
+ *
15
+ * The problem this solves is not delivery (`ay send` already delivers). It is
16
+ * that a delivered question leaves NO trace: if the asker moves on and the
17
+ * answering agent dies, wedges, or simply never replies, nothing anywhere
18
+ * records that an answer is owed, and the asker discovers it only by noticing
19
+ * that it is still waiting. So every ask also writes a `question` task
20
+ * carrying BOTH parties:
21
+ *
22
+ * owner = the asking agent → covered by reconcile's existing orphan
23
+ * rule: if the asker dies, the task is
24
+ * orphaned and offered to another agent.
25
+ * block = waiting-on-answer{answerer}
26
+ * → keeps the answerer on the record, so
27
+ * `ay todo ls` can report whether the party
28
+ * that owes an answer is still alive.
29
+ *
30
+ * With both identities stored, an unfinished question is self-describing:
31
+ * either side going `exited` is visible in one `ay todo ls`, which is exactly
32
+ * the failure mode ("who died holding this?") that a bare send cannot answer.
33
+ *
34
+ * `ask` returns immediately, like `ay send` — it does not wait for the answer.
35
+ * It prints the task id and the two commands worth watching (the task's state,
36
+ * and the answering agent's liveness).
37
+ *
38
+ * Delivery deliberately reuses `ay send` rather than reimplementing it: this
39
+ * module builds the `<ay-ask-msg …>` envelope and hands it over as a
40
+ * pre-built body (`--raw`), inheriting the typing backoff, submit-confirm and
41
+ * retry behaviour that path already has. Both the send function and the
42
+ * agent-resolver are injected by the caller (`ts/subcommands.ts`) so this
43
+ * module has no import cycle with it, and so tests can drive it without live
44
+ * agents.
45
+ */
46
+ function fail(message) {
47
+ throw new Error(message);
48
+ }
49
+ /**
50
+ * Quote a path for a command line someone (or some agent) will actually run.
51
+ *
52
+ * NOT `JSON.stringify`, which escapes backslashes: a Windows root would be
53
+ * embedded as `"C:\\Users\\x"`, and every shell there treats a backslash
54
+ * literally, so the command carries doubled separators. Path normalisation
55
+ * happens to absorb that today — but the reply command in the envelope below is
56
+ * copied verbatim by an agent, and a command that only works by accident is not
57
+ * one to hand out. Quote only when needed, escape only real quotes.
58
+ */
59
+ function shellQuote(p) {
60
+ return /[\s"]/.test(p) ? `"${p.replace(/"/g, "\\\"")}"` : p;
61
+ }
62
+ const out = (s) => process.stdout.write(`${s}\n`);
63
+ /** First line / first 72 chars of the question — the list view wants one line, `description` keeps the whole thing. */
64
+ function summarize(question) {
65
+ const firstLine = question.split("\n")[0].trim();
66
+ return firstLine.length > 72 ? `${firstLine.slice(0, 71)}…` : firstLine || question.trim();
67
+ }
68
+ /**
69
+ * How the answering agent should reply.
70
+ *
71
+ * `--root` is NOT optional here even though it usually resolves correctly:
72
+ * `ay answer` looks the task up in the store at ITS OWN cwd, and the two
73
+ * agents can be working in different repositories, where that id does not
74
+ * exist (or, worse, exists and means something else). Naming the asker's
75
+ * resolved store root removes the ambiguity — paths are valid across this
76
+ * exchange because delivery is same-host regardless.
77
+ */
78
+ function answerCommand(taskId, root) {
79
+ return `ay answer ${taskId} --root ${shellQuote(root)} "<your answer>"`;
80
+ }
81
+ /**
82
+ * `<ay-ask-msg …>` — the same envelope shape `ay send` uses, with the reply
83
+ * route pointing at `ay answer` instead of `ay send`. The nonce on the open
84
+ * and close tags is what makes the block's boundaries trustworthy: it is
85
+ * generated after the body was authored, so text inside the question cannot
86
+ * forge a matching pair and truncate or extend the trusted region.
87
+ */
88
+ function buildAskEnvelope(input) {
89
+ const nonce = input.nonce ?? randomBytes(4).toString("hex");
90
+ return [
91
+ `<ay-ask-msg ${nonce} from ${input.asker ? `${input.asker.cli} ${formatIdentity({
92
+ cwd: input.asker.cwd,
93
+ pid: input.asker.pid
94
+ })}` : "a human shell"} — task ${input.taskId} — reply: ${answerCommand(input.taskId, input.root)}>`,
95
+ input.question,
96
+ `</ay-ask-msg ${nonce}>`
97
+ ].join("\n");
98
+ }
99
+ /** The mirror of the above, sent back to the asker when the answer lands. */
100
+ function buildAnswerEnvelope(input) {
101
+ const nonce = input.nonce ?? randomBytes(4).toString("hex");
102
+ return [
103
+ `<ay-answer-msg ${nonce} from ${input.answerer ? `${input.answerer.cli} ${formatIdentity({
104
+ cwd: input.answerer.cwd,
105
+ pid: input.answerer.pid
106
+ })}` : "a human shell"} — answers task ${input.taskId} — close it with: ay todo transition ${input.taskId} done --root ${shellQuote(input.root)}>`,
107
+ input.answer,
108
+ `</ay-answer-msg ${nonce}>`
109
+ ].join("\n");
110
+ }
111
+ const askCmd = (deps) => ({
112
+ command: "$0 <agent> <question..>",
113
+ describe: "ask another agent a question and track it as a task",
114
+ builder: (y) => y.positional("agent", {
115
+ type: "string",
116
+ demandOption: true,
117
+ describe: "target agent (pid, agent id, or a cwd/prompt fragment — same matching as `ay send`)"
118
+ }).positional("question", {
119
+ type: "string",
120
+ array: true,
121
+ demandOption: true,
122
+ describe: "the question (unquoted words are joined)"
123
+ }).option("tag", {
124
+ type: "string",
125
+ array: true,
126
+ describe: "tag(s) for the created task"
127
+ }).option("force", {
128
+ type: "boolean",
129
+ default: false,
130
+ describe: "skip `ay send`'s recency guard (which otherwise requires you to have tailed the target recently)"
131
+ }),
132
+ handler: async (argv) => {
133
+ const question = argv.question.map(String).join(" ").trim();
134
+ if (!question) fail("usage: ay ask <agent> \"<question>\"");
135
+ const target = await deps.resolveAgent(argv.agent);
136
+ const answerer = target.agent_id;
137
+ if (!answerer) fail(`agent ${target.pid} has no stable agent id, so an answer could not be attributed to it (it predates agent-id registration — restart it, or use \`ay send\` instead).`);
138
+ const root = await resolveTodoRoot(argv.root);
139
+ const self = await (deps.self ?? resolveSelf)();
140
+ const store = await openStore(root);
141
+ const task = await store.create({
142
+ summary: summarize(question),
143
+ kind: "question",
144
+ description: question,
145
+ owner: self?.agentId,
146
+ acceptanceCriteria: `${answerer} answers the question (\`ay answer\`), and the asker reads it.`,
147
+ tags: argv.tag ?? []
148
+ });
149
+ await store.setBlock(task._id, {
150
+ type: "waiting-on-answer",
151
+ agentId: answerer,
152
+ question
153
+ });
154
+ const body = buildAskEnvelope({
155
+ question,
156
+ taskId: task._id,
157
+ root,
158
+ asker: self ? {
159
+ cli: self.cli,
160
+ cwd: self.cwd,
161
+ pid: self.pid
162
+ } : null
163
+ });
164
+ let delivered = true;
165
+ try {
166
+ delivered = await deps.send([
167
+ String(target.pid),
168
+ body,
169
+ "--raw",
170
+ ...argv.force ? ["--force"] : []
171
+ ]) === 0;
172
+ } catch (err) {
173
+ delivered = false;
174
+ process.stderr.write(`warning: could not deliver the question to ${answerer}: ${err instanceof Error ? err.message : String(err)}\n`);
175
+ }
176
+ if (!delivered) process.stderr.write(`warning: ${task._id} was recorded but NOT delivered. If this was the recency guard, confirm the target first (\`ay tail ${target.pid}\`) then re-ask, or re-run with --force.\n`);
177
+ out(`asked ${answerer} → ${task._id}${delivered ? "" : " (NOT DELIVERED)"}`);
178
+ out(` question: ${summarize(question)}`);
179
+ out(` monitor the answer: ay todo get ${task._id} --root ${shellQuote(root)}`);
180
+ out(` monitor the answerer: ay status ${answerer}`);
181
+ out(` both at once: ay todo ls --blocked --root ${shellQuote(root)}`);
182
+ }
183
+ });
184
+ const answerCmd = (deps) => ({
185
+ command: "$0 <task> <answer..>",
186
+ describe: "answer a question asked via `ay ask`",
187
+ builder: (y) => y.positional("task", {
188
+ type: "string",
189
+ demandOption: true,
190
+ describe: "task id from the ask"
191
+ }).positional("answer", {
192
+ type: "string",
193
+ array: true,
194
+ demandOption: true,
195
+ describe: "the answer (unquoted words are joined)"
196
+ }),
197
+ handler: async (argv) => {
198
+ const answer = argv.answer.map(String).join(" ").trim();
199
+ if (!answer) fail("usage: ay answer <task> \"<answer>\"");
200
+ const root = await resolveTodoRoot(argv.root);
201
+ const store = await openStore(root);
202
+ const task = store.get(argv.task);
203
+ if (!task) fail(`no such task in ${root}: ${argv.task}`);
204
+ if (task.kind !== "question") fail(`task ${argv.task} is a "${task.kind}" task, not a question from \`ay ask\``);
205
+ const self = await (deps.self ?? resolveSelf)();
206
+ const validator = self?.agentId ?? "human";
207
+ await store.approve(task._id, "answer-received", validator, { note: answer });
208
+ const answered = await store.transition(task._id, "answered");
209
+ await store.setBlock(task._id, null);
210
+ let notified = false;
211
+ if (answered.owner) {
212
+ const body = buildAnswerEnvelope({
213
+ answer,
214
+ taskId: task._id,
215
+ root,
216
+ answerer: self ? {
217
+ cli: self.cli,
218
+ cwd: self.cwd,
219
+ pid: self.pid
220
+ } : null
221
+ });
222
+ try {
223
+ const asker = await deps.resolveAgent(answered.owner);
224
+ notified = await deps.send([
225
+ String(asker.pid),
226
+ body,
227
+ "--raw",
228
+ "--force"
229
+ ]) === 0;
230
+ } catch {
231
+ notified = false;
232
+ }
233
+ }
234
+ out(`answered ${task._id} (validator: ${validator}) → ${answered.state}`);
235
+ if (answered.owner) out(notified ? ` told the asker: ${answered.owner}` : ` could NOT reach the asker (${answered.owner}) — the answer is recorded on the task: ay todo get ${task._id} --root ${shellQuote(root)}`);
236
+ out(` the asker closes it with: ay todo transition ${task._id} done --root ${shellQuote(root)}`);
237
+ }
238
+ });
239
+ /** Shared by both verbs: `--root` mirrors `ay todo`'s, resolved the same way. */
240
+ function withGlobals(y, scriptName) {
241
+ return y.scriptName(scriptName).option("root", {
242
+ type: "string",
243
+ defaultDescription: "the repo's common root (shared across worktrees), else cwd",
244
+ describe: "project root holding .agent-yes/todos.jsonl",
245
+ global: true
246
+ }).check((argv) => {
247
+ if (argv.root === "") fail("--root must not be empty");
248
+ return true;
249
+ }).strict().help().version(false).exitProcess(false).fail((msg, err) => {
250
+ throw err ?? new Error(msg);
251
+ });
252
+ }
253
+ async function runAskSubcommand(rest, deps) {
254
+ if (rest.length === 0) {
255
+ withGlobals(yargs([]), "ay ask").command(askCmd(deps)).showHelp((s) => out(s));
256
+ return 0;
257
+ }
258
+ await withGlobals(yargs(rest), "ay ask").command(askCmd(deps)).parseAsync();
259
+ return 0;
260
+ }
261
+ async function runAnswerSubcommand(rest, deps) {
262
+ if (rest.length === 0) {
263
+ withGlobals(yargs([]), "ay answer").command(answerCmd(deps)).showHelp((s) => out(s));
264
+ return 0;
265
+ }
266
+ await withGlobals(yargs(rest), "ay answer").command(answerCmd(deps)).parseAsync();
267
+ return 0;
268
+ }
269
+
270
+ //#endregion
271
+ export { runAnswerSubcommand, runAskSubcommand };
272
+ //# sourceMappingURL=askCli-Bbxt9Umz.js.map
@@ -13565,4 +13565,4 @@ const XTERM_CSS = `
13565
13565
 
13566
13566
  //#endregion
13567
13567
  export { AyTerminal as t };
13568
- //# sourceMappingURL=browser-DSduVZeZ.js.map
13568
+ //# sourceMappingURL=browser-CorYp8Yu.js.map
@@ -1,7 +1,7 @@
1
- import { d as randomHex } from "./e2e-CUMZC53I.js";
2
- import { d as parseChannelLink, f as secretFromTopic, o as deriveChannelId, s as deriveRoom } from "./channels-yE1YUHCl.js";
3
- import { a as renderThread, c as makeOp, f as hlcSend, n as maxHlc, o as sortOps, r as mergeOps, s as isValidOp } from "./store-BW5NBouz.js";
4
- import { t as ChannelPeer } from "./peer-DIl5xtx8.js";
1
+ import { d as randomHex } from "./e2e-OLfcNxiv.js";
2
+ import { d as parseChannelLink, f as secretFromTopic, o as deriveChannelId, s as deriveRoom } from "./channels-CkaCyvEp.js";
3
+ import { a as renderThread, c as makeOp, f as hlcSend, n as maxHlc, o as sortOps, r as mergeOps, s as isValidOp } from "./store-C7I2CAu9.js";
4
+ import { t as ChannelPeer } from "./peer-Dr4zvtcd.js";
5
5
 
6
6
  //#region ts/channels/store.browser.ts
7
7
  const PREFIX = "ay29ch:";
@@ -308,4 +308,4 @@ const WIDGET_HTML = `
308
308
 
309
309
  //#endregion
310
310
  export { AyChannel as t };
311
- //# sourceMappingURL=browser-B7eYrBy7.js.map
311
+ //# sourceMappingURL=browser-_2ndf80G.js.map
@@ -0,0 +1,13 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import "./globalPidIndex-Dq4KY2IV.js";
3
+ import "./messageLog-BYFwioFY.js";
4
+ import "./e2e-OLfcNxiv.js";
5
+ import "./configShared-DYxlvrH0.js";
6
+ import "./subcommands-03ghe-aZ.js";
7
+ import "./notifyStore-Dus2D1XA.js";
8
+ import "./webrtcLink-B72AQ2eb.js";
9
+ import "./remotes-IzQHupeo.js";
10
+ import "./callbackCore-Bbe7rpN2.js";
11
+ import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-DI2r3NrV.js";
12
+
13
+ export { cmdCallback };
@@ -1,6 +1,6 @@
1
1
  import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.js";
2
- import { B as resolveOne } from "./subcommands-DJpVXIb6.js";
3
- import { a as mintCapability, o as parseExpires } from "./callbackCore-K99x3C75.js";
2
+ import { B as resolveOne } from "./subcommands-03ghe-aZ.js";
3
+ import { a as mintCapability, o as parseExpires } from "./callbackCore-Bbe7rpN2.js";
4
4
  import { randomBytes } from "node:crypto";
5
5
  import { chmodSync, readFileSync, renameSync, writeFileSync } from "node:fs";
6
6
  import path from "node:path";
@@ -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-BqRNIxeq.js");
75
+ const { resolveLocalServeUrl } = await import("./serve-BaHCO0bU.js");
76
76
  const url = await resolveLocalServeUrl();
77
77
  if (url) return url;
78
78
  } catch {}
@@ -170,7 +170,7 @@ async function cmdCallback(rest) {
170
170
  createdAt: Date.now()
171
171
  };
172
172
  saveStore(all);
173
- const { buildSnippet } = await import("./callbackCore-DcptaZwH.js");
173
+ const { buildSnippet } = await import("./callbackCore-C0Cg9RbS.js");
174
174
  w(`callback ${id} → ${record.cli} #${record.pid} @ ${record.cwd}`);
175
175
  w(`expires: ${new Date(exp).toISOString()} (${expiresSpec})`);
176
176
  w(`endpoint: ${base.replace(/\/+$/, "")}/cb/${cap}`);
@@ -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-Dlqwh9n7.js.map
190
+ //# sourceMappingURL=callback-DI2r3NrV.js.map
@@ -140,4 +140,4 @@ function buildSnippet(opts) {
140
140
 
141
141
  //#endregion
142
142
  export { mintCapability as a, frameVisitorMessage as i, MAX_EXPIRES_MS as n, parseExpires as o, buildSnippet as r, verifyCapability as s, MAX_CALLBACK_MSG_BYTES as t };
143
- //# sourceMappingURL=callbackCore-K99x3C75.js.map
143
+ //# sourceMappingURL=callbackCore-Bbe7rpN2.js.map
@@ -1,3 +1,3 @@
1
- import { a as mintCapability, i as frameVisitorMessage, n as MAX_EXPIRES_MS, o as parseExpires, r as buildSnippet, s as verifyCapability, t as MAX_CALLBACK_MSG_BYTES } from "./callbackCore-K99x3C75.js";
1
+ import { a as mintCapability, i as frameVisitorMessage, n as MAX_EXPIRES_MS, o as parseExpires, r as buildSnippet, s as verifyCapability, t as MAX_CALLBACK_MSG_BYTES } from "./callbackCore-Bbe7rpN2.js";
2
2
 
3
3
  export { buildSnippet };
@@ -1,6 +1,6 @@
1
- import "./e2e-CUMZC53I.js";
2
- import { c as formatChannelLink, d as parseChannelLink, f as secretFromTopic, l as formatChannelWebLink, o as deriveChannelId, s as deriveRoom, u as isChannelLink } from "./channels-yE1YUHCl.js";
3
- import { a as renderThread, c as makeOp, f as hlcSend, n as maxHlc, o as sortOps, r as mergeOps, s as isValidOp } from "./store-BW5NBouz.js";
1
+ import "./e2e-OLfcNxiv.js";
2
+ import { c as formatChannelLink, d as parseChannelLink, f as secretFromTopic, l as formatChannelWebLink, o as deriveChannelId, s as deriveRoom, u as isChannelLink } from "./channels-CkaCyvEp.js";
3
+ import { a as renderThread, c as makeOp, f as hlcSend, n as maxHlc, o as sortOps, r as mergeOps, s as isValidOp } from "./store-C7I2CAu9.js";
4
4
  import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
5
5
  import os from "os";
6
6
  import path from "path";
@@ -386,8 +386,8 @@ function printOp(op) {
386
386
  * `extraStdin` (pipe mode) drives it. Returns the started peer + a stop fn.
387
387
  */
388
388
  async function runMesh(cwd, entry, opts) {
389
- const { ChannelPeer } = await import("./peer-Cvntlw1i.js");
390
- const { importRTC, getIceServers } = await import("./share-Db_zb6gH.js");
389
+ const { ChannelPeer } = await import("./peer-u1AUAZeA.js");
390
+ const { importRTC, getIceServers } = await import("./share-DTPZgbfH.js");
391
391
  const rtc = await importRTC();
392
392
  const seen = new Set((await readOps(cwd, entry.channelId)).map((o) => o.id));
393
393
  const peer = new ChannelPeer({
@@ -567,4 +567,4 @@ async function cmdCh(args) {
567
567
 
568
568
  //#endregion
569
569
  export { cmdCh };
570
- //# sourceMappingURL=channels-DQaZbare.js.map
570
+ //# sourceMappingURL=channels-3gz9gHP7.js.map
@@ -1,4 +1,4 @@
1
- import { m as validateS, u as parseSecret } from "./e2e-CUMZC53I.js";
1
+ import { m as validateS, u as parseSecret } from "./e2e-OLfcNxiv.js";
2
2
 
3
3
  //#region ts/channels/link.ts
4
4
  const CH_DEFAULT_SIGHOST = "s.agent-yes.com";
@@ -134,4 +134,4 @@ function formatUntrustedInbound(op, opts) {
134
134
 
135
135
  //#endregion
136
136
  export { CH_DEFAULT_SIGHOST as a, formatChannelLink as c, parseChannelLink as d, secretFromTopic as f, parseCmd as i, formatChannelWebLink as l, isActionableCmd as n, deriveChannelId as o, isEphemeral as r, deriveRoom as s, formatUntrustedInbound as t, isChannelLink as u };
137
- //# sourceMappingURL=channels-yE1YUHCl.js.map
137
+ //# sourceMappingURL=channels-CkaCyvEp.js.map
package/dist/channels.js CHANGED
@@ -1,7 +1,7 @@
1
- import "./e2e-CUMZC53I.js";
2
- import { a as CH_DEFAULT_SIGHOST, c as formatChannelLink, d as parseChannelLink, f as secretFromTopic, i as parseCmd, l as formatChannelWebLink, n as isActionableCmd, o as deriveChannelId, r as isEphemeral, s as deriveRoom, t as formatUntrustedInbound, u as isChannelLink } from "./channels-yE1YUHCl.js";
3
- import { a as renderThread, c as makeOp, d as formatHlc, f as hlcSend, i as opsMissing, l as opId, n as maxHlc, o as sortOps, p as parseHlc, r as mergeOps, s as isValidOp, t as haveVector, u as compareHlc } from "./store-BW5NBouz.js";
4
- import "./peer-DIl5xtx8.js";
5
- import { t as AyChannel } from "./browser-B7eYrBy7.js";
1
+ import "./e2e-OLfcNxiv.js";
2
+ import { a as CH_DEFAULT_SIGHOST, c as formatChannelLink, d as parseChannelLink, f as secretFromTopic, i as parseCmd, l as formatChannelWebLink, n as isActionableCmd, o as deriveChannelId, r as isEphemeral, s as deriveRoom, t as formatUntrustedInbound, u as isChannelLink } from "./channels-CkaCyvEp.js";
3
+ import { a as renderThread, c as makeOp, d as formatHlc, f as hlcSend, i as opsMissing, l as opId, n as maxHlc, o as sortOps, p as parseHlc, r as mergeOps, s as isValidOp, t as haveVector, u as compareHlc } from "./store-C7I2CAu9.js";
4
+ import "./peer-Dr4zvtcd.js";
5
+ import { t as AyChannel } from "./browser-_2ndf80G.js";
6
6
 
7
7
  export { AyChannel, AyChannel as default, CH_DEFAULT_SIGHOST, compareHlc, deriveChannelId, deriveRoom, formatChannelLink, formatChannelWebLink, formatHlc, formatUntrustedInbound, haveVector, hlcSend, isActionableCmd, isChannelLink, isEphemeral, isValidOp, makeOp, maxHlc, mergeOps, opId, opsMissing, parseChannelLink, parseCmd, parseHlc, renderThread, secretFromTopic, sortOps };
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-ZQYirfMp.js";
5
- import { r as getRustBinary } from "./rustBinary-B9FEbqTu.js";
4
+ import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-V6ceVsu9.js";
5
+ import { r as getRustBinary } from "./rustBinary-B2lNFJSe.js";
6
6
  import { argv } from "process";
7
7
  import { spawn } from "child_process";
8
8
  import ms from "ms";
@@ -295,7 +295,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
295
295
  const rawArg = process.argv[2];
296
296
  const managerCommands = !invokedCliName(process.argv);
297
297
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
298
- const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken, isBareManagerInvocation } = await import("./subcommands-DLrJfG4D.js");
298
+ const { isSubcommand, runSubcommand, cmdHelp, isUnknownManagerToken, isBareManagerInvocation } = await import("./subcommands-BS4lnTrb.js");
299
299
  if (isHelpFlag && process.argv.length === 3) {
300
300
  await cmdHelp(managerCommands);
301
301
  process.exit(0);
@@ -309,7 +309,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
309
309
  process.exit(code ?? 0);
310
310
  }
311
311
  {
312
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DDs0xUvV.js");
312
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CdyjhpZP.js");
313
313
  if (isUnknownManagerToken(rawArg, managerCommands, SUPPORTED_CLIS)) {
314
314
  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`);
315
315
  process.exit(1);
@@ -317,7 +317,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
317
317
  }
318
318
  }
319
319
  {
320
- const { detectCwdPassthrough, SUPPRESS_CWD_WARN_ENV } = await import("./cwdPassthroughHint-BlRuOGo_.js");
320
+ const { detectCwdPassthrough, SUPPRESS_CWD_WARN_ENV } = await import("./cwdPassthroughHint-BlWO-h5w.js");
321
321
  const dep = detectCwdPassthrough(process.argv);
322
322
  if (dep) {
323
323
  console.warn(dep.message);
@@ -328,16 +328,16 @@ await checkAndAutoUpdate();
328
328
  logger.info(versionString());
329
329
  const config = parseCliArgs(process.argv);
330
330
  if (config.tray) {
331
- const { startTray } = await import("./tray-CzGs9-ch.js");
331
+ const { startTray } = await import("./tray-Bxq_sfvU.js");
332
332
  await startTray();
333
333
  await new Promise(() => {});
334
334
  }
335
335
  {
336
- const { ensureTray } = await import("./tray-CzGs9-ch.js");
336
+ const { ensureTray } = await import("./tray-Bxq_sfvU.js");
337
337
  ensureTray();
338
338
  }
339
339
  if (!config.showVersion && !config.appendPrompt && !config.tray) {
340
- const { waitForSpawnCapacity } = await import("./spawnGate-CZlMs6uV.js");
340
+ const { waitForSpawnCapacity } = await import("./spawnGate-C6YKlnmb.js");
341
341
  await waitForSpawnCapacity({
342
342
  onWait: (reason, waitedMs) => {
343
343
  if (config.verbose || waitedMs === 0) console.error(`[agent-yes] spawn gate: ${reason} — waiting…`);
@@ -346,7 +346,7 @@ if (!config.showVersion && !config.appendPrompt && !config.tray) {
346
346
  });
347
347
  }
348
348
  if (!config.showVersion && !config.appendPrompt && !config.tray) {
349
- const { warnIfCwdOccupied } = await import("./cwdConflictWarn-BpweoAiy.js");
349
+ const { warnIfCwdOccupied } = await import("./cwdConflictWarn-Buy3r_qc.js");
350
350
  await warnIfCwdOccupied(process.argv);
351
351
  }
352
352
  if (config.useRust) {
@@ -360,14 +360,14 @@ if (config.useRust) {
360
360
  }
361
361
  }
362
362
  if (rustBinary) {
363
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DDs0xUvV.js");
363
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CdyjhpZP.js");
364
364
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
365
365
  if (config.verbose) {
366
366
  console.log(`[rust] Using binary: ${rustBinary}`);
367
367
  console.log(`[rust] Args: ${rustArgs.join(" ")}`);
368
368
  }
369
369
  const attach = config.attach || process.env.AGENT_YES_ATTACH === "1";
370
- const { shouldForkNested, buildSpawnTutorial, waitForRegistration, confirmAgentStarted, predictedLogPath } = await import("./forkNested-B_4c-TQk.js");
370
+ const { shouldForkNested, buildSpawnTutorial, waitForRegistration, confirmAgentStarted, predictedLogPath } = await import("./forkNested-jc2ER9xn.js");
371
371
  if (shouldForkNested({
372
372
  isTTY: Boolean(process.stdout.isTTY),
373
373
  ayPid: process.env.AGENT_YES_PID,