agent-yes 1.262.0 → 1.263.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/{SUPPORTED_CLIS-BFLGbQmm.js → SUPPORTED_CLIS--5E9wO4L.js} +3 -3
  2. package/dist/{SUPPORTED_CLIS-CjKxvhlh.js → SUPPORTED_CLIS-DjFlWHpg.js} +2 -2
  3. package/dist/{agentShare-LEETLOm1.js → agentShare-BP6wiu_5.js} +2 -2
  4. package/dist/{callback-lqCCgSy1.js → callback-B0PQUZcK.js} +2 -2
  5. package/dist/{callback-CB3UjryF.js → callback-DFUFLIZQ.js} +3 -3
  6. package/dist/cli.js +5 -5
  7. package/dist/index.js +2 -2
  8. package/dist/{notifyDaemon-P6Q7Eo_B.js → notifyDaemon-BskLhHdg.js} +2 -2
  9. package/dist/{rustBinary-_qRGhAa5.js → rustBinary-Borvwmya.js} +2 -2
  10. package/dist/{schedule-ChAdx2h6.js → schedule-BSEIVKQ0.js} +4 -4
  11. package/dist/{serve-DhtgWGZB.js → serve-BEdLsSpv.js} +159 -17
  12. package/dist/{setup-BlJRaODn.js → setup-DFRvZ_ku.js} +2 -2
  13. package/dist/subcommands-B1YlmRaZ.js +10 -0
  14. package/dist/{subcommands-Zk3brdKt.js → subcommands-Bo1MSwuC.js} +78 -17
  15. package/dist/{terminal-BVWhnQUf.js → terminal-BMQWojIp.js} +2 -2
  16. package/dist/{trayApp-DtThjl0_.js → trayApp-CjB5QhdV.js} +2 -2
  17. package/dist/trayApp-DrdktFnW.js +5 -0
  18. package/dist/{ts-BqaOPFCQ.js → ts-BSHR_dga.js} +2 -2
  19. package/dist/{versionChecker-D-WLKAN6.js → versionChecker-BTLZ3PAb.js} +2 -2
  20. package/dist/{widget-CJyd9t8k.js → widget-BVss0Z9Y.js} +3 -3
  21. package/dist/{ws-mG4yeZu8.js → ws-DMudFezi.js} +2 -2
  22. package/dist/{ws-Vy3mBjNm.js → ws-DxNNwnrc.js} +2 -2
  23. package/lab/ui/index.html +101 -36
  24. package/package.json +2 -1
  25. package/scripts/deepseek-codex.ts +580 -0
  26. package/ts/serve.spec.ts +36 -0
  27. package/ts/serve.ts +144 -1
  28. package/ts/subcommands.ts +109 -1
  29. package/dist/subcommands-D6O9uRvN.js +0 -10
  30. package/dist/trayApp-C6U7Hghi.js +0 -5
@@ -6,6 +6,7 @@ import { a as recordOutbox, i as recordMessage, n as readMailbox, t as partyMatc
6
6
  import { t as loadSharedCliDefaults } from "./configShared-DtHMBCl7.js";
7
7
  import { n as isWebrtcSpec } from "./webrtcLink-CS4D-3G1.js";
8
8
  import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-Cn-wszbK.js";
9
+ import { spawn } from "child_process";
9
10
  import ms from "ms";
10
11
  import yargs from "yargs";
11
12
  import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
@@ -13,6 +14,7 @@ import { homedir } from "os";
13
14
  import path from "path";
14
15
  import { randomUUID } from "node:crypto";
15
16
  import os$1 from "node:os";
17
+ import { fileURLToPath } from "node:url";
16
18
  import { randomBytes as randomBytes$1 } from "crypto";
17
19
 
18
20
  //#region ts/agentTree.ts
@@ -1318,6 +1320,8 @@ const SUBCOMMANDS = new Set([
1318
1320
  "expose",
1319
1321
  "callback",
1320
1322
  "reap",
1323
+ "deepseek",
1324
+ "ds",
1321
1325
  "help"
1322
1326
  ]);
1323
1327
  const MANAGER_SUBCOMMANDS = new Set(["setup", "ws"]);
@@ -1359,6 +1363,28 @@ function isUnknownManagerToken(rawArg, managerCommands, supportedClis) {
1359
1363
  return !supportedClis.includes(rawArg);
1360
1364
  }
1361
1365
  /**
1366
+ * Write to stdout and wait until it has actually been handed off.
1367
+ *
1368
+ * The CLI ends with `process.exit()`, which DISCARDS bytes still sitting in the
1369
+ * pipe buffer. Writing to a file completes synchronously so this never showed
1370
+ * there, but any consumer that PIPES us silently lost everything past 64KiB.
1371
+ * Measured on `ay ls --json` with a large fleet (symval CTO, 2026-08-05):
1372
+ *
1373
+ * ay ls --json > file 118055 bytes, valid JSON
1374
+ * ay ls --json | consumer 65536 bytes, cut mid-multibyte — will not parse
1375
+ *
1376
+ * The truncation is invisible to the caller: it looks exactly like a small
1377
+ * fleet, and an orchestrator that parses this reported 27 of 71 agents.
1378
+ *
1379
+ * Only capturing the REAL write's completion works. Probing afterwards with an
1380
+ * empty `write("")` — by return value, by drain event, or by callback — reports
1381
+ * ready while the big write is still queued (all three verified failing), so do
1382
+ * not "simplify" this into a flush helper at the exit site.
1383
+ */
1384
+ async function writeStdoutFlushed(text) {
1385
+ if (process.stdout.write(text) === false) await Promise.race([new Promise((resolve) => process.stdout.once("drain", () => resolve())), new Promise((resolve) => setTimeout(resolve, 2e3))]);
1386
+ }
1387
+ /**
1362
1388
  * Top-level entry. Returns the desired process exit code, or null if argv
1363
1389
  * is not a subcommand invocation.
1364
1390
  */
@@ -1402,35 +1428,35 @@ async function runSubcommand(argv) {
1402
1428
  return cmdCh(rest);
1403
1429
  }
1404
1430
  case "term": {
1405
- const { cmdTerm } = await import("./terminal-BVWhnQUf.js");
1431
+ const { cmdTerm } = await import("./terminal-BMQWojIp.js");
1406
1432
  return cmdTerm(rest);
1407
1433
  }
1408
1434
  case "widget": {
1409
- const { cmdWidget } = await import("./widget-CJyd9t8k.js");
1435
+ const { cmdWidget } = await import("./widget-BVss0Z9Y.js");
1410
1436
  return cmdWidget(rest);
1411
1437
  }
1412
1438
  case "mint": {
1413
- const { cmdMint } = await import("./widget-CJyd9t8k.js");
1439
+ const { cmdMint } = await import("./widget-BVss0Z9Y.js");
1414
1440
  return cmdMint(rest);
1415
1441
  }
1416
1442
  case "serve": {
1417
- const { cmdServe } = await import("./serve-DhtgWGZB.js");
1443
+ const { cmdServe } = await import("./serve-BEdLsSpv.js");
1418
1444
  return cmdServe(rest);
1419
1445
  }
1420
1446
  case "tray": {
1421
- const { cmdTray } = await import("./trayApp-C6U7Hghi.js");
1447
+ const { cmdTray } = await import("./trayApp-DrdktFnW.js");
1422
1448
  return cmdTray(rest);
1423
1449
  }
1424
1450
  case "setup": {
1425
- const { cmdSetup } = await import("./setup-BlJRaODn.js");
1451
+ const { cmdSetup } = await import("./setup-DFRvZ_ku.js");
1426
1452
  return cmdSetup(rest);
1427
1453
  }
1428
1454
  case "ws": {
1429
- const { cmdWs } = await import("./ws-mG4yeZu8.js");
1455
+ const { cmdWs } = await import("./ws-DMudFezi.js");
1430
1456
  return cmdWs(rest);
1431
1457
  }
1432
1458
  case "schedule": {
1433
- const { cmdSchedule } = await import("./schedule-ChAdx2h6.js");
1459
+ const { cmdSchedule } = await import("./schedule-BSEIVKQ0.js");
1434
1460
  return cmdSchedule(rest);
1435
1461
  }
1436
1462
  case "remote": {
@@ -1442,12 +1468,14 @@ async function runSubcommand(argv) {
1442
1468
  return cmdExpose(rest);
1443
1469
  }
1444
1470
  case "callback": {
1445
- const { cmdCallback } = await import("./callback-lqCCgSy1.js");
1471
+ const { cmdCallback } = await import("./callback-B0PQUZcK.js");
1446
1472
  return cmdCallback(rest);
1447
1473
  }
1448
1474
  case "reap":
1449
1475
  await (await import("./reaper-B231i49s.js")).sweep();
1450
1476
  return 0;
1477
+ case "deepseek":
1478
+ case "ds": return cmdDeepseek(rest);
1451
1479
  case "help": return cmdHelp(managerCommands);
1452
1480
  default: return null;
1453
1481
  }
@@ -1458,6 +1486,37 @@ async function runSubcommand(argv) {
1458
1486
  }
1459
1487
  }
1460
1488
  /**
1489
+ * `ay deepseek [-- <args>…]` / `ay ds …`: run the local DeepSeek adapter that
1490
+ * bridges Codex's Responses API to DeepSeek's chat completions, then spawn
1491
+ * `codex` pointed at it. The adapter script lives in this package's
1492
+ * `scripts/deepseek-codex.ts` and is Bun-only (Bun.serve / Bun.spawn), so it is
1493
+ * re-exec'd via the same bun runtime that's running us — never imported into
1494
+ * this process. Extra args are forwarded verbatim to `codex`.
1495
+ */
1496
+ function cmdDeepseek(rest) {
1497
+ const root = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
1498
+ const script = path.join(root, "scripts", "deepseek-codex.ts");
1499
+ const child = spawn(process.execPath.split(/[/\\]/).at(-1)?.startsWith("bun") ? process.execPath : "bun", [script, ...rest], {
1500
+ cwd: process.cwd(),
1501
+ env: {
1502
+ ...process.env,
1503
+ AGENT_YES_BIN: process.argv[1]
1504
+ },
1505
+ stdio: "inherit"
1506
+ });
1507
+ return new Promise((resolve) => {
1508
+ child.on("error", (err) => {
1509
+ process.stderr.write(`ay deepseek: failed to start: ${err.message}\n`);
1510
+ resolve(1);
1511
+ });
1512
+ child.on("exit", (code, signal) => {
1513
+ if (signal) resolve(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
1514
+ else resolve(code ?? 1);
1515
+ });
1516
+ for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child.kill(signal));
1517
+ });
1518
+ }
1519
+ /**
1461
1520
  * The banner shown by `ay help` / `ay -h` when this process is itself running
1462
1521
  * inside an agent (`AGENT_YES_PID` set — see resolveSender). Answers the three
1463
1522
  * things a nested agent actually needs: who am I, who spawned me, and how do I
@@ -1480,7 +1539,7 @@ async function cmdHelp(managerCommands = true) {
1480
1539
  const wsLines = managerCommands ? " ay ws ls [--status] list <owner>/<repo>/tree/<branch> workspaces\n ay ws new <owner>/<repo>[@branch] clone/refresh a workspace (ay ws help for more)\n" : ``;
1481
1540
  const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
1482
1541
  const agentSection = self ? await buildAgentContextSection(self) : "";
1483
- process.stdout.write(agentSection + "ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message (keyword '.' = agent in this cwd)\n ay msgs [keyword] [--in|--out] inter-agent message log (sent + received)\n ay role [name] | ay role <kw> <name> lane/role name shown in the send envelope\n ay ch mk|join|send|read|tail <topic> local-first E2E channels: AI ↔ humans on a topic (ay ch help)\n ay term embed <pid> <script> to embed a live read-only agent terminal in a page (ay term help)\n ay widget ls | read selection|dom read an opted-in page widget's context (selection/DOM) (ay widget help)\n ay mint <target> --caps ... mint a scoped, short-TTL capability token (embeddable in a page)\n ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\n ay status <keyword> agent status snapshot\n ay whoami [--json] (inside an agent) your own registry identity + reply address\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n" + wsLines + "\nRemote:\n" + setupLine + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay callback --expires 7d mint an embeddable message-me widget for one agent\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
1542
+ process.stdout.write(agentSection + "ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message (keyword '.' = agent in this cwd)\n ay msgs [keyword] [--in|--out] inter-agent message log (sent + received)\n ay role [name] | ay role <kw> <name> lane/role name shown in the send envelope\n ay ch mk|join|send|read|tail <topic> local-first E2E channels: AI ↔ humans on a topic (ay ch help)\n ay term embed <pid> <script> to embed a live read-only agent terminal in a page (ay term help)\n ay widget ls | read selection|dom read an opted-in page widget's context (selection/DOM) (ay widget help)\n ay mint <target> --caps ... mint a scoped, short-TTL capability token (embeddable in a page)\n ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\n ay status <keyword> agent status snapshot\n ay whoami [--json] (inside an agent) your own registry identity + reply address\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n ay deepseek|ds [-- <codex args>] run codex via the local DeepSeek adapter (DEEPSEEK_API_KEY)\n" + wsLines + "\nRemote:\n" + setupLine + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay callback --expires 7d mint an embeddable message-me widget for one agent\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
1484
1543
  return 0;
1485
1544
  }
1486
1545
  function matchKeyword(record, keyword) {
@@ -2263,7 +2322,7 @@ async function cmdLs(rest) {
2263
2322
  ...r,
2264
2323
  ...await deriveLiveState(r)
2265
2324
  })));
2266
- process.stdout.write(JSON.stringify(enriched, null, 2) + "\n");
2325
+ await writeStdoutFlushed(JSON.stringify(enriched, null, 2) + "\n");
2267
2326
  return 0;
2268
2327
  }
2269
2328
  if (records.length === 0) {
@@ -3256,7 +3315,7 @@ async function cmdSend(rest) {
3256
3315
  default: false,
3257
3316
  alias: "no-wrap",
3258
3317
  description: "Send the body verbatim: omit the <ay-msg …> attribution wrapper that agent senders add by default (also: AGENT_YES_SEND_RAW=1)"
3259
- }).help(false).version(false).exitProcess(false).parseAsync();
3318
+ }).help(false).version(false).strictOptions().exitProcess(false).parseAsync();
3260
3319
  const opts = {
3261
3320
  all: argv.all,
3262
3321
  active: false,
@@ -3267,6 +3326,7 @@ async function cmdSend(rest) {
3267
3326
  const keyword = argv._[0] !== void 0 ? String(argv._[0]) : void 0;
3268
3327
  const rawMessage = argv._.slice(1).map(String).join(" ");
3269
3328
  if (!keyword) throw new Error("usage: ay send <keyword> <msg|-> [--code=enter|esc|ctrl-c|ctrl-y|tab|none]");
3329
+ if (rawMessage !== "-" && rawMessage.trim() === "") throw new Error("ay send: refusing to send an empty message. Pass the text as a single argument, or use `-` to read the body from stdin (e.g. `ay send <keyword> - < file.txt`).");
3270
3330
  const codeName = argv.code.toLowerCase();
3271
3331
  {
3272
3332
  const remote = await resolveRemoteSpec(keyword);
@@ -3282,6 +3342,7 @@ async function cmdSend(rest) {
3282
3342
  const chunks = [];
3283
3343
  for await (const chunk of process.stdin) chunks.push(chunk);
3284
3344
  body = Buffer.concat(chunks).toString("utf-8").trimEnd();
3345
+ if (body.trim() === "") throw new Error("ay send: refusing to send an empty message (stdin was empty).");
3285
3346
  } else body = rawMessage;
3286
3347
  const force = Boolean(argv.force) || process.env.AGENT_YES_FORCE_SEND === "1";
3287
3348
  const raw = Boolean(argv.raw) || process.env.AGENT_YES_SEND_RAW === "1";
@@ -3956,7 +4017,7 @@ async function cmdAttach(rest) {
3956
4017
  const winsizeDir = path.join(ayHome, "winsize");
3957
4018
  await mkdir(winsizeDir, { recursive: true });
3958
4019
  const winsizePath = path.join(winsizeDir, String(record.pid));
3959
- const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-DhtgWGZB.js");
4020
+ const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-BEdLsSpv.js");
3960
4021
  const daemonBase = await resolveDaemonHttpBase().catch(() => null);
3961
4022
  const daemonToken = daemonBase ? await loadTokenReadOnly().catch(() => null) ?? "" : "";
3962
4023
  const capViewer = `attach:${process.pid}`;
@@ -4666,7 +4727,7 @@ async function cmdNotify(rest) {
4666
4727
  }
4667
4728
  const ensure = async () => {
4668
4729
  if (!argv["ensure-daemon"]) return;
4669
- const { ensureDaemon } = await import("./notifyDaemon-P6Q7Eo_B.js");
4730
+ const { ensureDaemon } = await import("./notifyDaemon-BskLhHdg.js");
4670
4731
  await ensureDaemon().catch(() => null);
4671
4732
  };
4672
4733
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4740,7 +4801,7 @@ async function cmdNotifyCursor(args) {
4740
4801
  }
4741
4802
  async function cmdNotifyd(rest) {
4742
4803
  const sub = rest[0] ?? "status";
4743
- const daemon = await import("./notifyDaemon-P6Q7Eo_B.js");
4804
+ const daemon = await import("./notifyDaemon-BskLhHdg.js");
4744
4805
  switch (sub) {
4745
4806
  case "run": return daemon.runDaemon();
4746
4807
  case "once": return daemon.runDaemon({ once: true });
@@ -4766,5 +4827,5 @@ async function cmdNotifyd(rest) {
4766
4827
  }
4767
4828
 
4768
4829
  //#endregion
4769
- export { liveWatchers as $, readPtysize as A, runSubcommand as B, lastStdinAt as C, readAgentPtysize as D, menuSelectKeys as E, renderRawLogLines as F, waitForLogQuiet as G, stdinActivityPath as H, resolveOne as I, appendEvent as J, writeKeysPaced as K, resolveReadWindow as L, recentReadEdges as M, renderLogTailLines as N, readLogForRender as O, renderRawLog as P, listInboxParents as Q, resolveResumeArgs as R, isUserTyping as S, matchKeyword as T, stopTipForCli as U, snapshotStatus as V, submitAndConfirm as W, hostId as X, gcInboxes as Y, isTransientLockMkdirError as Z, isExitRequest as _, backoffWhileTyping as a, TYPING_BADGE as at, isSubcommand as b, cursorAbs as c, extractBadges as d, readInbox as et, extractMenu as f, isAgentStuck as g, finalizedLines as h, TYPING_WINDOW_MS as i, notifyDir as it, recentMessageEdges as j, readNotes as k, deriveLiveState as l, extractTaskCounts as m, MAX_RENDER_BYTES as n, daemonLockDir as nt, cmdHelp as o, extractNeedsInput as p, writeToIpc as q, READ_PAGE_DEFAULT as r, daemonLockOwnerPath as rt, controlCodeFromName as s, GRACEFUL_EXIT_COMMANDS as t, shouldStealLock as tt, deriveLiveStatus as u, isPidAlive as v, listRecords as w, isUnknownManagerToken as x, isSlashCommand as y, restartHintLines as z };
4770
- //# sourceMappingURL=subcommands-Zk3brdKt.js.map
4830
+ export { listInboxParents as $, readNotes as A, restartHintLines as B, isUserTyping as C, menuSelectKeys as D, matchKeyword as E, renderRawLog as F, submitAndConfirm as G, snapshotStatus as H, renderRawLogLines as I, writeToIpc as J, waitForLogQuiet as K, resolveOne as L, recentMessageEdges as M, recentReadEdges as N, readAgentPtysize as O, renderLogTailLines as P, isTransientLockMkdirError as Q, resolveReadWindow as R, isUnknownManagerToken as S, listRecords as T, stdinActivityPath as U, runSubcommand as V, stopTipForCli as W, gcInboxes as X, appendEvent as Y, hostId as Z, isAgentStuck as _, backoffWhileTyping as a, notifyDir as at, isSlashCommand as b, controlCodeFromName as c, deriveLiveStatus as d, liveWatchers as et, extractBadges as f, finalizedLines as g, extractTaskCounts as h, TYPING_WINDOW_MS as i, daemonLockOwnerPath as it, readPtysize as j, readLogForRender as k, cursorAbs as l, extractNeedsInput as m, MAX_RENDER_BYTES as n, shouldStealLock as nt, cmdDeepseek as o, TYPING_BADGE as ot, extractMenu as p, writeKeysPaced as q, READ_PAGE_DEFAULT as r, daemonLockDir as rt, cmdHelp as s, GRACEFUL_EXIT_COMMANDS as t, readInbox as tt, deriveLiveState as u, isExitRequest as v, lastStdinAt as w, isSubcommand as x, isPidAlive as y, resolveResumeArgs as z };
4831
+ //# sourceMappingURL=subcommands-Bo1MSwuC.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-DhtgWGZB.js");
101
+ const { mintScopedTermToken } = await import("./serve-BEdLsSpv.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-BVWhnQUf.js.map
138
+ //# sourceMappingURL=terminal-BMQWojIp.js.map
@@ -1,4 +1,4 @@
1
- import { n as findTrayLauncher } from "./rustBinary-_qRGhAa5.js";
1
+ import { n as findTrayLauncher } from "./rustBinary-Borvwmya.js";
2
2
  import { t as agentYesHome } from "./agentYesHome-CtmsY6I-.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-DtThjl0_.js.map
100
+ //# sourceMappingURL=trayApp-CjB5QhdV.js.map
@@ -0,0 +1,5 @@
1
+ import "./versionChecker-BTLZ3PAb.js";
2
+ import "./rustBinary-Borvwmya.js";
3
+ import { a as trayHiddenMarker, i as launchTray, n as hasDesktop, r as isTrayHidden, t as cmdTray } from "./trayApp-CjB5QhdV.js";
4
+
5
+ export { cmdTray };
@@ -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-D-WLKAN6.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-BTLZ3PAb.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-BQRW614S.js";
6
6
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DBoONoM-.js";
@@ -2181,4 +2181,4 @@ function sleep(ms) {
2181
2181
 
2182
2182
  //#endregion
2183
2183
  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 };
2184
- //# sourceMappingURL=ts-BqaOPFCQ.js.map
2184
+ //# sourceMappingURL=ts-BSHR_dga.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.262.0";
10
+ var version = "1.263.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -215,4 +215,4 @@ async function displayVersion() {
215
215
 
216
216
  //#endregion
217
217
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
218
- //# sourceMappingURL=versionChecker-D-WLKAN6.js.map
218
+ //# sourceMappingURL=versionChecker-BTLZ3PAb.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-DhtgWGZB.js");
31
+ const { resolveDaemonHttpBase, loadTokenReadOnly } = await import("./serve-BEdLsSpv.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-DhtgWGZB.js");
189
+ const { mintScopedTermToken } = await import("./serve-BEdLsSpv.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-CJyd9t8k.js.map
205
+ //# sourceMappingURL=widget-BVss0Z9Y.js.map
@@ -3,9 +3,9 @@ import "./globalPidIndex-pwp7Vb7o.js";
3
3
  import "./messageLog-BYFwioFY.js";
4
4
  import "./e2e-CUMZC53I.js";
5
5
  import "./configShared-DtHMBCl7.js";
6
- import "./subcommands-Zk3brdKt.js";
6
+ import "./subcommands-Bo1MSwuC.js";
7
7
  import "./webrtcLink-CS4D-3G1.js";
8
8
  import "./remotes-Cn-wszbK.js";
9
- 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-Vy3mBjNm.js";
9
+ 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-DxNNwnrc.js";
10
10
 
11
11
  export { WS_JSON_SCHEMA, cmdWs, collectWorkspaces, isPathInside, loadProvision, walkWorkspaces, workspaceStatus };
@@ -1,4 +1,4 @@
1
- import { w as listRecords } from "./subcommands-Zk3brdKt.js";
1
+ import { T as listRecords } from "./subcommands-Bo1MSwuC.js";
2
2
  import { a as getProvisionRoot } from "./workspaceConfig-Bxuk_MOq.js";
3
3
  import { existsSync } from "fs";
4
4
  import { lstat, opendir } from "fs/promises";
@@ -426,4 +426,4 @@ async function cmdWs(args) {
426
426
 
427
427
  //#endregion
428
428
  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 };
429
- //# sourceMappingURL=ws-Vy3mBjNm.js.map
429
+ //# sourceMappingURL=ws-DxNNwnrc.js.map
package/lab/ui/index.html CHANGED
@@ -7982,6 +7982,7 @@ my.translate = async (text, lang) => {
7982
7982
  const res = await target.tx.post("/api/spawn", {
7983
7983
  cli: spec.cli || "claude",
7984
7984
  from: spec.from || undefined,
7985
+ branch: spec.branch || undefined,
7985
7986
  create: spec.create || undefined,
7986
7987
  fork: spec.fork || undefined,
7987
7988
  cwd: spec.cwd || undefined,
@@ -8104,11 +8105,10 @@ my.translate = async (text, lang) => {
8104
8105
  $("newform").innerHTML = `<div class="lcard">
8105
8106
  <div class="ltitle">New agent · ${esc(sourceLabel(target) || "local")}</div>
8106
8107
  ${hostRow}
8107
- <div class="nfield"><label>Working dir</label><input id="nf-cwd" list="nf-cwd-list" value="${esc(cwd)}" placeholder="(host default — ignored when Spawn from is set)" spellcheck="false" autocapitalize="off" /><datalist id="nf-cwd-list">${cwdOptions(target ? target.id : "")}</datalist></div>
8108
+ <div class="nfield"><label>Working dir</label><input id="nf-cwd" list="nf-cwd-list" value="${esc(cwd)}" placeholder="(host default — ignored when a Provision repo is set)" spellcheck="false" autocapitalize="off" /><datalist id="nf-cwd-list">${cwdOptions(target ? target.id : "")}</datalist></div>
8108
8109
  <div class="nfield"><label>CLI</label><select id="nf-cli">${cliOptions(null, preferredCli)}</select></div>
8109
- <div class="nfield"><label>Provision repo — optional</label><select id="nf-repo"><option value="" selected>(noneuse working dir or spawn-from)</option></select></div>
8110
+ <div class="nfield"><label>Provision repo — optional</label><input id="nf-repo" list="nf-repo-list" placeholder="owner/repo · github URL · git URL (gitlab etc.) provisions a worktree" spellcheck="false" autocapitalize="off" /><datalist id="nf-repo-list"></datalist><div class="lhint" id="nf-repo-hint" style="display:none"></div></div>
8110
8111
  <div class="nfield" id="nf-branch-row" style="display:none"><label>Branch</label><input id="nf-branch" list="nf-branch-list" placeholder="(repo default) — pick one, or type a new name to create it" spellcheck="false" autocapitalize="off" /><datalist id="nf-branch-list"></datalist><div class="lhint" id="nf-branch-hint" style="display:none"></div></div>
8111
- <div class="nfield"><label>Spawn from — optional</label><input id="nf-from" placeholder="github URL / owner/repo@branch — provisions a worktree" spellcheck="false" autocapitalize="off" /></div>
8112
8112
  <div class="nfield"><label>Prompt — optional</label><textarea id="nf-prompt" rows="3" placeholder="initial message to send the agent…"></textarea></div>
8113
8113
  <div class="lrow">
8114
8114
  <button class="lfleet" id="nf-go">▷ Launch</button>
@@ -8127,13 +8127,7 @@ my.translate = async (text, lang) => {
8127
8127
  showHook(sources.get(ev.target.value));
8128
8128
  loadRepoPicker(sources.get(ev.target.value));
8129
8129
  });
8130
- $("nf-repo")?.addEventListener("change", () => {
8131
- const repo = $("nf-repo").value;
8132
- const row = $("nf-branch-row");
8133
- if (row) row.style.display = repo ? "" : "none";
8134
- if (repo) loadBranchPicker(spawnTarget($("newform").dataset.room), repo);
8135
- else nfRemoteBranches = null;
8136
- });
8130
+ $("nf-repo")?.addEventListener("change", onRepoChange);
8137
8131
  $("nf-branch-row")?.addEventListener("input", (ev) => {
8138
8132
  if (ev.target.id === "nf-branch") updateBranchHint();
8139
8133
  });
@@ -8141,15 +8135,39 @@ my.translate = async (text, lang) => {
8141
8135
  loadRepoPicker(target);
8142
8136
  }
8143
8137
 
8144
- // ---- provisioning picker (repo → branch) for the New-agent form -------
8145
- // Data comes from the target host's /api/ws/repos and /api/ws/branches.
8146
- // Both are best-effort on the server (gh may be unauthenticated); here a
8147
- // fetch failure just leaves the pickers empty the free-text "Spawn
8148
- // from" field still works.
8138
+ // ---- provisioning picker (repo combobox → branch) for the New-agent form
8139
+ // The repo field is a COMBOBOX: a datalist filled from the target host's
8140
+ // /api/ws/repos (local checkouts + gh-visible repos), but free text always
8141
+ // works owner/repo, a full github URL (repo or /tree/<branch> URL), or
8142
+ // any other git URL (gitlab etc. — the host raw-`git clone`s those).
8143
+ // The listing endpoints are best-effort; a fetch failure just leaves the
8144
+ // datalist empty and shows a short hint, manual input still provisions.
8149
8145
  let nfRemoteBranches = null; // Set of remote branch names for the picked repo
8146
+
8147
+ // What the repo field's text means. github-first: bare owner/repo IS a
8148
+ // github repo; only scheme/git@ inputs on other hosts become raw git URLs.
8149
+ function parseRepoInput(v) {
8150
+ v = (v || "").trim();
8151
+ if (!v) return null;
8152
+ let m =
8153
+ /^https?:\/\/github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?(?:\/tree\/([^\s?#]+))?\/?$/i.exec(
8154
+ v,
8155
+ );
8156
+ if (m) return { kind: "github", owner: m[1], repo: m[2], branch: m[3] || "" };
8157
+ m = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i.exec(v);
8158
+ if (m) return { kind: "github", owner: m[1], repo: m[2], branch: "" };
8159
+ m = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(v);
8160
+ if (m && m[1] !== "." && m[1] !== "..")
8161
+ return { kind: "github", owner: m[1], repo: m[2], branch: "" };
8162
+ if (/^(git@[^:\s]+:|ssh:\/\/|git:\/\/|https?:\/\/)\S+$/.test(v))
8163
+ return { kind: "git", url: v.replace(/\/+$/, "") };
8164
+ return null;
8165
+ }
8150
8166
  function loadRepoPicker(target) {
8151
- const selEl = $("nf-repo");
8152
- if (!selEl || !target?.tx?.fetchJSON) return;
8167
+ const dl = $("nf-repo-list");
8168
+ const hint = $("nf-repo-hint");
8169
+ if (!dl) return;
8170
+ if (!target?.tx?.fetchJSON) return;
8153
8171
  const forId = target.id;
8154
8172
  target.tx
8155
8173
  .fetchJSON("/api/ws/repos")
@@ -8157,18 +8175,49 @@ my.translate = async (text, lang) => {
8157
8175
  // a slow answer for a host the user switched away from must not
8158
8176
  // paint the new host's repo list (same guard as showHook)
8159
8177
  if ($("newform").dataset.room !== forId) return;
8160
- const cur = selEl.value;
8161
8178
  const repos = (d && d.repos) || [];
8162
- selEl.innerHTML =
8163
- `<option value="">(none — use working dir or spawn-from)</option>` +
8164
- repos
8165
- .map((r) => {
8166
- const full = `${r.owner}/${r.repo}`;
8167
- return `<option value="${esc(full)}"${full === cur ? " selected" : ""}>${esc(full)}${r.local ? "" : " (gh)"}</option>`;
8168
- })
8169
- .join("");
8179
+ dl.innerHTML = repos
8180
+ .map(
8181
+ (r) =>
8182
+ `<option value="${esc(`${r.owner}/${r.repo}`)}">${r.local ? "provisioned" : "gh"}</option>`,
8183
+ )
8184
+ .join("");
8185
+ if (hint && d && d.gh && !d.gh.ok && d.gh.error) {
8186
+ hint.textContent = `gh listing unavailable (${d.gh.error}) — type a repo or URL`;
8187
+ hint.style.display = "";
8188
+ } else if (hint) hint.style.display = "none";
8170
8189
  })
8171
- .catch(() => {});
8190
+ .catch(() => {
8191
+ if ($("newform").dataset.room !== forId) return;
8192
+ if (hint) {
8193
+ hint.textContent =
8194
+ "repo list unavailable on this host — type owner/repo or a git URL";
8195
+ hint.style.display = "";
8196
+ }
8197
+ });
8198
+ }
8199
+ function onRepoChange() {
8200
+ const parsed = parseRepoInput($("nf-repo")?.value || "");
8201
+ const row = $("nf-branch-row");
8202
+ const hint = $("nf-branch-hint");
8203
+ nfRemoteBranches = null;
8204
+ if (row) row.style.display = parsed ? "" : "none";
8205
+ if (!parsed) return;
8206
+ if (parsed.kind === "github") {
8207
+ if (parsed.branch && $("nf-branch")) $("nf-branch").value = parsed.branch;
8208
+ loadBranchPicker(
8209
+ spawnTarget($("newform").dataset.room),
8210
+ `${parsed.owner}/${parsed.repo}`,
8211
+ );
8212
+ } else {
8213
+ const dl = $("nf-branch-list");
8214
+ if (dl) dl.innerHTML = "";
8215
+ if (hint) {
8216
+ hint.textContent =
8217
+ "non-GitHub git URL — the host runs a raw `git clone`; branch optional (a new name creates it)";
8218
+ hint.style.display = "";
8219
+ }
8220
+ }
8172
8221
  }
8173
8222
  function loadBranchPicker(target, repo) {
8174
8223
  const dl = $("nf-branch-list");
@@ -8184,7 +8233,9 @@ my.translate = async (text, lang) => {
8184
8233
  target.tx
8185
8234
  .fetchJSON("/api/ws/branches?repo=" + encodeURIComponent(repo))
8186
8235
  .then((d) => {
8187
- if ($("nf-repo")?.value !== want) return; // user picked another repo meanwhile
8236
+ // user picked another repo meanwhile (value may be a URL — normalize)
8237
+ const cur = parseRepoInput($("nf-repo")?.value || "");
8238
+ if (!cur || cur.kind !== "github" || `${cur.owner}/${cur.repo}` !== want) return;
8188
8239
  const branches = (d && d.branches) || [];
8189
8240
  nfRemoteBranches = new Set(branches.filter((b) => b.remote).map((b) => b.name));
8190
8241
  if (dl)
@@ -8258,23 +8309,37 @@ my.translate = async (text, lang) => {
8258
8309
  try {
8259
8310
  localStorage.setItem(LAST_CLI_KEY, cli);
8260
8311
  } catch {}
8261
- // The repo/branch pickers win over the free-text "Spawn from" field:
8262
- // picked repo [+ branch] → a `from` spec; a branch name that isn't on
8263
- // the remote asks the server to create it (create:true, ws new --create).
8264
- let from = $("nf-from")?.value.trim() || "";
8312
+ // The repo combobox decides provisioning: a github repo [+ branch]
8313
+ // becomes a `from` spec (a branch name that isn't on the remote asks
8314
+ // the server to create it create:true, ws new --create); any other
8315
+ // git URL is sent as-is with the branch alongside (raw clone path).
8316
+ let from = "";
8317
+ let branch = "";
8265
8318
  let create = false;
8266
- const pickedRepo = $("nf-repo")?.value || "";
8267
- if (pickedRepo) {
8268
- const br = ($("nf-branch")?.value || "").trim();
8269
- from = br ? `${pickedRepo}@${br}` : pickedRepo;
8319
+ const parsed = parseRepoInput($("nf-repo")?.value || "");
8320
+ const br = ($("nf-branch")?.value || "").trim();
8321
+ if (parsed && parsed.kind === "github") {
8322
+ from = br ? `${parsed.owner}/${parsed.repo}@${br}` : `${parsed.owner}/${parsed.repo}`;
8270
8323
  // Unknown remote list (fetch failed) → still send create: the server
8271
8324
  // only uses it after the branch turns out missing, so it's harmless
8272
8325
  // for existing branches and saves an un-actionable 502 for new ones.
8273
8326
  create = !!(br && (!nfRemoteBranches || !nfRemoteBranches.has(br)));
8327
+ } else if (parsed && parsed.kind === "git") {
8328
+ from = parsed.url;
8329
+ branch = br;
8330
+ create = !!br;
8331
+ } else if ($("nf-repo")?.value.trim()) {
8332
+ // unparseable text must not silently fall through to a plain-cwd spawn
8333
+ $("nf-repo").focus();
8334
+ alert(
8335
+ "Provision repo not recognized — use owner/repo, a github URL, or a git URL (git@… / https://…)",
8336
+ );
8337
+ return;
8274
8338
  }
8275
8339
  const spec = {
8276
8340
  cli,
8277
8341
  from,
8342
+ branch,
8278
8343
  create,
8279
8344
  // a provision spec decides the cwd server-side — don't send a stale one
8280
8345
  cwd: from ? "" : $("nf-cwd").value.trim(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.262.0",
3
+ "version": "1.263.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
@@ -108,6 +108,7 @@
108
108
  "registry": "https://registry.npmjs.org/"
109
109
  },
110
110
  "scripts": {
111
+ "deepseek": "bun scripts/deepseek-codex.ts",
111
112
  "build": "tsdown",
112
113
  "check:e2e": "bun scripts/check-e2e.ts",
113
114
  "cf": "bun scripts/cf.ts",