toyon 0.17.0 → 0.18.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 (31) hide show
  1. package/dist/CHANGELOG.md +36 -0
  2. package/dist/cli.js +1 -1
  3. package/dist/daemon.js +612 -384
  4. package/dist/shell/assets/{Editor-BMBnZV2V.js → Editor-CU91PIeX.js} +131 -131
  5. package/dist/shell/assets/{XTerm-xxm4nCyN.js → XTerm-y7vQoOEu.js} +1 -1
  6. package/dist/shell/assets/{cssMode-DRRmIKjD.js → cssMode-B7sFzM6K.js} +1 -1
  7. package/dist/shell/assets/{freemarker2-BWXbPnFF.js → freemarker2-ChTLQoDC.js} +1 -1
  8. package/dist/shell/assets/{grammar-BlgE3B1J.js → grammar-xXcxTc3V.js} +1 -1
  9. package/dist/shell/assets/{handlebars-swdzvdWB.js → handlebars-DPJSozQx.js} +1 -1
  10. package/dist/shell/assets/{html-GPVhANjL.js → html-Cog2rLcD.js} +1 -1
  11. package/dist/shell/assets/{htmlMode-BTtuR8kD.js → htmlMode-Xm5_ujKb.js} +1 -1
  12. package/dist/shell/assets/index-BpW1Kufz.css +1 -0
  13. package/dist/shell/assets/index-CdY8WF4P.js +270 -0
  14. package/dist/shell/assets/{javascript-CFXn2iKE.js → javascript-Dcv6lw7b.js} +1 -1
  15. package/dist/shell/assets/{jsonMode-wmrRCrSu.js → jsonMode-D5_V0TW5.js} +1 -1
  16. package/dist/shell/assets/{liquid-Bz7Svg4V.js → liquid-C_zXIF5F.js} +1 -1
  17. package/dist/shell/assets/{lspLanguageFeatures-BDP4BowE.js → lspLanguageFeatures-CkiTdLmp.js} +1 -1
  18. package/dist/shell/assets/{mdx-CzERTPdi.js → mdx-gvbILQkr.js} +1 -1
  19. package/dist/shell/assets/{python-3OsSAVmo.js → python-CZTrOkh_.js} +1 -1
  20. package/dist/shell/assets/{razor-BKY4SJuy.js → razor-DowlBj1i.js} +1 -1
  21. package/dist/shell/assets/{tsMode-BESW693Z.js → tsMode-BNl2cV_K.js} +1 -1
  22. package/dist/shell/assets/{xml-SaTEQQ9V.js → xml-DIiuHFHw.js} +1 -1
  23. package/dist/shell/assets/{yaml-gHi84-nk.js → yaml-CBnMyn4j.js} +1 -1
  24. package/dist/shell/icon-192.png +0 -0
  25. package/dist/shell/icon-512.png +0 -0
  26. package/dist/shell/icon-maskable-512.png +0 -0
  27. package/dist/shell/index.html +94 -13
  28. package/dist/shell/manifest.json +19 -1
  29. package/package.json +1 -1
  30. package/dist/shell/assets/index-9aG0ri4Y.css +0 -1
  31. package/dist/shell/assets/index-miqqiN7Z.js +0 -269
package/dist/daemon.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // @bun
2
2
  // ../daemon/src/index.ts
3
- import { rmSync as rmSync9, writeFileSync as writeFileSync13 } from "fs";
3
+ import { rmSync as rmSync9, writeFileSync as writeFileSync14 } from "fs";
4
4
  import { homedir as homedir15 } from "os";
5
- import { dirname as dirname13, join as join32 } from "path";
5
+ import { dirname as dirname14, join as join33 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
 
8
8
  // ../shared/src/protocol/events.ts
@@ -71,6 +71,7 @@ function applyLog(lines, proc, line, retract = 0) {
71
71
  return out;
72
72
  return [...out, { proc, line }];
73
73
  }
74
+ var isBusy = (status) => status === "working" || status === "waiting";
74
75
  var KEEP_AWAKE_MODES = ["use", "always", "off"];
75
76
 
76
77
  // ../shared/src/worktree-caps.ts
@@ -6601,7 +6602,7 @@ ${text.slice(text.length - (CONTEXT_CHARS - HEAD_CHARS))}`;
6601
6602
  }
6602
6603
 
6603
6604
  // ../shared/src/protocol/limits.ts
6604
- var PROTOCOL_VERSION = 115;
6605
+ var PROTOCOL_VERSION = 119;
6605
6606
  var PAGE_ERROR_MAX_CHARS = 2000;
6606
6607
  var PAGE_TITLE_MAX_CHARS = 1000;
6607
6608
  var FILE_MAX_CHARS = 5000000;
@@ -7039,7 +7040,13 @@ var clientMsgSchema = discriminatedUnion("t", [
7039
7040
  attachments,
7040
7041
  boxId
7041
7042
  }),
7042
- object({ t: literal("agent-decide"), worktreeId: id, askId: string2().max(64), choiceId: string2().max(200) }),
7043
+ object({
7044
+ t: literal("agent-decide"),
7045
+ worktreeId: id,
7046
+ askId: string2().max(64),
7047
+ choiceId: string2().max(200),
7048
+ note: string2().max(1e4).optional()
7049
+ }),
7043
7050
  object({
7044
7051
  t: literal("handoff-answer"),
7045
7052
  worktreeId: id,
@@ -8519,7 +8526,7 @@ async function turnRemoteOff(ts, daemonPort, file, was) {
8519
8526
  // ../daemon/package.json
8520
8527
  var package_default = {
8521
8528
  name: "@toyon/daemon",
8522
- version: "0.17.0",
8529
+ version: "0.18.0",
8523
8530
  private: true,
8524
8531
  type: "module",
8525
8532
  main: "src/index.ts",
@@ -13765,7 +13772,8 @@ function sameLimits(a, b) {
13765
13772
  if (!a || !b)
13766
13773
  return a === b;
13767
13774
  const win = (x) => x ? `${x.used}@${x.resetsAt}` : "";
13768
- return a.status === b.status && a.binding === b.binding && win(a.windows.five_hour) === win(b.windows.five_hour) && win(a.windows.seven_day) === win(b.windows.seven_day);
13775
+ const keys = ["five_hour", "seven_day", "seven_day_overage_included"];
13776
+ return a.status === b.status && a.binding === b.binding && keys.every((k) => win(a.windows[k]) === win(b.windows[k]));
13769
13777
  }
13770
13778
  function same(a, b) {
13771
13779
  if (!a || !b)
@@ -15091,6 +15099,91 @@ function loadAgentRegistry(agentsFile, agentsDir, sandbox, managed = MANAGED_DEF
15091
15099
  // ../daemon/src/agent/acp/session.ts
15092
15100
  import { randomUUID } from "crypto";
15093
15101
 
15102
+ // ../daemon/src/agent/awsAuth.ts
15103
+ import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
15104
+ import { dirname as dirname4, join as join8 } from "path";
15105
+ var AWS_EXPIRED_RE = /could not load aws credentials|aws sso login|aws authentication failed|expiredtoken|security token included in the request is (expired|invalid)|sso (session|token)[^.]*expired|token is expired|awsauthrefresh/i;
15106
+ var AWS_LOGIN_METHOD = "aws-login";
15107
+ var AWS_REFRESH_METHOD = "aws-refresh";
15108
+ function readAwsAuth(configDir, cwd, env = process.env) {
15109
+ const files = [
15110
+ join8(cwd, ".claude", "settings.local.json"),
15111
+ join8(cwd, ".claude", "settings.json"),
15112
+ join8(configDir, "settings.json")
15113
+ ];
15114
+ let profile;
15115
+ let refresh;
15116
+ for (const file of files) {
15117
+ const s = readSettings(file);
15118
+ if (!s)
15119
+ continue;
15120
+ const e = s.env;
15121
+ profile ??= str2(e?.AWS_PROFILE);
15122
+ refresh ??= str2(s.awsAuthRefresh);
15123
+ }
15124
+ profile ??= str2(env.AWS_PROFILE);
15125
+ return { ...profile ? { profile } : {}, ...refresh ? { refresh } : {} };
15126
+ }
15127
+ function awsLoginCommand(profile) {
15128
+ return profile ? `aws sso login --profile ${shellWord(profile)}` : "aws sso login";
15129
+ }
15130
+ function awsMethods(auth, settable) {
15131
+ const login = {
15132
+ id: AWS_LOGIN_METHOD,
15133
+ name: "log in to AWS",
15134
+ description: `runs ${awsLoginCommand(auth.profile)} in the terminal pane`,
15135
+ kind: "terminal"
15136
+ };
15137
+ if (auth.refresh || !settable)
15138
+ return [login];
15139
+ return [
15140
+ {
15141
+ id: AWS_REFRESH_METHOD,
15142
+ name: "refresh automatically",
15143
+ description: "sets awsAuthRefresh in your Claude settings, so Claude runs the login itself when the session expires",
15144
+ kind: "agent"
15145
+ },
15146
+ login
15147
+ ];
15148
+ }
15149
+ function setAwsAuthRefresh(configDir, command) {
15150
+ const file = join8(configDir, "settings.json");
15151
+ let settings = {};
15152
+ if (existsSync10(file)) {
15153
+ try {
15154
+ const v = JSON.parse(readFileSync9(file, "utf8"));
15155
+ if (!v || typeof v !== "object" || Array.isArray(v))
15156
+ throw new Error("not an object");
15157
+ settings = v;
15158
+ } catch (e) {
15159
+ throw new UserError(`${file} is not valid JSON (${e instanceof Error ? e.message : String(e)}); fix it, then try again`);
15160
+ }
15161
+ }
15162
+ const rest = Object.fromEntries(Object.entries(settings).filter(([k]) => k !== "awsAuthRefresh"));
15163
+ const next = { awsAuthRefresh: command, ...rest };
15164
+ mkdirSync7(dirname4(file), { recursive: true });
15165
+ const tmp = `${file}.${process.pid}.tmp`;
15166
+ writeFileSync6(tmp, `${JSON.stringify(next, null, 2)}
15167
+ `);
15168
+ renameSync2(tmp, file);
15169
+ return file;
15170
+ }
15171
+ function readSettings(file) {
15172
+ if (!existsSync10(file))
15173
+ return null;
15174
+ try {
15175
+ const v = JSON.parse(readFileSync9(file, "utf8"));
15176
+ return v && typeof v === "object" && !Array.isArray(v) ? v : null;
15177
+ } catch (e) {
15178
+ log.warn("aws-auth", `${file} is not valid JSON`, e);
15179
+ return null;
15180
+ }
15181
+ }
15182
+ var str2 = (v) => typeof v === "string" && v.trim() ? v.trim() : undefined;
15183
+ function shellWord(s) {
15184
+ return /^[\w.@-]+$/.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
15185
+ }
15186
+
15094
15187
  // ../daemon/src/agent/modes.ts
15095
15188
  var READ_ONLY_MODE_IDS = ["plan", "read-only"];
15096
15189
  function agentModeFor(spec, mode, available) {
@@ -15117,19 +15210,19 @@ function orderPlanOptions(options) {
15117
15210
  }
15118
15211
 
15119
15212
  // ../daemon/src/agent/origins.ts
15120
- import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
15213
+ import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
15121
15214
  import { homedir as homedir4 } from "os";
15122
- import { basename as basename3, join as join8 } from "path";
15215
+ import { basename as basename3, join as join9 } from "path";
15123
15216
  function commandFolders(spec, cwd, home = homedir4()) {
15124
15217
  if (spec.id === "claude") {
15125
15218
  return [
15126
- { origin: "project", skills: join8(cwd, ".claude", "skills"), commands: join8(cwd, ".claude", "commands") },
15127
- { origin: "user", skills: join8(home, ".claude", "skills"), commands: join8(home, ".claude", "commands") },
15128
- ...pluginPaths(spec.meta).map((p) => ({ origin: "plugin", skills: join8(p, "skills"), commands: join8(p, "commands") }))
15219
+ { origin: "project", skills: join9(cwd, ".claude", "skills"), commands: join9(cwd, ".claude", "commands") },
15220
+ { origin: "user", skills: join9(home, ".claude", "skills"), commands: join9(home, ".claude", "commands") },
15221
+ ...pluginPaths(spec.meta).map((p) => ({ origin: "plugin", skills: join9(p, "skills"), commands: join9(p, "commands") }))
15129
15222
  ];
15130
15223
  }
15131
15224
  if (spec.id === "codex")
15132
- return [{ origin: "user", commands: join8(home, ".codex", "prompts") }];
15225
+ return [{ origin: "user", commands: join9(home, ".codex", "prompts") }];
15133
15226
  return [];
15134
15227
  }
15135
15228
  function readCommandOrigins(folders) {
@@ -15169,15 +15262,15 @@ function pluginPaths(meta) {
15169
15262
  }
15170
15263
  function skillNames(dir) {
15171
15264
  return entries2(dir).flatMap((e) => {
15172
- const file = join8(dir, e, "SKILL.md");
15173
- if (!existsSync10(file))
15265
+ const file = join9(dir, e, "SKILL.md");
15266
+ if (!existsSync11(file))
15174
15267
  return [];
15175
15268
  return [frontMatterName(file) ?? e];
15176
15269
  });
15177
15270
  }
15178
15271
  function commandNames(dir) {
15179
15272
  return entries2(dir).flatMap((e) => {
15180
- const path = join8(dir, e);
15273
+ const path = join9(dir, e);
15181
15274
  if (e.endsWith(".md"))
15182
15275
  return [basename3(e, ".md")];
15183
15276
  return isDir(path) ? commandNames(path) : [];
@@ -15185,7 +15278,7 @@ function commandNames(dir) {
15185
15278
  }
15186
15279
  function frontMatterName(file) {
15187
15280
  try {
15188
- const text = readFileSync9(file, "utf8");
15281
+ const text = readFileSync10(file, "utf8");
15189
15282
  if (!text.startsWith("---"))
15190
15283
  return null;
15191
15284
  const end = text.indexOf(`
@@ -15199,7 +15292,7 @@ function frontMatterName(file) {
15199
15292
  }
15200
15293
  }
15201
15294
  function entries2(dir) {
15202
- if (!existsSync10(dir))
15295
+ if (!existsSync11(dir))
15203
15296
  return [];
15204
15297
  try {
15205
15298
  return readdirSync4(dir).filter((e) => !e.startsWith("."));
@@ -15249,10 +15342,10 @@ function unformatOutput(output) {
15249
15342
  import { isAbsolute as isAbsolute3, resolve as resolve4 } from "path";
15250
15343
 
15251
15344
  // ../daemon/src/agent/acp/background.ts
15252
- import { existsSync as existsSync11, statSync as statSync4 } from "fs";
15345
+ import { existsSync as existsSync12, statSync as statSync4 } from "fs";
15253
15346
  import { open as open2 } from "fs/promises";
15254
15347
  import { homedir as homedir5 } from "os";
15255
- import { basename as basename4, dirname as dirname4, join as join9 } from "path";
15348
+ import { basename as basename4, dirname as dirname5, join as join10 } from "path";
15256
15349
  var STARTED = /Command running in background with ID: (\S+)\. Output is being written to: (\S+)\./;
15257
15350
  function backgroundStart(input, output) {
15258
15351
  const flag = input && typeof input === "object" ? input.run_in_background : undefined;
@@ -15265,15 +15358,15 @@ function spawnDetached(output) {
15265
15358
  return output.includes("Async agent launched successfully");
15266
15359
  }
15267
15360
  function claudeConfigDir() {
15268
- return process.env.CLAUDE_CONFIG_DIR || join9(homedir5(), ".claude");
15361
+ return process.env.CLAUDE_CONFIG_DIR || join10(homedir5(), ".claude");
15269
15362
  }
15270
15363
  function sessionLogFor(file, configDir = claudeConfigDir()) {
15271
- const tasks = dirname4(file);
15364
+ const tasks = dirname5(file);
15272
15365
  if (basename4(tasks) !== "tasks")
15273
15366
  return null;
15274
- const session = dirname4(tasks);
15275
- const path = join9(configDir, "projects", basename4(dirname4(session)), `${basename4(session)}.jsonl`);
15276
- return existsSync11(path) ? path : null;
15367
+ const session = dirname5(tasks);
15368
+ const path = join10(configDir, "projects", basename4(dirname5(session)), `${basename4(session)}.jsonl`);
15369
+ return existsSync12(path) ? path : null;
15277
15370
  }
15278
15371
  function taskEnd(text, toolId) {
15279
15372
  const at = text.indexOf(`<tool-use-id>${toolId}</tool-use-id>`);
@@ -16187,11 +16280,11 @@ ${a.text}
16187
16280
  }
16188
16281
 
16189
16282
  // ../daemon/src/agent/transcript.ts
16190
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
16283
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
16191
16284
  import { appendFile } from "fs/promises";
16192
- import { join as join10 } from "path";
16285
+ import { join as join11 } from "path";
16193
16286
  function transcriptPathFor(transcriptsDir, worktreeId) {
16194
- return join10(transcriptsDir, `${worktreeId}.jsonl`);
16287
+ return join11(transcriptsDir, `${worktreeId}.jsonl`);
16195
16288
  }
16196
16289
  function coalesce(entries) {
16197
16290
  const out = [];
@@ -16214,6 +16307,24 @@ function coalesce(entries) {
16214
16307
  }
16215
16308
  return out;
16216
16309
  }
16310
+ var NOT_THE_TURNS = new Set(["usage", "session-info", "user-message", "fix-asked"]);
16311
+ function sealOpenTurn(entries) {
16312
+ let reason = null;
16313
+ let ts = 0;
16314
+ for (let i = entries.length - 1;i >= 0; i--) {
16315
+ const e = entries[i].event;
16316
+ if ("ts" in e && typeof e.ts === "number")
16317
+ ts = Math.max(ts, e.ts);
16318
+ if (e.type === "turn-end")
16319
+ return null;
16320
+ if (e.type === "turn-start")
16321
+ return { type: "turn-end", stopReason: reason ?? "lost", ts };
16322
+ if (NOT_THE_TURNS.has(e.type))
16323
+ continue;
16324
+ reason ??= e.type === "agent-auth-required" ? "auth" : e.type === "agent-error" ? "failed" : "lost";
16325
+ }
16326
+ return null;
16327
+ }
16217
16328
  function parseTranscript(text, tag) {
16218
16329
  const out = [];
16219
16330
  let torn = 0;
@@ -16245,7 +16356,7 @@ class Transcript {
16245
16356
  this.seq = (this.entries.at(-1)?.seq ?? -1) + 1;
16246
16357
  }
16247
16358
  read() {
16248
- return existsSync12(this.path) ? parseTranscript(readFileSync10(this.path, "utf8"), this.tag) : [];
16359
+ return existsSync13(this.path) ? parseTranscript(readFileSync11(this.path, "utf8"), this.tag) : [];
16249
16360
  }
16250
16361
  append(event) {
16251
16362
  const entry = { seq: this.seq++, event };
@@ -16458,6 +16569,9 @@ function toContent(form, answers) {
16458
16569
  // ../daemon/src/agent/acp/ratelimit.ts
16459
16570
  var RATE_LIMIT_META = "_claude/rateLimit";
16460
16571
  var STATUSES = new Set(["allowed", "allowed_warning", "rejected"]);
16572
+ var WINDOW_KEYS = ["five_hour", "seven_day", "seven_day_overage_included"];
16573
+ var isWindowKey = (v) => WINDOW_KEYS.includes(v);
16574
+ var hasWindow = (windows) => WINDOW_KEYS.some((key) => windows[key]);
16461
16575
  var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
16462
16576
  function window(v) {
16463
16577
  const w = v;
@@ -16479,18 +16593,17 @@ function parseRateLimit(meta, now) {
16479
16593
  const binding = typeof info.rateLimitType === "string" ? info.rateLimitType : undefined;
16480
16594
  const unified = info.unifiedWindows ?? undefined;
16481
16595
  const windows = {};
16482
- const fiveHour = window(unified?.five_hour);
16483
- const sevenDay = window(unified?.seven_day);
16484
- if (fiveHour)
16485
- windows.five_hour = fiveHour;
16486
- if (sevenDay)
16487
- windows.seven_day = sevenDay;
16488
- if (!fiveHour && !sevenDay && (binding === "five_hour" || binding === "seven_day")) {
16596
+ for (const key of WINDOW_KEYS) {
16597
+ const w = window(unified?.[key]);
16598
+ if (w)
16599
+ windows[key] = w;
16600
+ }
16601
+ if (!hasWindow(windows) && isWindowKey(binding)) {
16489
16602
  const top = window({ utilization: info.utilization, resetsAt: info.resetsAt });
16490
16603
  if (top)
16491
16604
  windows[binding] = top;
16492
16605
  }
16493
- if (!windows.five_hour && !windows.seven_day)
16606
+ if (!hasWindow(windows))
16494
16607
  return null;
16495
16608
  return { status, ...binding ? { binding } : {}, windows, at: now };
16496
16609
  }
@@ -16525,9 +16638,10 @@ class AcpSession {
16525
16638
  status = "idle";
16526
16639
  queue = [];
16527
16640
  steered = [];
16641
+ steerMark;
16642
+ streamingId;
16528
16643
  refused = null;
16529
- running = false;
16530
- interrupted = false;
16644
+ turn = null;
16531
16645
  stopped = false;
16532
16646
  holds = 0;
16533
16647
  get held() {
@@ -16557,6 +16671,7 @@ class AcpSession {
16557
16671
  ...d.claudeConfigDir !== undefined ? { configDir: d.claudeConfigDir } : {}
16558
16672
  });
16559
16673
  this.seq = nextNumbers(this.log.entries.map(({ event }) => event.type === "user-message" ? event.attachments : undefined));
16674
+ const seal = sealOpenTurn(this.log.entries);
16560
16675
  const open = new Set;
16561
16676
  for (const { event } of this.log.entries) {
16562
16677
  if (event.type === "agent-question" || event.type === "agent-permission")
@@ -16597,6 +16712,10 @@ class AcpSession {
16597
16712
  ts: Date.now()
16598
16713
  });
16599
16714
  }
16715
+ if (seal) {
16716
+ log.info(d.worktreeId, `sealed a turn left open: ${seal.stopReason}`);
16717
+ this.emit(seal);
16718
+ }
16600
16719
  if (this.openHandoffs.size > 0)
16601
16720
  this.syncStatus();
16602
16721
  }
@@ -16707,6 +16826,19 @@ class AcpSession {
16707
16826
  this.status = s;
16708
16827
  this.d.onStatus(s);
16709
16828
  }
16829
+ get running() {
16830
+ return this.turn !== null;
16831
+ }
16832
+ get stopping() {
16833
+ return this.turn?.stopping ?? false;
16834
+ }
16835
+ setStopping(on) {
16836
+ const turn = this.turn;
16837
+ if (!turn || turn.stopping === on)
16838
+ return;
16839
+ turn.stopping = on;
16840
+ this.d.onStopping?.();
16841
+ }
16710
16842
  send(text, opts = {}) {
16711
16843
  if (this.stopped)
16712
16844
  return log.warn(this.d.worktreeId, "send after close dropped");
@@ -16716,6 +16848,11 @@ class AcpSession {
16716
16848
  this.post(text, opts);
16717
16849
  }
16718
16850
  post(text, opts = {}) {
16851
+ const steer = this.deliver(text, opts);
16852
+ if (steer)
16853
+ fireAndForget(this.d.worktreeId, steer, "agent steer");
16854
+ }
16855
+ deliver(text, opts = {}) {
16719
16856
  const { context, attachments, asked } = opts;
16720
16857
  const item = {
16721
16858
  text,
@@ -16728,10 +16865,11 @@ class AcpSession {
16728
16865
  item.recorded = { attachments: [] };
16729
16866
  }
16730
16867
  this.d.attachments.uploads.hold(uploadIds(attachments));
16731
- if ((this.running || this.own) && !this.interrupted && !this.held && this.live?.conn.steering) {
16732
- return fireAndForget(this.d.worktreeId, this.steer(item), "agent steer");
16868
+ if ((this.running || this.own) && !this.turn?.stopping && !this.held && this.live?.conn.steering) {
16869
+ return this.steer(item);
16733
16870
  }
16734
16871
  this.enqueue(item);
16872
+ return;
16735
16873
  }
16736
16874
  enqueue(item) {
16737
16875
  this.queue.push(item);
@@ -16754,9 +16892,10 @@ class AcpSession {
16754
16892
  async steer(item) {
16755
16893
  item.recorded ??= await this.record(item);
16756
16894
  const live = this.live;
16757
- if (!live || !(this.running || this.own) || this.interrupted)
16895
+ if (!live || !(this.running || this.own) || this.turn?.stopping)
16758
16896
  return this.enqueue(item);
16759
16897
  this.steered.push(item);
16898
+ this.steerMark = this.streamingId;
16760
16899
  let outcome;
16761
16900
  try {
16762
16901
  outcome = steerOutcome(await live.conn.ctx.request(STEER_METHOD, {
@@ -16787,6 +16926,13 @@ class AcpSession {
16787
16926
  if (this.live === live)
16788
16927
  preempted(live.tools);
16789
16928
  }
16929
+ answered() {
16930
+ this.steered = [];
16931
+ if (!this.turn?.stopping || !this.queue.some((q) => q.handedBack))
16932
+ return;
16933
+ this.queue = this.queue.filter((q) => !q.handedBack);
16934
+ this.queueChanged();
16935
+ }
16790
16936
  unsteer(item) {
16791
16937
  const at = this.steered.indexOf(item);
16792
16938
  if (at >= 0)
@@ -16803,8 +16949,10 @@ class AcpSession {
16803
16949
  }
16804
16950
  return;
16805
16951
  }
16806
- this.interrupted = true;
16952
+ this.setStopping(true);
16807
16953
  if (this.steered.length > 0) {
16954
+ for (const item of this.steered)
16955
+ item.handedBack = true;
16808
16956
  this.queue.unshift(...this.steered);
16809
16957
  this.steered = [];
16810
16958
  this.queueChanged();
@@ -16829,7 +16977,7 @@ class AcpSession {
16829
16977
  }
16830
16978
  async drain() {
16831
16979
  this.endOwn("end_turn");
16832
- this.running = true;
16980
+ this.turn = { open: false, stopping: false };
16833
16981
  this.clearReaper();
16834
16982
  this.setStatus("working");
16835
16983
  let item = null;
@@ -16837,44 +16985,69 @@ class AcpSession {
16837
16985
  while (this.queue.length > 0 && !this.held) {
16838
16986
  item = this.queue.shift();
16839
16987
  this.queueChanged();
16988
+ const turn = { open: false, stopping: false };
16989
+ this.turn = turn;
16840
16990
  try {
16841
16991
  await this.runTurn(item);
16842
16992
  } catch (e) {
16843
- if (!this.interrupted)
16993
+ if (!turn.stopping)
16844
16994
  throw e;
16845
- this.emit({ type: "turn-end", stopReason: "interrupted", ts: Date.now() });
16995
+ if (turn.open)
16996
+ this.endTurn("interrupted");
16997
+ else
16998
+ this.emit({ type: "agent-error", message: this.describe(e), ts: Date.now() });
16846
16999
  }
16847
- this.interrupted = false;
16848
17000
  this.steered = [];
17001
+ for (const q of this.queue)
17002
+ q.handedBack = false;
17003
+ this.setStopping(false);
16849
17004
  }
16850
17005
  this.setStatus(this.openHandoffs.size > 0 ? "waiting" : "idle");
16851
17006
  } catch (e) {
16852
- if (this.conn && (isAuthRequired(e) || this.rejectedCredential(e))) {
17007
+ const aws = this.conn && AWS_EXPIRED_RE.test(this.describe(e)) ? this.awsAuth() : null;
17008
+ if (this.conn && (aws || isAuthRequired(e) || this.rejectedCredential(e))) {
16853
17009
  const rejected = !isAuthRequired(e);
16854
- if (rejected)
17010
+ if (rejected && !aws)
16855
17011
  this.emit({ type: "agent-error", message: this.describe(e), ts: Date.now() });
16856
17012
  this.refused = item;
16857
17013
  this.emit({
16858
17014
  type: "agent-auth-required",
16859
17015
  agent: this.conn.spec.id,
16860
17016
  agentName: this.conn.spec.name,
16861
- ...this.offeredMethods(this.conn),
17017
+ ...aws ? {
17018
+ methods: awsMethods(aws, this.conn.spec.id === "claude"),
17019
+ provider: "aws",
17020
+ detail: this.describe(e),
17021
+ ...aws.refresh ? { refresh: aws.refresh } : {}
17022
+ } : this.offeredMethods(this.conn),
16862
17023
  ...rejected ? { rejected: true } : {},
16863
17024
  ts: Date.now()
16864
17025
  });
17026
+ this.endTurn("auth");
16865
17027
  this.setStatus("error");
16866
17028
  } else {
16867
17029
  this.emit({ type: "agent-error", message: this.describe(e), ts: Date.now() });
17030
+ this.endTurn("failed");
16868
17031
  this.setStatus("error");
16869
17032
  fireAndForget(this.d.worktreeId, this.dropConn("error"), "drop agent after error");
16870
17033
  }
16871
17034
  } finally {
16872
- this.interrupted = false;
16873
17035
  this.steered = [];
16874
- this.running = false;
17036
+ for (const q of this.queue)
17037
+ q.handedBack = false;
17038
+ this.setStopping(false);
17039
+ this.turn = null;
16875
17040
  this.maybeArmReaper();
16876
17041
  }
16877
17042
  }
17043
+ endTurn(stopReason) {
17044
+ const turn = this.turn;
17045
+ if (!turn?.open)
17046
+ return;
17047
+ turn.open = false;
17048
+ this.emit({ type: "turn-end", stopReason, ts: Date.now() });
17049
+ this.setStopping(false);
17050
+ }
16878
17051
  async ask(system, prompt, opts = {}) {
16879
17052
  if (this.stopped)
16880
17053
  return null;
@@ -16905,6 +17078,8 @@ class AcpSession {
16905
17078
  }
16906
17079
  async authenticate(methodId, apiKey) {
16907
17080
  const conn = await this.ensureConn("login");
17081
+ if (methodId === AWS_LOGIN_METHOD || methodId === AWS_REFRESH_METHOD)
17082
+ return this.authenticateAws(methodId);
16908
17083
  const method = conn.authMethods.find((m) => m.id === methodId);
16909
17084
  if (!method)
16910
17085
  throw new UserError(`${conn.spec.name} offers no login method "${methodId}"`);
@@ -16936,6 +17111,23 @@ class AcpSession {
16936
17111
  this.loggedIn();
16937
17112
  return { kind: "done" };
16938
17113
  }
17114
+ async authenticateAws(methodId) {
17115
+ const command = awsLoginCommand(this.awsAuth().profile);
17116
+ if (methodId === AWS_LOGIN_METHOD) {
17117
+ return { kind: "terminal", run: { command: process.env.SHELL || "sh", args: ["-lc", command], env: {} } };
17118
+ }
17119
+ const file = setAwsAuthRefresh(this.claudeConfigDir(), command);
17120
+ log.info(this.d.worktreeId, `awsAuthRefresh set to "${command}" in ${file}`);
17121
+ await this.dropConn("settings changed");
17122
+ this.loggedIn();
17123
+ return { kind: "done" };
17124
+ }
17125
+ awsAuth() {
17126
+ return readAwsAuth(this.claudeConfigDir(), this.d.cwd);
17127
+ }
17128
+ claudeConfigDir() {
17129
+ return this.d.claudeConfigDir ?? claudeConfigDir();
17130
+ }
16939
17131
  loggedIn() {
16940
17132
  this.emit({ type: "agent-auth-ok", ts: Date.now() });
16941
17133
  this.retry();
@@ -17000,12 +17192,13 @@ class AcpSession {
17000
17192
  log.warn(this.d.worktreeId, "before-turn hook failed", e);
17001
17193
  }
17002
17194
  this.emit({ type: "turn-start", ts: Date.now() });
17195
+ this.turn.open = true;
17003
17196
  const live = await this.ensureLive("a turn");
17004
17197
  await this.applyMode(live);
17005
17198
  await this.applyOption(live, "model");
17006
17199
  await this.applyOption(live, "thought_level");
17007
- if (this.interrupted) {
17008
- this.emit({ type: "turn-end", stopReason: "interrupted", ts: Date.now() });
17200
+ if (this.turn?.stopping) {
17201
+ this.endTurn("interrupted");
17009
17202
  return;
17010
17203
  }
17011
17204
  const carried = this.carried(live, item.recorded.attachments);
@@ -17015,7 +17208,7 @@ class AcpSession {
17015
17208
  sessionId: live.sessionId,
17016
17209
  prompt: buildPrompt(item.text, this.contextFor(item), prefix, carried)
17017
17210
  });
17018
- this.emit({ type: "turn-end", stopReason: mapStopReason(res.stopReason), ts: Date.now() });
17211
+ this.endTurn(mapStopReason(res.stopReason));
17019
17212
  }
17020
17213
  contextFor(item) {
17021
17214
  return ambientBlock([
@@ -17285,8 +17478,15 @@ class AcpSession {
17285
17478
  if (!this.running)
17286
17479
  this.trackOwn(events);
17287
17480
  for (const ev of events) {
17288
- if (ev.type === "text-delta" || ev.type === "tool-start")
17289
- this.steered = [];
17481
+ if (ev.type === "tool-start")
17482
+ this.answered();
17483
+ else if (ev.type === "text-delta") {
17484
+ if (ev.messageId === undefined)
17485
+ this.steered = [];
17486
+ else if (ev.messageId !== this.steerMark)
17487
+ this.answered();
17488
+ this.streamingId = ev.messageId;
17489
+ }
17290
17490
  this.emit(ev.type === "session-info" ? { ...ev, sessionId: live.sessionId } : ev);
17291
17491
  }
17292
17492
  }
@@ -17357,18 +17557,23 @@ class AcpSession {
17357
17557
  const choice = picked ? choices.find((c) => c.id === picked) : undefined;
17358
17558
  if (!choice)
17359
17559
  return { outcome: { outcome: "cancelled" } };
17560
+ const res = { outcome: { outcome: "selected", optionId: choice.id } };
17360
17561
  if (plan && (choice.kind === "allow_once" || choice.kind === "allow_always")) {
17361
17562
  this.d.setMode?.(modeAfterPlan(choice.name));
17362
17563
  if (planPath)
17363
- fireAndForget(this.d.worktreeId, this.sayPlanEdited(detail, planPath), "edited plan");
17564
+ return this.sayPlanEdited(detail, planPath).then(() => res);
17364
17565
  }
17365
- return { outcome: { outcome: "selected", optionId: choice.id } };
17566
+ return res;
17366
17567
  });
17367
17568
  }
17368
17569
  async sayPlanEdited(proposed, path) {
17369
- if (!await this.d.planEdited?.(path, proposed))
17370
- return;
17371
- this.post(`I edited the plan before approving it. Build what is in ${path}, not the plan you proposed.`);
17570
+ try {
17571
+ if (!await this.d.planEdited?.(path, proposed))
17572
+ return;
17573
+ await this.deliver(`I edited the plan before approving it. Build what is in ${path}, not the plan you proposed.`);
17574
+ } catch (e) {
17575
+ log.warn(this.d.worktreeId, "the edited plan was not sent", e);
17576
+ }
17372
17577
  }
17373
17578
  onElicit(params, signal) {
17374
17579
  const live = this.live;
@@ -17399,13 +17604,14 @@ class AcpSession {
17399
17604
  openAsk(card, respond, signal, headers) {
17400
17605
  const id = randomUUID();
17401
17606
  return new Promise((resolve) => {
17402
- const settle = (outcome, reply) => {
17607
+ const settle = (outcome, reply, before) => {
17403
17608
  if (!this.asks.delete(id))
17404
17609
  return;
17405
17610
  signal?.removeEventListener("abort", onAbort);
17406
17611
  this.emit({ type: "agent-ask-end", id, outcome, ...recorded(reply), ts: Date.now() });
17407
17612
  this.syncStatus();
17408
- resolve(respond(outcome, reply));
17613
+ const res = respond(outcome, reply);
17614
+ resolve(before ? before().then(() => res) : res);
17409
17615
  };
17410
17616
  const onAbort = () => settle("cancelled");
17411
17617
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -17419,12 +17625,21 @@ class AcpSession {
17419
17625
  if (!ask)
17420
17626
  return log.debug(this.d.worktreeId, `answer for an ask that already closed (${askId})`);
17421
17627
  const skipped = reply.kind === "answers" && !reply.answers;
17422
- ask.settle(skipped ? "skipped" : "answered", reply);
17423
- if (skipped || !followUp?.attachments.length)
17424
- return;
17628
+ const note = reply.kind === "choice" ? reply.note?.trim() : undefined;
17629
+ const attachments = skipped ? [] : followUp?.attachments ?? [];
17425
17630
  const headers = ask.headers ?? [];
17426
17631
  const text = headers.length === 1 ? `with my answer to "${headers[0]}"` : "with my answers";
17427
- this.post(text, { attachments: followUp.attachments });
17632
+ const before = note || attachments.length ? async () => {
17633
+ try {
17634
+ if (note)
17635
+ await this.deliver(note);
17636
+ if (attachments.length)
17637
+ await this.deliver(text, { attachments });
17638
+ } catch (e) {
17639
+ log.warn(this.d.worktreeId, "what followed the answer did not go", e);
17640
+ }
17641
+ } : undefined;
17642
+ ask.settle(skipped ? "skipped" : "answered", reply, before);
17428
17643
  }
17429
17644
  cancelAsks(outcome = "cancelled") {
17430
17645
  for (const ask of [...this.asks.values()])
@@ -18803,6 +19018,7 @@ function defaultAgent(wt, d, preview, onProcess) {
18803
19018
  setSessionId: (id) => d.state.setSession(wt.id, id),
18804
19019
  onEvent: (event, seq) => d.hub.emit("agent", wt.id, seq, event),
18805
19020
  onStatus: (status) => d.hub.emit("agentStatus", wt.id, status),
19021
+ onStopping: () => d.hub.emit("agentStopping", wt.id),
18806
19022
  beforeTurn: async () => d.turnStarting?.(wt.id),
18807
19023
  onAuth: (agentId, o) => d.readiness?.observe(agentId, o),
18808
19024
  onLimits: (agentId, limits) => d.readiness?.observeLimits(agentId, limits),
@@ -19648,8 +19864,8 @@ class RuntimeRegistry {
19648
19864
 
19649
19865
  // ../daemon/src/worktrees/naming.ts
19650
19866
  import { randomBytes as randomBytes3 } from "crypto";
19651
- import { existsSync as existsSync13 } from "fs";
19652
- import { join as join11 } from "path";
19867
+ import { existsSync as existsSync14 } from "fs";
19868
+ import { join as join12 } from "path";
19653
19869
  function shortId() {
19654
19870
  return randomBytes3(5).toString("hex");
19655
19871
  }
@@ -19657,7 +19873,7 @@ function freeSlot(parent) {
19657
19873
  for (;; ) {
19658
19874
  const id = shortId();
19659
19875
  const dir = `wt-${id.slice(0, 4)}`;
19660
- if (!existsSync13(join11(parent, dir)))
19876
+ if (!existsSync14(join12(parent, dir)))
19661
19877
  return { id, dir };
19662
19878
  }
19663
19879
  }
@@ -19685,6 +19901,7 @@ function variantLens(index) {
19685
19901
  }
19686
19902
 
19687
19903
  // ../daemon/src/agent/recap.ts
19904
+ var DAEMON_STOPS = new Set(["interrupted", "failed", "auth", "lost"]);
19688
19905
  var REPLY_KEEP = 2000;
19689
19906
  var CLIP = 160;
19690
19907
  function clip(text, max = CLIP) {
@@ -19807,7 +20024,7 @@ function openAskOf(entries) {
19807
20024
  }
19808
20025
  function factsOf(turns, end, ask) {
19809
20026
  const last = turns.at(-1);
19810
- const cut = last?.stop && last.stop !== "end_turn" && last.stop !== "interrupted" ? last.stop : undefined;
20027
+ const cut = last?.stop && last.stop !== "end_turn" && !DAEMON_STOPS.has(last.stop) ? last.stop : undefined;
19811
20028
  return {
19812
20029
  turns: Math.max(1, turns.length),
19813
20030
  edits: turns.reduce((n, t) => n + t.edits, 0),
@@ -20114,8 +20331,8 @@ function makePlanner(agents) {
20114
20331
  }
20115
20332
 
20116
20333
  // ../daemon/src/agent/transcriptForward.ts
20117
- import { existsSync as existsSync14, readdirSync as readdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
20118
- import { join as join12 } from "path";
20334
+ import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "fs";
20335
+ import { join as join13 } from "path";
20119
20336
  var FORWARD_STEP = "1";
20120
20337
  var MARK = '"page';
20121
20338
  function forwardLine(line) {
@@ -20157,22 +20374,22 @@ function forwardTranscripts(dirs, stamp) {
20157
20374
  let files = 0;
20158
20375
  let rows = 0;
20159
20376
  for (const path of dirs.flatMap(transcriptsUnder)) {
20160
- const { text, changed } = forwardText(readFileSync11(path, "utf8"));
20377
+ const { text, changed } = forwardText(readFileSync12(path, "utf8"));
20161
20378
  if (!changed)
20162
20379
  continue;
20163
20380
  const tmp = `${path}.tmp`;
20164
- writeFileSync6(tmp, text);
20165
- renameSync2(tmp, path);
20381
+ writeFileSync7(tmp, text);
20382
+ renameSync3(tmp, path);
20166
20383
  files++;
20167
20384
  rows += changed;
20168
20385
  }
20169
- writeFileSync6(stamp, FORWARD_STEP);
20386
+ writeFileSync7(stamp, FORWARD_STEP);
20170
20387
  if (files)
20171
20388
  log.info("transcripts", `moved ${rows} row(s) in ${files} transcript(s) past the preview-threw row`);
20172
20389
  }
20173
20390
  function readStamp(stamp) {
20174
20391
  try {
20175
- return readFileSync11(stamp, "utf8").trim();
20392
+ return readFileSync12(stamp, "utf8").trim();
20176
20393
  } catch {
20177
20394
  return null;
20178
20395
  }
@@ -20181,10 +20398,10 @@ function transcriptsUnder(dir) {
20181
20398
  const found = [];
20182
20399
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
20183
20400
  if (entry.isFile() && entry.name.endsWith(".jsonl"))
20184
- found.push(join12(dir, entry.name));
20401
+ found.push(join13(dir, entry.name));
20185
20402
  else if (entry.isDirectory()) {
20186
- const inner = join12(dir, entry.name, "transcript.jsonl");
20187
- if (existsSync14(inner))
20403
+ const inner = join13(dir, entry.name, "transcript.jsonl");
20404
+ if (existsSync15(inner))
20188
20405
  found.push(inner);
20189
20406
  }
20190
20407
  }
@@ -20192,23 +20409,23 @@ function transcriptsUnder(dir) {
20192
20409
  }
20193
20410
 
20194
20411
  // ../daemon/src/core/assets.ts
20195
- import { existsSync as existsSync15, readdirSync as readdirSync6, rmSync as rmSync3, statSync as statSync5 } from "fs";
20196
- import { join as join13, resolve as resolve5 } from "path";
20412
+ import { existsSync as existsSync16, readdirSync as readdirSync6, rmSync as rmSync3, statSync as statSync5 } from "fs";
20413
+ import { join as join14, resolve as resolve5 } from "path";
20197
20414
  function locateAssets(here, env = process.env) {
20198
- const packaged = existsSync15(join13(here, "shell", "index.html"));
20415
+ const packaged = existsSync16(join14(here, "shell", "index.html"));
20199
20416
  return {
20200
- shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join13(here, "shell") : join13(here, "../../shell/dist")),
20201
- bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join13(here, "bridge.js") : join13(here, "../../bridge/dist/bridge.js")),
20202
- helperStub: packaged ? join13(here, "helper-stub") : join13(here, "../dist/helper-stub"),
20203
- cliEntry: packaged ? join13(here, "cli.js") : join13(here, "../../cli/src/cli.ts"),
20417
+ shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join14(here, "shell") : join14(here, "../../shell/dist")),
20418
+ bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join14(here, "bridge.js") : join14(here, "../../bridge/dist/bridge.js")),
20419
+ helperStub: packaged ? join14(here, "helper-stub") : join14(here, "../dist/helper-stub"),
20420
+ cliEntry: packaged ? join14(here, "cli.js") : join14(here, "../../cli/src/cli.ts"),
20204
20421
  sourceRoot: packaged ? null : resolve5(here, "../../.."),
20205
- packageJson: packaged ? join13(here, "..", "package.json") : null,
20206
- changelog: packaged ? join13(here, "CHANGELOG.md") : resolve5(here, "../../../CHANGELOG.md")
20422
+ packageJson: packaged ? join14(here, "..", "package.json") : null,
20423
+ changelog: packaged ? join14(here, "CHANGELOG.md") : resolve5(here, "../../../CHANGELOG.md")
20207
20424
  };
20208
20425
  }
20209
20426
  var KEEP_MS = 3 * 24 * 60 * 60 * 1000;
20210
20427
  function pruneAssets(shellDist) {
20211
- const dir = join13(shellDist, "assets");
20428
+ const dir = join14(shellDist, "assets");
20212
20429
  let names;
20213
20430
  try {
20214
20431
  names = readdirSync6(dir);
@@ -20217,7 +20434,7 @@ function pruneAssets(shellDist) {
20217
20434
  }
20218
20435
  const found = [];
20219
20436
  for (const name of names) {
20220
- const path = join13(dir, name);
20437
+ const path = join14(dir, name);
20221
20438
  try {
20222
20439
  found.push({ path, mtime: statSync5(path).mtimeMs });
20223
20440
  } catch {}
@@ -20662,7 +20879,7 @@ class KeepAwake {
20662
20879
  }
20663
20880
 
20664
20881
  // ../daemon/src/core/dialog.ts
20665
- import { existsSync as existsSync16 } from "fs";
20882
+ import { existsSync as existsSync17 } from "fs";
20666
20883
  import { homedir as homedir6 } from "os";
20667
20884
  var PROMPTS = {
20668
20885
  location: { prompt: "Choose where the new project goes", pick: "folder" },
@@ -20681,7 +20898,7 @@ function folderDialog() {
20681
20898
  let cancelled = false;
20682
20899
  const ask = async (start, purpose) => {
20683
20900
  const typed = start === "~" || start.startsWith("~/") ? `${homedir6()}${start.slice(1)}` : start;
20684
- const from = typed.startsWith("/") && existsSync16(typed) ? typed : homedir6();
20901
+ const from = typed.startsWith("/") && existsSync17(typed) ? typed : homedir6();
20685
20902
  cancelled = false;
20686
20903
  const { prompt, pick } = PROMPTS[purpose];
20687
20904
  const p = Bun.spawn(["osascript", ...script(pick).flatMap((line) => ["-e", line]), prompt, from], {
@@ -20759,9 +20976,9 @@ class GrantCodes {
20759
20976
 
20760
20977
  // ../daemon/src/core/helper.ts
20761
20978
  import { createHash as createHash3 } from "crypto";
20762
- import { existsSync as existsSync17 } from "fs";
20979
+ import { existsSync as existsSync18 } from "fs";
20763
20980
  import { chmod, copyFile as copyFile3, mkdir as mkdir2, readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
20764
- import { basename as basename5, dirname as dirname5, join as join14 } from "path";
20981
+ import { basename as basename5, dirname as dirname6, join as join15 } from "path";
20765
20982
  var EXECUTABLE = "Toyon";
20766
20983
  function helperWanted(where) {
20767
20984
  return where.platform === "darwin" && !where.cloud && where.port === DAEMON_DEFAULT_PORT && where.home === undefined;
@@ -20828,7 +21045,7 @@ class Helper {
20828
21045
  run;
20829
21046
  constructor(spec, run2) {
20830
21047
  this.spec = spec;
20831
- this.run = run2 ?? (async (cmd) => (await run(cmd[0], cmd.slice(1), dirname5(spec.dir))).ok);
21048
+ this.run = run2 ?? (async (cmd) => (await run(cmd[0], cmd.slice(1), dirname6(spec.dir))).ok);
20832
21049
  }
20833
21050
  startLink() {
20834
21051
  return this.ready ? START_LINK : null;
@@ -20840,23 +21057,23 @@ class Helper {
20840
21057
  }
20841
21058
  async write() {
20842
21059
  const { spec } = this;
20843
- if (!existsSync17(spec.stub))
21060
+ if (!existsSync18(spec.stub))
20844
21061
  return "skipped";
20845
21062
  const stubHash = createHash3("sha256").update(await readFile2(spec.stub)).digest("hex").slice(0, 16);
20846
21063
  const { plist, script } = helperFiles(spec, stubHash);
20847
- const contents = join14(spec.dir, "Contents");
20848
- const resources = join14(contents, "Resources");
20849
- const stub = join14(contents, "MacOS", EXECUTABLE);
20850
- const scriptFile = join14(resources, "launch.sh");
20851
- if (existsSync17(stub) && existsSync17(scriptFile) && await readFile2(scriptFile, "utf8") === script)
21064
+ const contents = join15(spec.dir, "Contents");
21065
+ const resources = join15(contents, "Resources");
21066
+ const stub = join15(contents, "MacOS", EXECUTABLE);
21067
+ const scriptFile = join15(resources, "launch.sh");
21068
+ if (existsSync18(stub) && existsSync18(scriptFile) && await readFile2(scriptFile, "utf8") === script)
20852
21069
  return "kept";
20853
- await mkdir2(join14(contents, "MacOS"), { recursive: true });
21070
+ await mkdir2(join15(contents, "MacOS"), { recursive: true });
20854
21071
  await mkdir2(resources, { recursive: true });
20855
- await writeFile2(join14(contents, "Info.plist"), plist);
21072
+ await writeFile2(join15(contents, "Info.plist"), plist);
20856
21073
  await writeFile2(scriptFile, script, { mode: 493 });
20857
21074
  await copyFile3(spec.stub, stub);
20858
21075
  await chmod(stub, 493);
20859
- if (!existsSync17(join14(resources, "AppIcon.icns")))
21076
+ if (!existsSync18(join15(resources, "AppIcon.icns")))
20860
21077
  await this.drawIcon(resources);
20861
21078
  await this.run(["xattr", "-cr", spec.dir]);
20862
21079
  if (!await this.run(["codesign", "--force", "--deep", "-s", "-", spec.dir])) {
@@ -20867,16 +21084,16 @@ class Helper {
20867
21084
  }
20868
21085
  async drawIcon(resources) {
20869
21086
  const svg = this.spec.icon;
20870
- const tmp = join14(resources, "iconset.tmp");
20871
- const set = join14(tmp, "AppIcon.iconset");
21087
+ const tmp = join15(resources, "iconset.tmp");
21088
+ const set = join15(tmp, "AppIcon.iconset");
20872
21089
  await mkdir2(set, { recursive: true });
20873
21090
  await this.run(["qlmanage", "-t", "-s", "1024", "-o", tmp, svg]);
20874
- const big = join14(tmp, `${basename5(svg)}.png`);
20875
- if (existsSync17(big)) {
21091
+ const big = join15(tmp, `${basename5(svg)}.png`);
21092
+ if (existsSync18(big)) {
20876
21093
  for (const size of [16, 32, 64, 128, 256, 512, 1024]) {
20877
- await this.run(["sips", "-z", String(size), String(size), big, "--out", join14(set, `icon_${size}x${size}.png`)]);
21094
+ await this.run(["sips", "-z", String(size), String(size), big, "--out", join15(set, `icon_${size}x${size}.png`)]);
20878
21095
  }
20879
- await this.run(["iconutil", "-c", "icns", set, "-o", join14(resources, "AppIcon.icns")]);
21096
+ await this.run(["iconutil", "-c", "icns", set, "-o", join15(resources, "AppIcon.icns")]);
20880
21097
  }
20881
21098
  await rm(tmp, { recursive: true, force: true });
20882
21099
  }
@@ -21100,7 +21317,7 @@ class Origins {
21100
21317
  }
21101
21318
 
21102
21319
  // ../daemon/src/core/remoteSwitch.ts
21103
- import { writeFileSync as writeFileSync7 } from "fs";
21320
+ import { writeFileSync as writeFileSync8 } from "fs";
21104
21321
  class RemoteSwitch {
21105
21322
  d;
21106
21323
  constructor(d) {
@@ -21133,7 +21350,7 @@ class RemoteSwitch {
21133
21350
  const accepted = acceptRemote(view, this.d.policy);
21134
21351
  if (typeof accepted === "string")
21135
21352
  throw new UserError(accepted);
21136
- writeFileSync7(this.d.file, `${JSON.stringify(accepted, null, 2)}
21353
+ writeFileSync8(this.d.file, `${JSON.stringify(accepted, null, 2)}
21137
21354
  `);
21138
21355
  return this.live(accepted);
21139
21356
  }
@@ -21156,12 +21373,12 @@ import { spawn as spawn4 } from "child_process";
21156
21373
  import { homedir as homedir7 } from "os";
21157
21374
 
21158
21375
  // ../shared/src/logfile.ts
21159
- import { chmodSync, existsSync as existsSync18, openSync, renameSync as renameSync3, statSync as statSync6 } from "fs";
21376
+ import { chmodSync, existsSync as existsSync19, openSync, renameSync as renameSync4, statSync as statSync6 } from "fs";
21160
21377
  var LOG_ROTATE_BYTES = 10 * 1024 * 1024;
21161
21378
  var previousLog = (path) => `${path}.1`;
21162
21379
  function openLog(path, maxBytes = LOG_ROTATE_BYTES) {
21163
- if (existsSync18(path) && statSync6(path).size > maxBytes)
21164
- renameSync3(path, previousLog(path));
21380
+ if (existsSync19(path) && statSync6(path).size > maxBytes)
21381
+ renameSync4(path, previousLog(path));
21165
21382
  const fd = openSync(path, "a", 384);
21166
21383
  chmodSync(path, 384);
21167
21384
  return fd;
@@ -21328,7 +21545,7 @@ class SelfWatch {
21328
21545
 
21329
21546
  // ../daemon/src/core/state.ts
21330
21547
  import { randomBytes as randomBytes6 } from "crypto";
21331
- import { existsSync as existsSync19, readFileSync as readFileSync12, renameSync as renameSync4, writeFileSync as writeFileSync8 } from "fs";
21548
+ import { existsSync as existsSync20, readFileSync as readFileSync13, renameSync as renameSync5, writeFileSync as writeFileSync9 } from "fs";
21332
21549
  var TRUSTED_MAX = 16;
21333
21550
  var DAEMON_GROUPS = "daemon";
21334
21551
  var GROUPS_SAVE_MS = 250;
@@ -21337,11 +21554,11 @@ var empty = { repos: [], worktrees: [], sessions: {} };
21337
21554
  function loadState(paths) {
21338
21555
  ensureDirs(paths);
21339
21556
  const STATE_FILE = paths.stateFile;
21340
- if (!existsSync19(STATE_FILE))
21557
+ if (!existsSync20(STATE_FILE))
21341
21558
  return structuredClone(empty);
21342
21559
  let raw;
21343
21560
  try {
21344
- raw = readFileSync12(STATE_FILE, "utf8");
21561
+ raw = readFileSync13(STATE_FILE, "utf8");
21345
21562
  } catch (e) {
21346
21563
  log.error("state", `cannot read ${STATE_FILE}; starting empty`, e);
21347
21564
  return structuredClone(empty);
@@ -21352,7 +21569,7 @@ function loadState(paths) {
21352
21569
  } catch (e) {
21353
21570
  const backup = `${STATE_FILE}.corrupt-${Date.now()}`;
21354
21571
  try {
21355
- writeFileSync8(backup, raw);
21572
+ writeFileSync9(backup, raw);
21356
21573
  } catch (be) {
21357
21574
  log.warn("state", "could not write the corrupt-state backup", be);
21358
21575
  }
@@ -21383,16 +21600,16 @@ function loadState(paths) {
21383
21600
  }
21384
21601
  function saveState(paths, state) {
21385
21602
  const tmp = `${paths.stateFile}.tmp`;
21386
- writeFileSync8(tmp, JSON.stringify(state, null, 2));
21387
- renameSync4(tmp, paths.stateFile);
21603
+ writeFileSync9(tmp, JSON.stringify(state, null, 2));
21604
+ renameSync5(tmp, paths.stateFile);
21388
21605
  }
21389
21606
  function loadOrCreateToken(paths) {
21390
21607
  ensureDirs(paths);
21391
21608
  const TOKEN_FILE = paths.tokenFile;
21392
- if (existsSync19(TOKEN_FILE))
21393
- return readFileSync12(TOKEN_FILE, "utf8").trim();
21609
+ if (existsSync20(TOKEN_FILE))
21610
+ return readFileSync13(TOKEN_FILE, "utf8").trim();
21394
21611
  const token = randomBytes6(32).toString("hex");
21395
- writeFileSync8(TOKEN_FILE, token, { mode: 384 });
21612
+ writeFileSync9(TOKEN_FILE, token, { mode: 384 });
21396
21613
  return token;
21397
21614
  }
21398
21615
 
@@ -21855,7 +22072,7 @@ class Tailnet {
21855
22072
  }
21856
22073
 
21857
22074
  // ../daemon/src/design/service.ts
21858
- import { join as join15 } from "path";
22075
+ import { join as join16 } from "path";
21859
22076
 
21860
22077
  // ../daemon/src/design/scan.ts
21861
22078
  var COLOR = /^(#|rgba?\(|hsla?\(|oklch\(|oklab\(|lab\(|lch\(|color\(|color-mix\(|light-dark\()/i;
@@ -22305,7 +22522,7 @@ class DesignService {
22305
22522
  async function readAll(root, paths) {
22306
22523
  const out = [];
22307
22524
  for (const path of paths) {
22308
- const file = Bun.file(join15(root, path));
22525
+ const file = Bun.file(join16(root, path));
22309
22526
  if (file.size === 0 || file.size > MAX_FILE_BYTES)
22310
22527
  continue;
22311
22528
  try {
@@ -22420,14 +22637,14 @@ function typicalUse(classes) {
22420
22637
  var normal = (s) => s.replace(/[-_]/g, "").toLowerCase();
22421
22638
  async function loadProjectTypescript(root) {
22422
22639
  try {
22423
- return await import(join15(root, "node_modules", "typescript", "lib", "typescript.js"));
22640
+ return await import(join16(root, "node_modules", "typescript", "lib", "typescript.js"));
22424
22641
  } catch {
22425
22642
  return null;
22426
22643
  }
22427
22644
  }
22428
22645
 
22429
22646
  // ../daemon/src/drafts/store.ts
22430
- import { existsSync as existsSync20, readFileSync as readFileSync13, renameSync as renameSync5, writeFileSync as writeFileSync9 } from "fs";
22647
+ import { existsSync as existsSync21, readFileSync as readFileSync14, renameSync as renameSync6, writeFileSync as writeFileSync10 } from "fs";
22431
22648
  var SAVE_DELAY_MS = 1000;
22432
22649
 
22433
22650
  class DraftStore {
@@ -22564,10 +22781,10 @@ class DraftStore {
22564
22781
  }
22565
22782
  }
22566
22783
  function readRecord(file) {
22567
- if (!existsSync20(file))
22784
+ if (!existsSync21(file))
22568
22785
  return [];
22569
22786
  try {
22570
- const raw = JSON.parse(readFileSync13(file, "utf8"));
22787
+ const raw = JSON.parse(readFileSync14(file, "utf8"));
22571
22788
  return raw && typeof raw === "object" ? Object.entries(raw) : [];
22572
22789
  } catch (e) {
22573
22790
  log.warn("drafts", `skipped an unreadable ${file}`, e);
@@ -22577,8 +22794,8 @@ function readRecord(file) {
22577
22794
  function writeAtomic(file, value) {
22578
22795
  const tmp = `${file}.tmp`;
22579
22796
  try {
22580
- writeFileSync9(tmp, JSON.stringify(value));
22581
- renameSync5(tmp, file);
22797
+ writeFileSync10(tmp, JSON.stringify(value));
22798
+ renameSync6(tmp, file);
22582
22799
  } catch (e) {
22583
22800
  log.error("drafts", `could not write ${file}`, e);
22584
22801
  }
@@ -22824,13 +23041,13 @@ import { stat as stat3 } from "fs/promises";
22824
23041
  import { basename as basename6, resolve as resolve7 } from "path";
22825
23042
 
22826
23043
  // ../daemon/src/repos/browse.ts
22827
- import { existsSync as existsSync21, statSync as statSync7 } from "fs";
23044
+ import { existsSync as existsSync22, statSync as statSync7 } from "fs";
22828
23045
  import { readdir } from "fs/promises";
22829
23046
  import { homedir as homedir8 } from "os";
22830
- import { dirname as dirname6, join as join16 } from "path";
23047
+ import { dirname as dirname7, join as join17 } from "path";
22831
23048
  var MAX_ENTRIES = 40;
22832
23049
  function expandTilde(raw) {
22833
- return raw === "~" || raw.startsWith("~/") ? join16(homedir8(), raw.slice(1)) : raw;
23050
+ return raw === "~" || raw.startsWith("~/") ? join17(homedir8(), raw.slice(1)) : raw;
22834
23051
  }
22835
23052
  function collapseTilde(abs) {
22836
23053
  const home = homedir8();
@@ -22841,7 +23058,7 @@ function holdsNothing(names) {
22841
23058
  }
22842
23059
  async function describeFolder(abs) {
22843
23060
  const path = collapseTilde(abs);
22844
- if (existsSync21(join16(abs, ".git")))
23061
+ if (existsSync22(join17(abs, ".git")))
22845
23062
  return { path, kind: "project" };
22846
23063
  try {
22847
23064
  return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
@@ -22859,11 +23076,11 @@ function describe2(typed, parent) {
22859
23076
  isFile = st.isFile();
22860
23077
  } catch {}
22861
23078
  return {
22862
- exists: existsSync21(typed),
23079
+ exists: existsSync22(typed),
22863
23080
  isDir,
22864
- isRepo: isDir && existsSync21(join16(typed, ".git")),
23081
+ isRepo: isDir && existsSync22(join17(typed, ".git")),
22865
23082
  isFile,
22866
- parentExists: existsSync21(parent)
23083
+ parentExists: existsSync22(parent)
22867
23084
  };
22868
23085
  }
22869
23086
  async function browsePath(raw) {
@@ -22871,7 +23088,7 @@ async function browsePath(raw) {
22871
23088
  if (!typed.startsWith("/"))
22872
23089
  return { entries: [], target: NOWHERE };
22873
23090
  const listing = typed.endsWith("/");
22874
- const dir = listing ? typed : dirname6(typed);
23091
+ const dir = listing ? typed : dirname7(typed);
22875
23092
  const prefix = listing ? "" : typed.slice(dir.length).replace(/^\//, "").toLowerCase();
22876
23093
  const target = describe2(listing ? typed.replace(/\/+$/, "") : typed, dir);
22877
23094
  let found;
@@ -22879,7 +23096,7 @@ async function browsePath(raw) {
22879
23096
  const listed = await readdir(dir, { withFileTypes: true });
22880
23097
  found = listed.map((e) => ({
22881
23098
  name: e.name,
22882
- isFile: e.isSymbolicLink() ? isFileAt(join16(dir, e.name)) : e.isFile() ? true : e.isDirectory() ? false : null
23099
+ isFile: e.isSymbolicLink() ? isFileAt(join17(dir, e.name)) : e.isFile() ? true : e.isDirectory() ? false : null
22883
23100
  })).filter((e) => e.isFile !== null);
22884
23101
  } catch {
22885
23102
  return { entries: [], target };
@@ -22890,8 +23107,8 @@ async function browsePath(raw) {
22890
23107
  continue;
22891
23108
  if (!name.toLowerCase().startsWith(prefix))
22892
23109
  continue;
22893
- const abs = join16(dir, name);
22894
- entries.push({ path: collapseTilde(abs), name, isRepo: !isFile && existsSync21(join16(abs, ".git")), isFile });
23110
+ const abs = join17(dir, name);
23111
+ entries.push({ path: collapseTilde(abs), name, isRepo: !isFile && existsSync22(join17(abs, ".git")), isFile });
22895
23112
  }
22896
23113
  const rank = (e) => e.isRepo ? 0 : e.isFile ? 2 : 1;
22897
23114
  entries.sort((a, b) => rank(a) - rank(b) || a.name.localeCompare(b.name));
@@ -23014,7 +23231,7 @@ function withFetchLock(repoPath, fn) {
23014
23231
  var fileLockKey = (absPath) => `file:${absPath}`;
23015
23232
 
23016
23233
  // ../daemon/src/git/status.ts
23017
- import { join as join17 } from "path";
23234
+ import { join as join18 } from "path";
23018
23235
  async function treeFingerprint(worktreePath) {
23019
23236
  const [head, diff, untracked] = await Promise.all([
23020
23237
  git(worktreePath, "rev-parse", "HEAD"),
@@ -23024,7 +23241,7 @@ async function treeFingerprint(worktreePath) {
23024
23241
  const others = await Promise.all(untracked.out.split(`
23025
23242
  `).filter(Boolean).map(async (p) => {
23026
23243
  try {
23027
- const f = Bun.file(join17(worktreePath, p));
23244
+ const f = Bun.file(join18(worktreePath, p));
23028
23245
  const body = f.size > 2000000 ? String(f.size) : Bun.hash(await f.arrayBuffer()).toString(36);
23029
23246
  return `${p}:${body}`;
23030
23247
  } catch {
@@ -23109,7 +23326,7 @@ async function numstat(worktreePath, ...range) {
23109
23326
  }
23110
23327
  async function untrackedLines(worktreePath, file, budget) {
23111
23328
  try {
23112
- const f = Bun.file(join17(worktreePath, file));
23329
+ const f = Bun.file(join18(worktreePath, file));
23113
23330
  if (f.size > 2000000 || f.size > budget.left)
23114
23331
  return {};
23115
23332
  budget.left -= f.size;
@@ -23822,7 +24039,7 @@ class OpenService {
23822
24039
  async grant(path, size, mtimeMs) {
23823
24040
  const name = basename6(path);
23824
24041
  if (this.d.refused.some((r) => within2(path, canonical(resolve7(r))))) {
23825
- throw new UserError(`${name} is inside Toyon's own folder, which is not for editing`);
24042
+ throw new UserError(`${name} is Toyon's own state, which the daemon keeps itself`);
23826
24043
  }
23827
24044
  const id = shortId();
23828
24045
  if (size > FILE_MAX_CHARS) {
@@ -24215,9 +24432,9 @@ class AfterLand {
24215
24432
  }
24216
24433
 
24217
24434
  // ../daemon/src/repos/registry.ts
24218
- import { existsSync as existsSync25, mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
24435
+ import { existsSync as existsSync26, mkdirSync as mkdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
24219
24436
  import { homedir as homedir10 } from "os";
24220
- import { basename as basename8, dirname as dirname8, join as join21 } from "path";
24437
+ import { basename as basename8, dirname as dirname9, join as join22 } from "path";
24221
24438
 
24222
24439
  // ../daemon/src/git/base.ts
24223
24440
  async function resolveBase(repoPath, defaultBranch, route) {
@@ -24228,8 +24445,8 @@ async function resolveBase(repoPath, defaultBranch, route) {
24228
24445
  }
24229
24446
 
24230
24447
  // ../daemon/src/repos/config.ts
24231
- import { closeSync, existsSync as existsSync22, openSync as openSync2, readdirSync as readdirSync7, readFileSync as readFileSync14, readSync, statSync as statSync8 } from "fs";
24232
- import { join as join18 } from "path";
24448
+ import { closeSync, existsSync as existsSync23, openSync as openSync2, readdirSync as readdirSync7, readFileSync as readFileSync15, readSync, statSync as statSync8 } from "fs";
24449
+ import { join as join19 } from "path";
24233
24450
 
24234
24451
  // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
24235
24452
  function createScanner(text, ignoreTrivia = false) {
@@ -25593,7 +25810,7 @@ function applyEdits(text, edits) {
25593
25810
 
25594
25811
  // ../daemon/src/repos/config.ts
25595
25812
  var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
25596
- var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync22(join18(repoPath, rel)));
25813
+ var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync23(join19(repoPath, rel)));
25597
25814
  var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
25598
25815
  function parseSettings(text) {
25599
25816
  const errors = [];
@@ -25618,7 +25835,7 @@ function readConfigFile(repoPath) {
25618
25835
  for (const rel of files) {
25619
25836
  let raw;
25620
25837
  try {
25621
- raw = parseSettings(readFileSync14(join18(repoPath, rel), "utf8"));
25838
+ raw = parseSettings(readFileSync15(join19(repoPath, rel), "utf8"));
25622
25839
  } catch (e) {
25623
25840
  return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
25624
25841
  }
@@ -25677,12 +25894,12 @@ function configTarget(repoPath, made) {
25677
25894
  }
25678
25895
  function configBody(repoPath, rel, config) {
25679
25896
  const place = PLACES.find((p) => p.local === rel);
25680
- const abs = place && join18(repoPath, place.shared);
25681
- if (!abs || !existsSync22(abs))
25897
+ const abs = place && join19(repoPath, place.shared);
25898
+ if (!abs || !existsSync23(abs))
25682
25899
  return config;
25683
25900
  let shared;
25684
25901
  try {
25685
- shared = parseSettings(readFileSync14(abs, "utf8"));
25902
+ shared = parseSettings(readFileSync15(abs, "utf8"));
25686
25903
  } catch {
25687
25904
  return config;
25688
25905
  }
@@ -25695,7 +25912,7 @@ function configText(repoPath, rel, config) {
25695
25912
  let text;
25696
25913
  let was;
25697
25914
  try {
25698
- text = readFileSync14(join18(repoPath, rel), "utf8");
25915
+ text = readFileSync15(join19(repoPath, rel), "utf8");
25699
25916
  was = parseSettings(text);
25700
25917
  } catch {
25701
25918
  return fresh;
@@ -25725,9 +25942,9 @@ function detectConfig(repoPath) {
25725
25942
  return { config: explicit.config, needsSetup: false };
25726
25943
  if (explicit)
25727
25944
  log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
25728
- const pkgPath = join18(repoPath, "package.json");
25729
- if (existsSync22(pkgPath)) {
25730
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf8"));
25945
+ const pkgPath = join19(repoPath, "package.json");
25946
+ if (existsSync23(pkgPath)) {
25947
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
25731
25948
  const scripts = pkg.scripts ?? {};
25732
25949
  const runner = detectRunner(repoPath, pkg.packageManager);
25733
25950
  const pages = detectPages(repoPath, scripts, runner);
@@ -25748,7 +25965,7 @@ function detectConfig(repoPath) {
25748
25965
  };
25749
25966
  }
25750
25967
  }
25751
- if (existsSync22(join18(repoPath, "start.sh"))) {
25968
+ if (existsSync23(join19(repoPath, "start.sh"))) {
25752
25969
  return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
25753
25970
  }
25754
25971
  const assumed = assumeNothingToRun(repoPath);
@@ -25791,7 +26008,7 @@ function assumeNothingToRun(repoPath) {
25791
26008
  return marker && !servesPage(repoPath) ? marker : undefined;
25792
26009
  }
25793
26010
  function markerIn(repoPath) {
25794
- const named = NO_PAGE_MARKERS.find((f) => existsSync22(join18(repoPath, f)));
26011
+ const named = NO_PAGE_MARKERS.find((f) => existsSync23(join19(repoPath, f)));
25795
26012
  if (named)
25796
26013
  return named;
25797
26014
  let entries;
@@ -25803,12 +26020,12 @@ function markerIn(repoPath) {
25803
26020
  return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
25804
26021
  }
25805
26022
  function servesPage(repoPath) {
25806
- if (WEB_FILES.some((f) => existsSync22(join18(repoPath, f))))
26023
+ if (WEB_FILES.some((f) => existsSync23(join19(repoPath, f))))
25807
26024
  return true;
25808
- if (/^web:/m.test(head(join18(repoPath, "Procfile"))))
26025
+ if (/^web:/m.test(head(join19(repoPath, "Procfile"))))
25809
26026
  return true;
25810
26027
  for (const [files, names] of WEB_DEPENDENCIES) {
25811
- const text = files.map((f) => head(join18(repoPath, f))).join(`
26028
+ const text = files.map((f) => head(join19(repoPath, f))).join(`
25812
26029
  `);
25813
26030
  if (text.trim() && names.some((n) => mentions(text, n)))
25814
26031
  return true;
@@ -25825,7 +26042,7 @@ function frontEndBeside(repoPath) {
25825
26042
  return dirs.some((d) => {
25826
26043
  if (!d.isDirectory() || d.name.startsWith(".") || NOT_A_PACKAGE.has(d.name))
25827
26044
  return false;
25828
- const pkg = head(join18(repoPath, d.name, "package.json"));
26045
+ const pkg = head(join19(repoPath, d.name, "package.json"));
25829
26046
  if (!pkg)
25830
26047
  return false;
25831
26048
  try {
@@ -25840,7 +26057,7 @@ function mentions(text, name) {
25840
26057
  return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i").test(text);
25841
26058
  }
25842
26059
  function head(path) {
25843
- if (!existsSync22(path))
26060
+ if (!existsSync23(path))
25844
26061
  return "";
25845
26062
  try {
25846
26063
  const fd = openSync2(path, "r");
@@ -25881,11 +26098,11 @@ function isLocalDatabase(url) {
25881
26098
  }
25882
26099
  function detectServices(repoPath) {
25883
26100
  const out = {};
25884
- const compose = COMPOSE_FILES.find((f) => existsSync22(join18(repoPath, f)));
26101
+ const compose = COMPOSE_FILES.find((f) => existsSync23(join19(repoPath, f)));
25885
26102
  if (compose)
25886
26103
  out.compose = compose;
25887
26104
  for (const file of ENV_FILES) {
25888
- const text = head(join18(repoPath, file));
26105
+ const text = head(join19(repoPath, file));
25889
26106
  if (!text)
25890
26107
  continue;
25891
26108
  for (const name of DB_URL_NAMES) {
@@ -25909,7 +26126,7 @@ function inferPaths(cmd, repoPath, depth = 1) {
25909
26126
  return;
25910
26127
  let dir = false;
25911
26128
  try {
25912
- dir = statSync8(join18(repoPath, clean)).isDirectory();
26129
+ dir = statSync8(join19(repoPath, clean)).isDirectory();
25913
26130
  } catch {
25914
26131
  return;
25915
26132
  }
@@ -25920,25 +26137,25 @@ function inferPaths(cmd, repoPath, depth = 1) {
25920
26137
  const t = tokens[i];
25921
26138
  if (t === "cd") {
25922
26139
  const dir = tokens[i + 1];
25923
- if (dir && !dir.startsWith("-") && existsSync22(join18(repoPath, dir)))
26140
+ if (dir && !dir.startsWith("-") && existsSync23(join19(repoPath, dir)))
25924
26141
  add(dir);
25925
26142
  continue;
25926
26143
  }
25927
26144
  if (t.startsWith("-") || t.startsWith("$"))
25928
26145
  continue;
25929
26146
  const mod = /^([A-Za-z_]\w*)(?:\.\w+)*:\w+$/.exec(t);
25930
- if (mod?.[1] && existsSync22(join18(repoPath, mod[1]))) {
26147
+ if (mod?.[1] && existsSync23(join19(repoPath, mod[1]))) {
25931
26148
  add(mod[1]);
25932
26149
  continue;
25933
26150
  }
25934
26151
  const rel = t.replace(/^\.\//, "");
25935
26152
  if (rel.startsWith("/") || rel.startsWith("..") || rel.includes("="))
25936
26153
  continue;
25937
- if (!existsSync22(join18(repoPath, rel)))
26154
+ if (!existsSync23(join19(repoPath, rel)))
25938
26155
  continue;
25939
26156
  add(rel);
25940
26157
  if (depth > 0 && rel.endsWith(".sh")) {
25941
- for (const p of inferPaths(head(join18(repoPath, rel)), repoPath, depth - 1))
26158
+ for (const p of inferPaths(head(join19(repoPath, rel)), repoPath, depth - 1))
25942
26159
  out.add(p);
25943
26160
  }
25944
26161
  }
@@ -25961,31 +26178,31 @@ function procCommand(runner, script, body) {
25961
26178
  return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
25962
26179
  }
25963
26180
  function detectPages(repoPath, scripts, runner) {
25964
- if (!scripts.build || !existsSync22(join18(repoPath, "functions")))
26181
+ if (!scripts.build || !existsSync23(join19(repoPath, "functions")))
25965
26182
  return null;
25966
- const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join18(repoPath, f)).find((f) => existsSync22(f));
26183
+ const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join19(repoPath, f)).find((f) => existsSync23(f));
25967
26184
  if (!cfg)
25968
26185
  return null;
25969
- const out = readFileSync14(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
26186
+ const out = readFileSync15(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
25970
26187
  if (!out)
25971
26188
  return null;
25972
26189
  return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
25973
26190
  }
25974
26191
  function detectRunner(repoPath, packageManager) {
25975
- if (existsSync22(join18(repoPath, "bun.lock")) || existsSync22(join18(repoPath, "bun.lockb")))
26192
+ if (existsSync23(join19(repoPath, "bun.lock")) || existsSync23(join19(repoPath, "bun.lockb")))
25976
26193
  return "bun";
25977
- if (existsSync22(join18(repoPath, "pnpm-lock.yaml")))
26194
+ if (existsSync23(join19(repoPath, "pnpm-lock.yaml")))
25978
26195
  return "pnpm";
25979
- if (existsSync22(join18(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
26196
+ if (existsSync23(join19(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
25980
26197
  return "yarn";
25981
26198
  return "npm";
25982
26199
  }
25983
26200
 
25984
26201
  // ../daemon/src/repos/create.ts
25985
- import { existsSync as existsSync23, realpathSync as realpathSync2, statSync as statSync9 } from "fs";
26202
+ import { existsSync as existsSync24, realpathSync as realpathSync2, statSync as statSync9 } from "fs";
25986
26203
  import { appendFile as appendFile2, mkdir as mkdir3, rm as rm2 } from "fs/promises";
25987
26204
  import { homedir as homedir9 } from "os";
25988
- import { dirname as dirname7, join as join19, resolve as resolve8 } from "path";
26205
+ import { dirname as dirname8, join as join20, resolve as resolve8 } from "path";
25989
26206
  function isInside(dir, parent) {
25990
26207
  const p = parent.replace(/\/+$/, "");
25991
26208
  return dir === p || dir.startsWith(`${p}/`);
@@ -25996,35 +26213,35 @@ function planProject(opts, home = homedir9()) {
25996
26213
  throw new UserError(nameError);
25997
26214
  const first = firstProjectsFolder(opts.parent, home);
25998
26215
  const parent = first ?? resolveParent(opts.parent);
25999
- const dir = join19(parent, opts.name.trim());
26000
- if (existsSync23(dir))
26216
+ const dir = join20(parent, opts.name.trim());
26217
+ if (existsSync24(dir))
26001
26218
  throw new UserError(`${dir} already exists`);
26002
26219
  return { parent, dir, makeParent: first !== null };
26003
26220
  }
26004
26221
  function firstProjectsFolder(raw, home) {
26005
26222
  const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
26006
- if (typed !== join19(home, PROJECTS_FOLDER) || existsSync23(typed) || !existsSync23(home))
26223
+ if (typed !== join20(home, PROJECTS_FOLDER) || existsSync24(typed) || !existsSync24(home))
26007
26224
  return null;
26008
- return join19(realpathSync2(home), PROJECTS_FOLDER);
26225
+ return join20(realpathSync2(home), PROJECTS_FOLDER);
26009
26226
  }
26010
26227
  function planInPlace(opts) {
26011
26228
  const name = opts.name.trim();
26012
26229
  if (!name || name === "." || name === ".." || name.includes("/")) {
26013
26230
  throw new UserError(`${opts.name} is not a folder name`);
26014
26231
  }
26015
- const typed = join19(resolveParent(opts.parent), name);
26016
- if (!existsSync23(typed) || !statSync9(typed).isDirectory())
26232
+ const typed = join20(resolveParent(opts.parent), name);
26233
+ if (!existsSync24(typed) || !statSync9(typed).isDirectory())
26017
26234
  throw new UserError(`${typed} is not a folder`);
26018
26235
  const dir = realpathSync2(typed);
26019
- if (existsSync23(join19(dir, ".git")))
26236
+ if (existsSync24(join20(dir, ".git")))
26020
26237
  throw new UserError(`${dir} is already a project; open it instead`);
26021
- return { parent: dirname7(dir), dir, makeParent: false };
26238
+ return { parent: dirname8(dir), dir, makeParent: false };
26022
26239
  }
26023
26240
  function resolveParent(raw) {
26024
26241
  const typed = expandTilde(raw.trim());
26025
26242
  if (!typed.startsWith("/"))
26026
26243
  throw new UserError(`${raw} is not a folder path`);
26027
- if (!existsSync23(typed))
26244
+ if (!existsSync24(typed))
26028
26245
  throw new UserError(`${raw} does not exist; make it first`);
26029
26246
  if (!statSync9(typed).isDirectory())
26030
26247
  throw new UserError(`${raw} is not a folder`);
@@ -26065,7 +26282,7 @@ async function initWithFirstCommit(dir) {
26065
26282
  if (!exclude.ok)
26066
26283
  throw new UserError(`git rev-parse failed: ${exclude.err}`);
26067
26284
  const excludePath = resolve8(dir, exclude.out);
26068
- await mkdir3(dirname7(excludePath), { recursive: true });
26285
+ await mkdir3(dirname8(excludePath), { recursive: true });
26069
26286
  await appendFile2(excludePath, `
26070
26287
  ${LOCAL_EXCLUDES.join(`
26071
26288
  `)}
@@ -26105,13 +26322,13 @@ async function undoCreate(dir) {
26105
26322
  }
26106
26323
  async function undoInit(dir) {
26107
26324
  try {
26108
- await rm2(join19(dir, ".git"), { recursive: true, force: true });
26325
+ await rm2(join20(dir, ".git"), { recursive: true, force: true });
26109
26326
  } catch (e) {
26110
26327
  log.warn(dir, "could not take back a half-made .git", e);
26111
26328
  }
26112
26329
  }
26113
26330
  async function unmakeProject(dir, made) {
26114
- const target = made === "folder" ? dir : join19(dir, ".git");
26331
+ const target = made === "folder" ? dir : join20(dir, ".git");
26115
26332
  try {
26116
26333
  await rm2(target, { recursive: true, force: true });
26117
26334
  } catch (e) {
@@ -26141,8 +26358,8 @@ async function ok(p, what) {
26141
26358
  }
26142
26359
 
26143
26360
  // ../daemon/src/repos/watcher.ts
26144
- import { existsSync as existsSync24, watch } from "fs";
26145
- import { basename as basename7, join as join20 } from "path";
26361
+ import { existsSync as existsSync25, watch } from "fs";
26362
+ import { basename as basename7, join as join21 } from "path";
26146
26363
  function watchDefaultBranch(repoPath, branch, onMove) {
26147
26364
  const last = { local: null, upstream: null };
26148
26365
  const read = async () => {
@@ -26195,29 +26412,29 @@ function watchDefaultBranch(repoPath, branch, onMove) {
26195
26412
  ]);
26196
26413
  if (stopped)
26197
26414
  return;
26198
- const gitDir = common.ok && common.out ? common.out : join20(repoPath, ".git");
26415
+ const gitDir = common.ok && common.out ? common.out : join21(repoPath, ".git");
26199
26416
  tryWatch(gitDir);
26200
- tryWatch(join20(gitDir, "refs", "heads"));
26417
+ tryWatch(join21(gitDir, "refs", "heads"));
26201
26418
  if (!remote.ok || !remote.out)
26202
26419
  return;
26203
- const remotes = join20(gitDir, "refs", "remotes");
26204
- const mine = join20(remotes, remote.out);
26420
+ const remotes = join21(gitDir, "refs", "remotes");
26421
+ const mine = join21(remotes, remote.out);
26205
26422
  let inner = null;
26206
26423
  let mid = null;
26207
26424
  const armInner = () => {
26208
26425
  inner?.close();
26209
- inner = existsSync24(mine) ? tryWatch(mine) : null;
26426
+ inner = existsSync25(mine) ? tryWatch(mine) : null;
26210
26427
  };
26211
26428
  const armMid = () => {
26212
26429
  mid?.close();
26213
- mid = existsSync24(remotes) ? tryWatch(remotes, (_event, filename) => {
26430
+ mid = existsSync25(remotes) ? tryWatch(remotes, (_event, filename) => {
26214
26431
  if (!filename || filename === remote.out)
26215
26432
  armInner();
26216
26433
  schedule();
26217
26434
  }) : null;
26218
26435
  armInner();
26219
26436
  };
26220
- tryWatch(join20(gitDir, "refs"), (_event, filename) => {
26437
+ tryWatch(join21(gitDir, "refs"), (_event, filename) => {
26221
26438
  if (!filename || filename === "remotes")
26222
26439
  armMid();
26223
26440
  schedule();
@@ -26246,7 +26463,7 @@ function watchWorktreeDir(repoPath, onChange) {
26246
26463
  }, 300);
26247
26464
  };
26248
26465
  const arm = (commonDir) => {
26249
- const dir = join20(commonDir, "worktrees");
26466
+ const dir = join21(commonDir, "worktrees");
26250
26467
  const armInner = () => {
26251
26468
  inner?.close();
26252
26469
  inner = null;
@@ -26289,7 +26506,7 @@ function watchWorktreeDir(repoPath, onChange) {
26289
26506
  function watchConfigFile(repoPath, onChange) {
26290
26507
  const atRoot = new Set([CONFIG_FILES.root.shared, CONFIG_FILES.root.local, CONFIG_DIR]);
26291
26508
  const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) => basename7(rel)));
26292
- const dir = join20(repoPath, CONFIG_DIR);
26509
+ const dir = join21(repoPath, CONFIG_DIR);
26293
26510
  let timer = null;
26294
26511
  let outer = null;
26295
26512
  let inner = null;
@@ -26304,7 +26521,7 @@ function watchConfigFile(repoPath, onChange) {
26304
26521
  const armInner = () => {
26305
26522
  inner?.close();
26306
26523
  inner = null;
26307
- if (!existsSync24(dir))
26524
+ if (!existsSync25(dir))
26308
26525
  return;
26309
26526
  try {
26310
26527
  inner = watch(dir, (_event, filename) => {
@@ -26358,7 +26575,7 @@ class RepoRegistry {
26358
26575
  }
26359
26576
  async boot() {
26360
26577
  const { state } = this.d;
26361
- const gone = state.worktrees.filter((wt) => !existsSync25(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
26578
+ const gone = state.worktrees.filter((wt) => !existsSync26(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
26362
26579
  for (const wt of gone)
26363
26580
  await this.d.worktrees.forgetGone(wt);
26364
26581
  for (const repo of state.repos) {
@@ -26426,7 +26643,7 @@ class RepoRegistry {
26426
26643
  return `${repo.name} is set up now`;
26427
26644
  if (main.promptedAt || main.lastTurn)
26428
26645
  return `${repo.name} has a chat now`;
26429
- if (existsSync25(join21(repo.path, ".claude")))
26646
+ if (existsSync26(join22(repo.path, ".claude")))
26430
26647
  return `an agent has already run in ${repo.name}`;
26431
26648
  if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
26432
26649
  return `${repo.name} has files in it now`;
@@ -26507,7 +26724,7 @@ class RepoRegistry {
26507
26724
  }
26508
26725
  async register(rawPath, made) {
26509
26726
  const path = expandTilde(rawPath);
26510
- if (!existsSync25(path))
26727
+ if (!existsSync26(path))
26511
26728
  throw new UserError(`${path} does not exist`);
26512
26729
  if (!await isGitRepo(path))
26513
26730
  throw new UserError(`${path} is not a git repository`);
@@ -26581,10 +26798,10 @@ class RepoRegistry {
26581
26798
  throw new UserError(current.reason);
26582
26799
  const was = configTarget(repo.path, !!repo.made);
26583
26800
  const rel = configSibling(was, kind);
26584
- const moved = rel !== was && existsSync25(join21(repo.path, was));
26801
+ const moved = rel !== was && existsSync26(join22(repo.path, was));
26585
26802
  if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
26586
26803
  try {
26587
- rmSync4(join21(repo.path, was));
26804
+ rmSync4(join22(repo.path, was));
26588
26805
  } catch (e) {
26589
26806
  log.warn(repoId, `could not remove ${was}`, e);
26590
26807
  }
@@ -26593,10 +26810,10 @@ class RepoRegistry {
26593
26810
  }
26594
26811
  if (isLocalConfigFile(rel))
26595
26812
  await excludeFromGit(repo.path, rel);
26596
- const file = join21(repo.path, rel);
26813
+ const file = join22(repo.path, rel);
26597
26814
  try {
26598
- mkdirSync7(dirname8(file), { recursive: true });
26599
- writeFileSync10(file, configText(repo.path, rel, config));
26815
+ mkdirSync8(dirname9(file), { recursive: true });
26816
+ writeFileSync11(file, configText(repo.path, rel, config));
26600
26817
  } catch (e) {
26601
26818
  log.warn(repoId, `could not write ${rel}`, e);
26602
26819
  }
@@ -26697,7 +26914,7 @@ class RepoRegistry {
26697
26914
  }
26698
26915
  placeConfig(repo) {
26699
26916
  const rel = configTarget(repo.path, !!repo.made);
26700
- if (isLocalConfigFile(rel) && existsSync25(join21(repo.path, rel))) {
26917
+ if (isLocalConfigFile(rel) && existsSync26(join22(repo.path, rel))) {
26701
26918
  fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
26702
26919
  }
26703
26920
  if (repo.configFile === rel)
@@ -26789,7 +27006,7 @@ class RepoRegistry {
26789
27006
  }
26790
27007
 
26791
27008
  // ../daemon/src/routes/service.ts
26792
- import { join as join22 } from "path";
27009
+ import { join as join23 } from "path";
26793
27010
 
26794
27011
  // ../daemon/src/routes/fileRouters.ts
26795
27012
  var SKIP = /(^|\/)(node_modules|dist|build|out|coverage|\.next|\.nuxt|\.output|\.svelte-kit|\.vercel|\.netlify)\//;
@@ -28002,7 +28219,7 @@ class RouteService {
28002
28219
  return mergeRoutes(fileRoutes(paths, roots), code);
28003
28220
  }
28004
28221
  async cachedRead(root, path, had, kept, decode) {
28005
- const abs = join22(root, path);
28222
+ const abs = join23(root, path);
28006
28223
  const stat = await this.fs.stat(abs);
28007
28224
  if (!stat)
28008
28225
  return null;
@@ -28027,7 +28244,7 @@ class RouteService {
28027
28244
  }
28028
28245
  }
28029
28246
  async hash(root, file) {
28030
- const abs = join22(root, file);
28247
+ const abs = join23(root, file);
28031
28248
  const stat = await this.fs.stat(abs);
28032
28249
  if (!stat)
28033
28250
  return null;
@@ -28289,7 +28506,7 @@ function isAlive(pid) {
28289
28506
  }
28290
28507
 
28291
28508
  // ../daemon/src/runtime/bridge-script.ts
28292
- import { existsSync as existsSync26, readFileSync as readFileSync15, statSync as statSync10 } from "fs";
28509
+ import { existsSync as existsSync27, readFileSync as readFileSync16, statSync as statSync10 } from "fs";
28293
28510
  var LEARNED_MAX = 8;
28294
28511
 
28295
28512
  class BridgeScript {
@@ -28319,7 +28536,7 @@ class BridgeScript {
28319
28536
  this.cache = null;
28320
28537
  }
28321
28538
  get() {
28322
- if (!existsSync26(this.path))
28539
+ if (!existsSync27(this.path))
28323
28540
  return "// toyon bridge not built";
28324
28541
  const s = statSync10(this.path);
28325
28542
  const stamp = `${s.mtimeMs}:${s.size}`;
@@ -28328,7 +28545,7 @@ class BridgeScript {
28328
28545
  this.stamp = stamp;
28329
28546
  const origins = [...this.origins, ...this.learned];
28330
28547
  this.cache = `window.__toyonShellOrigins=${JSON.stringify(origins)};
28331
- ${readFileSync15(this.path, "utf8")}`;
28548
+ ${readFileSync16(this.path, "utf8")}`;
28332
28549
  return this.cache;
28333
28550
  }
28334
28551
  }
@@ -28397,50 +28614,50 @@ import { homedir as homedir12, hostname as hostname2 } from "os";
28397
28614
  import { randomBytes as randomBytes7 } from "crypto";
28398
28615
 
28399
28616
  // ../daemon/src/agent/config.ts
28400
- import { existsSync as existsSync27, readFileSync as readFileSync16 } from "fs";
28617
+ import { existsSync as existsSync28, readFileSync as readFileSync17 } from "fs";
28401
28618
  import { homedir as homedir11 } from "os";
28402
- import { join as join23 } from "path";
28619
+ import { join as join24 } from "path";
28403
28620
  function agentConfigFiles(spec, repoPath, home = homedir11()) {
28404
28621
  const file = (id, label, path) => ({
28405
28622
  id,
28406
28623
  label,
28407
28624
  path,
28408
- exists: existsSync27(path)
28625
+ exists: existsSync28(path)
28409
28626
  });
28410
28627
  if (spec.id === "claude") {
28411
28628
  return [
28412
- file("user-config", "user config (MCP servers, projects)", join23(home, ".claude.json")),
28413
- file("user-settings", "user settings (permissions, hooks)", join23(home, ".claude", "settings.json")),
28414
- ...repoPath ? [file("project-mcp", "project MCP servers", join23(repoPath, ".mcp.json"))] : [],
28415
- ...repoPath ? [file("project-settings", "project settings", join23(repoPath, ".claude", "settings.json"))] : []
28629
+ file("user-config", "user config (MCP servers, projects)", join24(home, ".claude.json")),
28630
+ file("user-settings", "user settings (permissions, hooks)", join24(home, ".claude", "settings.json")),
28631
+ ...repoPath ? [file("project-mcp", "project MCP servers", join24(repoPath, ".mcp.json"))] : [],
28632
+ ...repoPath ? [file("project-settings", "project settings", join24(repoPath, ".claude", "settings.json"))] : []
28416
28633
  ];
28417
28634
  }
28418
28635
  if (spec.id === "codex")
28419
- return [file("user-config", "config", join23(home, ".codex", "config.toml"))];
28636
+ return [file("user-config", "config", join24(home, ".codex", "config.toml"))];
28420
28637
  return [];
28421
28638
  }
28422
28639
  function describeAgentConfig(spec, repoPath, home = homedir11()) {
28423
28640
  const files = agentConfigFiles(spec, repoPath, home);
28424
28641
  const servers = [];
28425
28642
  if (spec.id === "claude") {
28426
- const user = readJson(join23(home, ".claude.json"));
28643
+ const user = readJson(join24(home, ".claude.json"));
28427
28644
  servers.push(...serversOf(user?.mcpServers, "user"));
28428
28645
  if (repoPath) {
28429
28646
  const projects = user?.projects;
28430
28647
  const mine = projects?.[repoPath];
28431
28648
  servers.push(...serversOf(mine?.mcpServers, "local"));
28432
- servers.push(...serversOf(readJson(join23(repoPath, ".mcp.json"))?.mcpServers, "project"));
28649
+ servers.push(...serversOf(readJson(join24(repoPath, ".mcp.json"))?.mcpServers, "project"));
28433
28650
  }
28434
28651
  } else if (spec.id === "codex") {
28435
- servers.push(...codexServers(join23(home, ".codex", "config.toml")));
28652
+ servers.push(...codexServers(join24(home, ".codex", "config.toml")));
28436
28653
  }
28437
28654
  return { agent: spec.id, files, servers };
28438
28655
  }
28439
28656
  function readJson(path) {
28440
- if (!existsSync27(path))
28657
+ if (!existsSync28(path))
28441
28658
  return null;
28442
28659
  try {
28443
- const v = JSON.parse(readFileSync16(path, "utf8"));
28660
+ const v = JSON.parse(readFileSync17(path, "utf8"));
28444
28661
  return v && typeof v === "object" && !Array.isArray(v) ? v : null;
28445
28662
  } catch (e) {
28446
28663
  log.warn("agent-config", `${path} is not valid JSON`, e);
@@ -28458,11 +28675,11 @@ function serversOf(raw, scope) {
28458
28675
  });
28459
28676
  }
28460
28677
  function codexServers(path) {
28461
- if (!existsSync27(path))
28678
+ if (!existsSync28(path))
28462
28679
  return [];
28463
28680
  const out = [];
28464
28681
  let current = null;
28465
- for (const raw of readFileSync16(path, "utf8").split(`
28682
+ for (const raw of readFileSync17(path, "utf8").split(`
28466
28683
  `)) {
28467
28684
  const line = raw.trim();
28468
28685
  const header = /^\[mcp_servers\.("?)([^\]"]+)\1\]$/.exec(line);
@@ -29381,6 +29598,7 @@ var handlers = {
29381
29598
  },
29382
29599
  continue(msg, _ctx, s) {
29383
29600
  s.fix.goOn(msg.worktreeId);
29601
+ s.worktrees.markPrompted(msg.worktreeId);
29384
29602
  },
29385
29603
  async "write-file"(msg, ctx, s) {
29386
29604
  const head = { t: "file-written", worktreeId: msg.worktreeId, path: msg.path, seq: msg.seq };
@@ -29497,7 +29715,7 @@ var handlers = {
29497
29715
  },
29498
29716
  "agent-decide"(msg, _ctx, s) {
29499
29717
  requireRun(s, msg.worktreeId);
29500
- s.runtime.agentFor(msg.worktreeId)?.answer(msg.askId, { kind: "choice", choiceId: msg.choiceId });
29718
+ s.runtime.agentFor(msg.worktreeId)?.answer(msg.askId, { kind: "choice", choiceId: msg.choiceId, ...msg.note ? { note: msg.note } : {} });
29501
29719
  s.worktrees.markPrompted(msg.worktreeId);
29502
29720
  },
29503
29721
  async "handoff-answer"(msg, _ctx, s) {
@@ -29650,9 +29868,9 @@ async function dispatch(msg, ctx, s) {
29650
29868
  }
29651
29869
 
29652
29870
  // ../daemon/src/server/http.ts
29653
- import { existsSync as existsSync28 } from "fs";
29871
+ import { existsSync as existsSync29 } from "fs";
29654
29872
  import { hostname } from "os";
29655
- import { join as join24 } from "path";
29873
+ import { join as join25 } from "path";
29656
29874
 
29657
29875
  // ../daemon/src/server/cors.ts
29658
29876
  var ALLOW_HEADERS = "authorization, content-type";
@@ -29955,7 +30173,7 @@ function createFetch(opts) {
29955
30173
  return new Response("unauthorized", { status: 401 });
29956
30174
  const id = url.pathname.slice("/uploads/".length);
29957
30175
  const path = opts.attachments.uploads.path(id);
29958
- if (!path || !existsSync28(path))
30176
+ if (!path || !existsSync29(path))
29959
30177
  return new Response("not found", { status: 404 });
29960
30178
  return new Response(Bun.file(path), {
29961
30179
  headers: { "cache-control": IMMUTABLE, ...attachedHeaders(opts.attachments.uploads.imageType(id)) }
@@ -29968,7 +30186,7 @@ function createFetch(opts) {
29968
30186
  const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
29969
30187
  if (worktreeId && file)
29970
30188
  await opts.attachments.whenWritten(worktreeId, file);
29971
- const path = places.find((p) => !!p && existsSync28(p));
30189
+ const path = places.find((p) => !!p && existsSync29(p));
29972
30190
  if (!path)
29973
30191
  return new Response("not found", { status: 404 });
29974
30192
  if (url.searchParams.has("where")) {
@@ -30006,12 +30224,12 @@ function createFetch(opts) {
30006
30224
  });
30007
30225
  }
30008
30226
  const rel = url.pathname === "/" ? "/index.html" : url.pathname;
30009
- const file = join24(opts.shellDist, rel.replaceAll("..", ""));
30227
+ const file = join25(opts.shellDist, rel.replaceAll("..", ""));
30010
30228
  const hashed = rel.startsWith("/assets/");
30011
- if (existsSync28(file) && Bun.file(file).size > 0) {
30229
+ if (existsSync29(file) && Bun.file(file).size > 0) {
30012
30230
  if (rel === "/manifest.json") {
30013
- const { bar, ground } = opts.manifestColors();
30014
- const manifest = { ...await Bun.file(file).json(), theme_color: bar, background_color: ground };
30231
+ const ground = opts.manifestGround();
30232
+ const manifest = { ...await Bun.file(file).json(), theme_color: ground, background_color: ground };
30015
30233
  return Response.json(manifest, {
30016
30234
  headers: { "content-type": "application/manifest+json", "cache-control": NO_STORE2 }
30017
30235
  });
@@ -30027,8 +30245,8 @@ function createFetch(opts) {
30027
30245
  }
30028
30246
  if (hashed)
30029
30247
  return new Response("not found", { status: 404 });
30030
- const index = join24(opts.shellDist, "index.html");
30031
- if (existsSync28(index))
30248
+ const index = join25(opts.shellDist, "index.html");
30249
+ if (existsSync29(index))
30032
30250
  return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE2 } });
30033
30251
  return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
30034
30252
  }
@@ -30203,6 +30421,7 @@ function startServer(opts) {
30203
30421
  };
30204
30422
  s.hub.on("worktreesChanged", worktreesChanged);
30205
30423
  s.hub.on("agentStatus", worktreesChanged);
30424
+ s.hub.on("agentStopping", worktreesChanged);
30206
30425
  s.hub.on("reposChanged", () => broadcast({ t: "repos", repos: s.state.repos }));
30207
30426
  s.hub.on("pendingChanged", () => broadcast({ t: "pending-repos", pending: s.repos.pending }));
30208
30427
  s.hub.on("proc", (worktreeId, proc) => broadcast({ t: "proc", worktreeId, proc }));
@@ -30349,10 +30568,7 @@ function startServer(opts) {
30349
30568
  managed: { source: opts.managed.source, hash: opts.managed.hash },
30350
30569
  preview: (id) => s.runtime.get(id)?.proxy?.handler ?? null,
30351
30570
  bootstrap: helloFrame,
30352
- manifestColors: () => {
30353
- const { surface0, surface1 } = s.themes.current().colors;
30354
- return { bar: surface0, ground: surface1 };
30355
- },
30571
+ manifestGround: () => s.themes.current().colors.surface1,
30356
30572
  open: (path) => s.opens.open(path),
30357
30573
  restart: (now) => s.restarter.request({ now }),
30358
30574
  restartWait: () => ({ waiting: s.restarter.waitingOn(), asking: s.restarter.asking() }),
@@ -30477,16 +30693,16 @@ function startServer(opts) {
30477
30693
  }
30478
30694
 
30479
30695
  // ../daemon/src/themes/store.ts
30480
- import { existsSync as existsSync29, readdirSync as readdirSync8, readFileSync as readFileSync17, statSync as statSync11, writeFileSync as writeFileSync11 } from "fs";
30696
+ import { existsSync as existsSync30, readdirSync as readdirSync8, readFileSync as readFileSync18, statSync as statSync11, writeFileSync as writeFileSync12 } from "fs";
30481
30697
  import { homedir as homedir13 } from "os";
30482
- import { basename as basename9, dirname as dirname9, join as join25, resolve as resolve9 } from "path";
30698
+ import { basename as basename9, dirname as dirname10, join as join26, resolve as resolve9 } from "path";
30483
30699
  var home = homedir13();
30484
30700
  var defaultExtensionDirs = [
30485
- join25(home, ".vscode", "extensions"),
30486
- join25(home, ".vscode-insiders", "extensions"),
30487
- join25(home, ".vscode-oss", "extensions"),
30488
- join25(home, ".cursor", "extensions"),
30489
- join25(home, ".windsurf", "extensions"),
30701
+ join26(home, ".vscode", "extensions"),
30702
+ join26(home, ".vscode-insiders", "extensions"),
30703
+ join26(home, ".vscode-oss", "extensions"),
30704
+ join26(home, ".cursor", "extensions"),
30705
+ join26(home, ".windsurf", "extensions"),
30490
30706
  "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions",
30491
30707
  "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions",
30492
30708
  "/Applications/Cursor.app/Contents/Resources/app/extensions",
@@ -30570,14 +30786,14 @@ class ThemeStore {
30570
30786
  name: json?.name ?? base,
30571
30787
  source: "file"
30572
30788
  });
30573
- writeFileSync11(join25(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
30789
+ writeFileSync12(join26(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
30574
30790
  `);
30575
30791
  this.load();
30576
30792
  return theme;
30577
30793
  }
30578
30794
  }
30579
30795
  function readJsonc(file) {
30580
- return parse6(readFileSync17(file, "utf8"), [], { allowTrailingComma: true });
30796
+ return parse6(readFileSync18(file, "utf8"), [], { allowTrailingComma: true });
30581
30797
  }
30582
30798
  function readVscodeTheme(file, depth = 0) {
30583
30799
  const json = readJsonc(file);
@@ -30585,7 +30801,7 @@ function readVscodeTheme(file, depth = 0) {
30585
30801
  throw new ThemeImportError(`${file}: not a JSON object`);
30586
30802
  const t = json;
30587
30803
  if (typeof t.include === "string" && depth < 5) {
30588
- const parent = readVscodeTheme(resolve9(dirname9(file), t.include), depth + 1);
30804
+ const parent = readVscodeTheme(resolve9(dirname10(file), t.include), depth + 1);
30589
30805
  return {
30590
30806
  ...parent,
30591
30807
  ...t,
@@ -30599,13 +30815,13 @@ function readVscodeTheme(file, depth = 0) {
30599
30815
  return t;
30600
30816
  }
30601
30817
  function loadDir(dir) {
30602
- if (!existsSync29(dir))
30818
+ if (!existsSync30(dir))
30603
30819
  return [];
30604
30820
  const out = [];
30605
30821
  for (const f of readdirSync8(dir)) {
30606
30822
  if (!/\.jsonc?$/i.test(f))
30607
30823
  continue;
30608
- const file = join25(dir, f);
30824
+ const file = join26(dir, f);
30609
30825
  const id = `file:${slug(f.replace(/\.(json|jsonc)$/i, ""))}`;
30610
30826
  try {
30611
30827
  const json = readJsonc(file);
@@ -30621,7 +30837,7 @@ function loadDir(dir) {
30621
30837
  return out;
30622
30838
  }
30623
30839
  function discover(dir) {
30624
- if (!existsSync29(dir))
30840
+ if (!existsSync30(dir))
30625
30841
  return [];
30626
30842
  const out = [];
30627
30843
  let entries;
@@ -30631,21 +30847,21 @@ function discover(dir) {
30631
30847
  return out;
30632
30848
  }
30633
30849
  for (const name of entries) {
30634
- const extDir = join25(dir, name);
30635
- const pkgFile = join25(extDir, "package.json");
30850
+ const extDir = join26(dir, name);
30851
+ const pkgFile = join26(extDir, "package.json");
30636
30852
  try {
30637
- if (!statSync11(extDir).isDirectory() || !existsSync29(pkgFile))
30853
+ if (!statSync11(extDir).isDirectory() || !existsSync30(pkgFile))
30638
30854
  continue;
30639
- const pkg = JSON.parse(readFileSync17(pkgFile, "utf8"));
30855
+ const pkg = JSON.parse(readFileSync18(pkgFile, "utf8"));
30640
30856
  const themes = pkg.contributes?.themes;
30641
30857
  if (!Array.isArray(themes))
30642
30858
  continue;
30643
30859
  const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
30644
30860
  let nls = {};
30645
- const nlsFile = join25(extDir, "package.nls.json");
30646
- if (existsSync29(nlsFile)) {
30861
+ const nlsFile = join26(extDir, "package.nls.json");
30862
+ if (existsSync30(nlsFile)) {
30647
30863
  try {
30648
- nls = JSON.parse(readFileSync17(nlsFile, "utf8"));
30864
+ nls = JSON.parse(readFileSync18(nlsFile, "utf8"));
30649
30865
  } catch {}
30650
30866
  }
30651
30867
  for (const c of themes) {
@@ -31057,16 +31273,16 @@ class BackendShare {
31057
31273
 
31058
31274
  // ../daemon/src/worktrees/cache.ts
31059
31275
  import { createHash as createHash4 } from "crypto";
31060
- import { existsSync as existsSync30, readFileSync as readFileSync18 } from "fs";
31276
+ import { existsSync as existsSync31, readFileSync as readFileSync19 } from "fs";
31061
31277
  import { mkdir as mkdir5, readdir as readdir2, rename, rm as rm4, stat as stat4, utimes, writeFile as writeFile3 } from "fs/promises";
31062
- import { join as join26 } from "path";
31278
+ import { join as join27 } from "path";
31063
31279
 
31064
31280
  // ../daemon/src/core/clone.ts
31065
31281
  import { mkdir as mkdir4, rm as rm3 } from "fs/promises";
31066
- import { dirname as dirname10 } from "path";
31282
+ import { dirname as dirname11 } from "path";
31067
31283
  async function cloneTree(src, dst) {
31068
31284
  const started = Date.now();
31069
- await mkdir4(dirname10(dst), { recursive: true });
31285
+ await mkdir4(dirname11(dst), { recursive: true });
31070
31286
  const attempts = [
31071
31287
  ["clonefile", ["-Rc", src, dst]],
31072
31288
  ["reflink", ["-R", "--reflink=auto", src, dst]],
@@ -31074,7 +31290,7 @@ async function cloneTree(src, dst) {
31074
31290
  ];
31075
31291
  let error = "";
31076
31292
  for (const [how, args] of attempts) {
31077
- const r = await run("cp", args, dirname10(dst));
31293
+ const r = await run("cp", args, dirname11(dst));
31078
31294
  if (r.ok)
31079
31295
  return { ok: true, how, ms: Date.now() - started };
31080
31296
  error = r.err.split(`
@@ -31090,7 +31306,7 @@ var STALE_MS = 30 * 24 * 3600000;
31090
31306
  var TMP = ".tmp-";
31091
31307
  var MANIFEST = "manifest.json";
31092
31308
  function defaultTools(dir) {
31093
- const has = (...files) => files.some((f) => existsSync30(join26(dir, f)));
31309
+ const has = (...files) => files.some((f) => existsSync31(join27(dir, f)));
31094
31310
  const out = [];
31095
31311
  if (has("bun.lock", "bun.lockb"))
31096
31312
  out.push("bun --version");
@@ -31103,7 +31319,7 @@ function defaultTools(dir) {
31103
31319
  return out;
31104
31320
  }
31105
31321
  function withSidecars(root, rel) {
31106
- return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) => existsSync30(join26(root, n)));
31322
+ return [rel, ...SIDECARS.map((s) => rel + s)].filter((n) => existsSync31(join27(root, n)));
31107
31323
  }
31108
31324
  var entryName = (base, at) => `${base.slice(0, 12)}-${at.toString(36)}`;
31109
31325
  async function listEntries(familyDir) {
@@ -31118,8 +31334,8 @@ async function listEntries(familyDir) {
31118
31334
  const m = /^([0-9a-f]{12})-([0-9a-z]+)$/.exec(name);
31119
31335
  if (!m?.[1] || !m[2])
31120
31336
  continue;
31121
- const dir = join26(familyDir, name);
31122
- if (!existsSync30(join26(dir, MANIFEST)))
31337
+ const dir = join27(familyDir, name);
31338
+ if (!existsSync31(join27(dir, MANIFEST)))
31123
31339
  continue;
31124
31340
  out.push({ dir, base: m[1], at: Number.parseInt(m[2], 36) });
31125
31341
  }
@@ -31149,7 +31365,7 @@ class ArtifactCache {
31149
31365
  try {
31150
31366
  const family = await this.family(wt, repo, policy);
31151
31367
  const base = await this.baseCommit(wt, repo);
31152
- const entries = await listEntries(join26(this.d.paths.cacheDir, repo.id, family.key));
31368
+ const entries = await listEntries(join27(this.d.paths.cacheDir, repo.id, family.key));
31153
31369
  const entry = entries.find((e) => e.base === base.slice(0, 12)) ?? entries[0];
31154
31370
  if (!entry) {
31155
31371
  say(`cache: nothing kept for these lockfiles and tools on ${family.platform}`);
@@ -31157,14 +31373,14 @@ class ArtifactCache {
31157
31373
  }
31158
31374
  const restored = [];
31159
31375
  for (const rel of policy.paths) {
31160
- if (existsSync30(join26(wt.path, rel)))
31376
+ if (existsSync31(join27(wt.path, rel)))
31161
31377
  continue;
31162
31378
  const names = withSidecars(entry.dir, rel);
31163
31379
  if (names.length === 0)
31164
31380
  continue;
31165
31381
  let whole = true;
31166
31382
  for (const name of names) {
31167
- const r = await (this.d.clone ?? cloneTree)(join26(entry.dir, name), join26(wt.path, name));
31383
+ const r = await (this.d.clone ?? cloneTree)(join27(entry.dir, name), join27(wt.path, name));
31168
31384
  if (r.ok)
31169
31385
  continue;
31170
31386
  say(`cache: could not restore ${name}: ${r.error}`);
@@ -31198,12 +31414,12 @@ class ArtifactCache {
31198
31414
  let tmp;
31199
31415
  let slot;
31200
31416
  try {
31201
- const present = policy.paths.filter((rel) => existsSync30(join26(wt.path, rel)));
31417
+ const present = policy.paths.filter((rel) => existsSync31(join27(wt.path, rel)));
31202
31418
  if (present.length === 0)
31203
31419
  return;
31204
31420
  const family = await this.family(wt, repo, policy);
31205
31421
  const base = await this.baseCommit(wt, repo);
31206
- const familyDir = join26(this.d.paths.cacheDir, repo.id, family.key);
31422
+ const familyDir = join27(this.d.paths.cacheDir, repo.id, family.key);
31207
31423
  const key = `${familyDir}/${base.slice(0, 12)}`;
31208
31424
  const kept = (await listEntries(familyDir)).find((e) => e.base === base.slice(0, 12));
31209
31425
  if (kept || this.making.has(key)) {
@@ -31214,21 +31430,21 @@ class ArtifactCache {
31214
31430
  this.making.add(key);
31215
31431
  const at = Date.now();
31216
31432
  const name = entryName(base, at);
31217
- tmp = join26(familyDir, `${TMP}${name}`);
31433
+ tmp = join27(familyDir, `${TMP}${name}`);
31218
31434
  await mkdir5(tmp, { recursive: true });
31219
31435
  let how = "clonefile";
31220
31436
  for (const rel of present) {
31221
31437
  for (const n of withSidecars(wt.path, rel)) {
31222
- const r = await (this.d.clone ?? cloneTree)(join26(wt.path, n), join26(tmp, n));
31438
+ const r = await (this.d.clone ?? cloneTree)(join27(wt.path, n), join27(tmp, n));
31223
31439
  if (!r.ok)
31224
31440
  throw new Error(`could not keep ${n}: ${r.error}`);
31225
31441
  how = r.how;
31226
31442
  }
31227
31443
  }
31228
31444
  const manifest = { at, base, platform: family.platform, tools: family.tools, paths: present };
31229
- await writeFile3(join26(tmp, MANIFEST), `${JSON.stringify(manifest, null, 2)}
31445
+ await writeFile3(join27(tmp, MANIFEST), `${JSON.stringify(manifest, null, 2)}
31230
31446
  `);
31231
- await rename(tmp, join26(familyDir, name));
31447
+ await rename(tmp, join27(familyDir, name));
31232
31448
  tmp = undefined;
31233
31449
  log.info(worktreeId, `cache: kept ${list(present)} for ${base.slice(0, 7)} via ${how} in ${Date.now() - started}ms`);
31234
31450
  await this.prune(familyDir);
@@ -31256,7 +31472,7 @@ class ArtifactCache {
31256
31472
  return;
31257
31473
  }
31258
31474
  for (const repoId of repoIds) {
31259
- const repoDir = join26(root, repoId);
31475
+ const repoDir = join27(root, repoId);
31260
31476
  if (!this.d.state.repo(repoId)) {
31261
31477
  await rm4(repoDir, { recursive: true, force: true });
31262
31478
  continue;
@@ -31268,7 +31484,7 @@ class ArtifactCache {
31268
31484
  continue;
31269
31485
  }
31270
31486
  for (const family of families) {
31271
- const familyDir = join26(repoDir, family);
31487
+ const familyDir = join27(repoDir, family);
31272
31488
  let names;
31273
31489
  try {
31274
31490
  names = await readdir2(familyDir);
@@ -31277,7 +31493,7 @@ class ArtifactCache {
31277
31493
  }
31278
31494
  for (const n of names)
31279
31495
  if (n.startsWith(TMP))
31280
- await rm4(join26(familyDir, n), { recursive: true, force: true });
31496
+ await rm4(join27(familyDir, n), { recursive: true, force: true });
31281
31497
  const entries = await listEntries(familyDir);
31282
31498
  let newest = 0;
31283
31499
  for (const e of entries) {
@@ -31297,7 +31513,7 @@ class ArtifactCache {
31297
31513
  for (const f of policy.key) {
31298
31514
  h.update(`\x00${f}\x00`);
31299
31515
  try {
31300
- h.update(readFileSync18(join26(wt.path, f)));
31516
+ h.update(readFileSync19(join27(wt.path, f)));
31301
31517
  } catch {
31302
31518
  h.update("(absent)");
31303
31519
  }
@@ -31502,6 +31718,8 @@ function lastStop(entries) {
31502
31718
  const e = entries[i].event;
31503
31719
  if (e.type === "usage" || e.type === "session-info")
31504
31720
  continue;
31721
+ if (e.type === "turn-end" && (e.stopReason === "failed" || e.stopReason === "auth" || e.stopReason === "lost"))
31722
+ continue;
31505
31723
  if (e.type === "turn-end" && e.stopReason === "interrupted")
31506
31724
  return { kind: "stopped" };
31507
31725
  if (e.type === "agent-error")
@@ -31854,7 +32072,7 @@ class LandingService {
31854
32072
  this.judging.delete(worktreeId);
31855
32073
  }
31856
32074
  note(worktreeId, status) {
31857
- if (status === "working" || status === "waiting")
32075
+ if (isBusy(status))
31858
32076
  this.busy.add(worktreeId);
31859
32077
  else
31860
32078
  this.busy.delete(worktreeId);
@@ -32537,9 +32755,9 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
32537
32755
  }
32538
32756
 
32539
32757
  // ../daemon/src/worktrees/service.ts
32540
- import { cpSync, existsSync as existsSync35, readFileSync as readFileSync20, rmSync as rmSync8 } from "fs";
32758
+ import { cpSync, existsSync as existsSync36, readFileSync as readFileSync21, rmSync as rmSync8 } from "fs";
32541
32759
  import { rm as rm5 } from "fs/promises";
32542
- import { dirname as dirname12, join as join31 } from "path";
32760
+ import { dirname as dirname13, join as join32 } from "path";
32543
32761
 
32544
32762
  // ../daemon/src/git/archive.ts
32545
32763
  import { rmSync as rmSync5 } from "fs";
@@ -32663,7 +32881,7 @@ async function dropLandRefs(repoPath, worktreeId) {
32663
32881
  }
32664
32882
 
32665
32883
  // ../daemon/src/git/land.ts
32666
- import { existsSync as existsSync31 } from "fs";
32884
+ import { existsSync as existsSync32 } from "fs";
32667
32885
  var STEP_TIMEOUT_MS = RUN_CEILING_MS;
32668
32886
  function rowOf(r) {
32669
32887
  return r.shown && r.toolId && r.command ? { toolId: r.toolId, command: r.command, text: r.text } : undefined;
@@ -32797,7 +33015,7 @@ async function midway(worktreePath) {
32797
33015
  if (!r.ok)
32798
33016
  return null;
32799
33017
  const at = r.out.split(`
32800
- `).findIndex((file) => file && existsSync31(file));
33018
+ `).findIndex((file) => file && existsSync32(file));
32801
33019
  return MIDWAY[at]?.[1] ?? null;
32802
33020
  }
32803
33021
  async function takeMainIn(worktreePath, defaultBr, own, w = UNWATCHED, carry = false) {
@@ -33147,8 +33365,8 @@ function compareUrl(remoteUrl, base, branch) {
33147
33365
  }
33148
33366
 
33149
33367
  // ../daemon/src/git/turn.ts
33150
- import { copyFileSync, existsSync as existsSync32, rmSync as rmSync6 } from "fs";
33151
- import { join as join27 } from "path";
33368
+ import { copyFileSync, existsSync as existsSync33, rmSync as rmSync6 } from "fs";
33369
+ import { join as join28 } from "path";
33152
33370
  var turnRef = (worktreeId, which) => `refs/toyon/turns/${worktreeId}/${which}`;
33153
33371
  var SNAPSHOT_SUBJECT2 = "toyon: tree at a send";
33154
33372
  var queue = new Map;
@@ -33166,14 +33384,14 @@ async function treeNow(wtPath, head, dirty) {
33166
33384
  const dir = await git(wtPath, "rev-parse", "--absolute-git-dir");
33167
33385
  if (!dir.ok || !dir.out)
33168
33386
  return null;
33169
- index = join27(dir.out, "toyon-turn-index");
33387
+ index = join28(dir.out, "toyon-turn-index");
33170
33388
  }
33171
33389
  const env = { GIT_INDEX_FILE: index };
33172
33390
  const writeTree = async () => {
33173
33391
  const tree = await run(GIT, ["write-tree"], wtPath, env);
33174
33392
  return tree.ok && tree.out ? tree.out : null;
33175
33393
  };
33176
- if (!existsSync32(index))
33394
+ if (!existsSync33(index))
33177
33395
  was = undefined;
33178
33396
  const paths = dirty && !dirty.some((f) => /[RC]/.test(f.xy)) ? dirty.map((f) => f.path) : null;
33179
33397
  if (was && paths && was.head === head) {
@@ -33188,9 +33406,9 @@ async function treeNow(wtPath, head, dirty) {
33188
33406
  }
33189
33407
  }
33190
33408
  synced.delete(wtPath);
33191
- if (!existsSync32(index)) {
33192
- const own = join27(index, "..", "index");
33193
- if (existsSync32(own))
33409
+ if (!existsSync33(index)) {
33410
+ const own = join28(index, "..", "index");
33411
+ if (existsSync33(own))
33194
33412
  copyFileSync(own, index);
33195
33413
  else if (!(await run(GIT, ["read-tree", "HEAD"], wtPath, env)).ok)
33196
33414
  return null;
@@ -33271,20 +33489,20 @@ async function dropTurnRefs(repoPath, worktreeId) {
33271
33489
  }
33272
33490
 
33273
33491
  // ../daemon/src/worktrees/archive.ts
33274
- import { existsSync as existsSync33, mkdirSync as mkdirSync8, readdirSync as readdirSync9, readFileSync as readFileSync19, renameSync as renameSync6, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
33275
- import { basename as basename11, dirname as dirname11, join as join28 } from "path";
33492
+ import { existsSync as existsSync34, mkdirSync as mkdirSync9, readdirSync as readdirSync9, readFileSync as readFileSync20, renameSync as renameSync7, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
33493
+ import { basename as basename11, dirname as dirname12, join as join29 } from "path";
33276
33494
  class WorktreeArchive {
33277
33495
  dir;
33278
33496
  records = new Map;
33279
33497
  constructor(dir) {
33280
33498
  this.dir = dir;
33281
- mkdirSync8(dir, { recursive: true });
33499
+ mkdirSync9(dir, { recursive: true });
33282
33500
  for (const id of readdirSync9(dir)) {
33283
33501
  const file = this.filesOf(id).record;
33284
- if (!existsSync33(file))
33502
+ if (!existsSync34(file))
33285
33503
  continue;
33286
33504
  try {
33287
- this.records.set(id, JSON.parse(readFileSync19(file, "utf8")));
33505
+ this.records.set(id, JSON.parse(readFileSync20(file, "utf8")));
33288
33506
  } catch (e) {
33289
33507
  log.warn("archive", `skipped an unreadable record: ${file}`, e);
33290
33508
  }
@@ -33295,11 +33513,11 @@ class WorktreeArchive {
33295
33513
  }
33296
33514
  put(rec, from) {
33297
33515
  const at = this.filesOf(rec.worktree.id);
33298
- mkdirSync8(at.dir, { recursive: true });
33516
+ mkdirSync9(at.dir, { recursive: true });
33299
33517
  move(from.transcript, at.transcript);
33300
33518
  move(from.attachments, at.attachments);
33301
33519
  move(from.plans, at.plans);
33302
- writeFileSync12(at.record, JSON.stringify(rec, null, 2));
33520
+ writeFileSync13(at.record, JSON.stringify(rec, null, 2));
33303
33521
  this.records.set(rec.worktree.id, rec);
33304
33522
  return { transcript: at.transcript, attachments: at.attachments, plans: at.plans };
33305
33523
  }
@@ -33328,7 +33546,7 @@ class WorktreeArchive {
33328
33546
  const rec = this.records.get(id);
33329
33547
  if (!rec)
33330
33548
  return;
33331
- writeFileSync12(this.filesOf(id).record, JSON.stringify(rec, null, 2));
33549
+ writeFileSync13(this.filesOf(id).record, JSON.stringify(rec, null, 2));
33332
33550
  }
33333
33551
  chatFiles(id) {
33334
33552
  if (!this.records.has(id))
@@ -33339,24 +33557,24 @@ class WorktreeArchive {
33339
33557
  planFile(id, rel) {
33340
33558
  if (!this.records.has(id) || !isPlanPath(rel))
33341
33559
  return null;
33342
- const file = join28(this.filesOf(id).plans, basename11(rel));
33343
- if (!existsSync33(file))
33560
+ const file = join29(this.filesOf(id).plans, basename11(rel));
33561
+ if (!existsSync34(file))
33344
33562
  return null;
33345
33563
  try {
33346
- return readFileSync19(file, "utf8");
33564
+ return readFileSync20(file, "utf8");
33347
33565
  } catch (e) {
33348
33566
  log.warn(id, `could not read its archived plan ${rel}`, e);
33349
33567
  return null;
33350
33568
  }
33351
33569
  }
33352
33570
  filesOf(id) {
33353
- const dir = join28(this.dir, id);
33571
+ const dir = join29(this.dir, id);
33354
33572
  return {
33355
33573
  dir,
33356
- record: join28(dir, "record.json"),
33357
- transcript: join28(dir, "transcript.jsonl"),
33358
- attachments: join28(dir, "attachments"),
33359
- plans: join28(dir, "plans")
33574
+ record: join29(dir, "record.json"),
33575
+ transcript: join29(dir, "transcript.jsonl"),
33576
+ attachments: join29(dir, "attachments"),
33577
+ plans: join29(dir, "plans")
33360
33578
  };
33361
33579
  }
33362
33580
  }
@@ -33381,9 +33599,9 @@ function summarize(r, repoId, transcript) {
33381
33599
  };
33382
33600
  }
33383
33601
  function lastUsage(transcript) {
33384
- if (!existsSync33(transcript))
33602
+ if (!existsSync34(transcript))
33385
33603
  return null;
33386
- const lines = readFileSync19(transcript, "utf8").split(`
33604
+ const lines = readFileSync20(transcript, "utf8").split(`
33387
33605
  `);
33388
33606
  for (let i = lines.length - 1;i >= 0; i--) {
33389
33607
  if (!lines[i]?.includes('"usage"'))
@@ -33397,9 +33615,9 @@ function lastUsage(transcript) {
33397
33615
  return null;
33398
33616
  }
33399
33617
  function firstPrompt(transcript) {
33400
- if (!existsSync33(transcript))
33618
+ if (!existsSync34(transcript))
33401
33619
  return;
33402
- for (const line of readFileSync19(transcript, "utf8").split(`
33620
+ for (const line of readFileSync20(transcript, "utf8").split(`
33403
33621
  `)) {
33404
33622
  if (!line.includes('"user-message"'))
33405
33623
  continue;
@@ -33412,11 +33630,11 @@ function firstPrompt(transcript) {
33412
33630
  return;
33413
33631
  }
33414
33632
  function move(from, to) {
33415
- if (!existsSync33(from))
33633
+ if (!existsSync34(from))
33416
33634
  return;
33417
- mkdirSync8(dirname11(to), { recursive: true });
33635
+ mkdirSync9(dirname12(to), { recursive: true });
33418
33636
  rmSync7(to, { recursive: true, force: true });
33419
- renameSync6(from, to);
33637
+ renameSync7(from, to);
33420
33638
  }
33421
33639
 
33422
33640
  // ../daemon/src/worktrees/archivedGit.ts
@@ -33494,8 +33712,8 @@ async function fillLandMarks(jobs, read, width = 8) {
33494
33712
  }
33495
33713
 
33496
33714
  // ../daemon/src/worktrees/spare.ts
33497
- import { existsSync as existsSync34 } from "fs";
33498
- import { basename as basename12, join as join29 } from "path";
33715
+ import { existsSync as existsSync35 } from "fs";
33716
+ import { basename as basename12, join as join30 } from "path";
33499
33717
  class SparePool {
33500
33718
  d;
33501
33719
  inflight = new Map;
@@ -33518,7 +33736,7 @@ class SparePool {
33518
33736
  if (!text.trim())
33519
33737
  return;
33520
33738
  const wt = this.d.state.worktree(boxId);
33521
- if (wt?.kind !== "spare" || !existsSync34(wt.path))
33739
+ if (wt?.kind !== "spare" || !existsSync35(wt.path))
33522
33740
  return;
33523
33741
  const agent = this.d.runtime.ensureAgent(wt).agent;
33524
33742
  if (agent.runningAgent === null)
@@ -33563,11 +33781,11 @@ class SparePool {
33563
33781
  return this.record(repoId, repo).then((wt) => this.warmUp(wt, repo, {}));
33564
33782
  }
33565
33783
  async record(repoId, repo) {
33566
- const { id, dir } = freeSlot(join29(this.d.paths.worktreesDir, repo.name));
33784
+ const { id, dir } = freeSlot(join30(this.d.paths.worktreesDir, repo.name));
33567
33785
  const wt = {
33568
33786
  id,
33569
33787
  repoId,
33570
- path: join29(this.d.paths.worktreesDir, repo.name, dir),
33788
+ path: join30(this.d.paths.worktreesDir, repo.name, dir),
33571
33789
  branch: repo.defaultBranch,
33572
33790
  kind: "spare",
33573
33791
  phase: "reserved",
@@ -33582,7 +33800,7 @@ class SparePool {
33582
33800
  const repoId = repo.id;
33583
33801
  let cut = () => {};
33584
33802
  const added = (async () => {
33585
- if (existsSync34(wt.path))
33803
+ if (existsSync35(wt.path))
33586
33804
  return;
33587
33805
  const run = this.d.runs.begin(wt.id, "setup", {
33588
33806
  timeoutMs: timeoutFor(repo.config, "setup"),
@@ -33643,7 +33861,7 @@ class SparePool {
33643
33861
  }
33644
33862
  log.warn(wt.repoId, "spare warm-up failed; rolling back", e);
33645
33863
  this.d.carryDraft?.(wt.id, wt.repoId);
33646
- if (existsSync34(wt.path)) {
33864
+ if (existsSync35(wt.path)) {
33647
33865
  await this.d.discard(wt.id).catch((re) => log.warn(wt.repoId, "spare rollback failed", re));
33648
33866
  return;
33649
33867
  }
@@ -33715,7 +33933,7 @@ class SparePool {
33715
33933
  try {
33716
33934
  await running?.added;
33717
33935
  running?.cut();
33718
- if (!existsSync34(wt.path))
33936
+ if (!existsSync35(wt.path))
33719
33937
  throw new Error("the worktree's directory is gone");
33720
33938
  await this.refreshing.get(repoId);
33721
33939
  await prepare(wt);
@@ -33743,7 +33961,7 @@ class SparePool {
33743
33961
  for (const k of Object.keys(stamps))
33744
33962
  delete wt[k];
33745
33963
  delete wt.promptedAt;
33746
- if (!existsSync34(wt.path)) {
33964
+ if (!existsSync35(wt.path)) {
33747
33965
  await this.rollBack(wt, e);
33748
33966
  return;
33749
33967
  }
@@ -33757,7 +33975,7 @@ class SparePool {
33757
33975
  // ../daemon/src/worktrees/treeState.ts
33758
33976
  import { watch as watch2 } from "fs";
33759
33977
  import { stat as stat5 } from "fs/promises";
33760
- import { join as join30 } from "path";
33978
+ import { join as join31 } from "path";
33761
33979
  var EDIT_DELAY_MS = 150;
33762
33980
  var SHELL_QUIET_MS2 = 1500;
33763
33981
  var WATCH_DELAY_MS = 300;
@@ -34140,7 +34358,7 @@ class TreeState {
34140
34358
  xy: f.xy,
34141
34359
  ...entries.folded.has(f.path) ? { add: f.add, del: f.del } : {}
34142
34360
  }));
34143
- const stats = await Promise.all(files.slice(0, SIGNATURE_STATS).map((f) => stat5(join30(r.path, f.path)).then((s) => `${s.mtimeMs}:${s.size}`, () => "-")));
34361
+ const stats = await Promise.all(files.slice(0, SIGNATURE_STATS).map((f) => stat5(join31(r.path, f.path)).then((s) => `${s.mtimeMs}:${s.size}`, () => "-")));
34144
34362
  const t0 = performance.now();
34145
34363
  const counts = { ...ab, ...unpushed === null ? {} : { unpushed } };
34146
34364
  const signature = String(Bun.hash([
@@ -34449,7 +34667,7 @@ class TurnService {
34449
34667
  }
34450
34668
  }
34451
34669
  function stopOf(prev, next) {
34452
- const busy = prev === "working" || prev === "waiting";
34670
+ const busy = isBusy(prev);
34453
34671
  if (next === "waiting" && prev !== "waiting")
34454
34672
  return "asking";
34455
34673
  if (next === "idle" && busy)
@@ -34666,8 +34884,8 @@ class WorktreeService {
34666
34884
  if (!await isGitRepo(repo.path)) {
34667
34885
  throw new UserError(`git no longer reads ${repo.name} as a checkout, so there is nothing to start a worktree from. If \`git config core.bare\` says true there, \`git config --unset core.bare\` puts it back.`);
34668
34886
  }
34669
- const { id, dir } = freeSlot(join31(this.d.paths.worktreesDir, repo.name));
34670
- const wtPath = join31(this.d.paths.worktreesDir, repo.name, dir);
34887
+ const { id, dir } = freeSlot(join32(this.d.paths.worktreesDir, repo.name));
34888
+ const wtPath = join32(this.d.paths.worktreesDir, repo.name, dir);
34671
34889
  const branch = `toyon/${dir}`;
34672
34890
  await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--no-track", "-b", branch, wtPath, baseOf(repo)));
34673
34891
  const wt = {
@@ -34741,7 +34959,7 @@ class WorktreeService {
34741
34959
  };
34742
34960
  const carried = await git(repo.path, "ls-tree", "-r", "--name-only", `${sha}^3`);
34743
34961
  const restored = (carried.ok ? carried.out.split(`
34744
- `).filter(Boolean) : []).filter((p) => !existsSync35(join31(wt.path, p)));
34962
+ `).filter(Boolean) : []).filter((p) => !existsSync36(join32(wt.path, p)));
34745
34963
  const applied = await git(wt.path, "stash", "apply", sha);
34746
34964
  if (applied.ok) {
34747
34965
  const ref = await entry();
@@ -34778,9 +34996,9 @@ class WorktreeService {
34778
34996
  const branch = kind === "pr" ? `pr/${number}` : ref;
34779
34997
  const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
34780
34998
  let slug = branchSlug(title) || "branch";
34781
- if (existsSync35(join31(this.d.paths.worktreesDir, repo.name, slug)))
34999
+ if (existsSync36(join32(this.d.paths.worktreesDir, repo.name, slug)))
34782
35000
  slug = `${slug}-${shortId().slice(0, 4)}`;
34783
- const wtPath = join31(this.d.paths.worktreesDir, repo.name, slug);
35001
+ const wtPath = join32(this.d.paths.worktreesDir, repo.name, slug);
34784
35002
  await withRepoLock(repo.path, async () => {
34785
35003
  const out = (await listWorktrees(repo.path)).find((w) => w.branch === branch);
34786
35004
  if (out)
@@ -35047,7 +35265,7 @@ class WorktreeService {
35047
35265
  }
35048
35266
  async teardown(wt, repo) {
35049
35267
  const cmds = repo.config.teardown ?? [];
35050
- if (cmds.length === 0 || !existsSync35(wt.path))
35268
+ if (cmds.length === 0 || !existsSync36(wt.path))
35051
35269
  return;
35052
35270
  for (const cmd of cmds) {
35053
35271
  const code = await runSetup(cmd, wt.path, (line) => this.d.hub.emit("log", wt.id, "setup", line), worktreeEnv(wt, repo), { timeoutMs: timeoutFor(repo.config, "setup") });
@@ -35056,7 +35274,7 @@ class WorktreeService {
35056
35274
  }
35057
35275
  }
35058
35276
  async keep(repo, wt) {
35059
- const index = join31(this.d.paths.archiveDir, `${wt.id}.index`);
35277
+ const index = join32(this.d.paths.archiveDir, `${wt.id}.index`);
35060
35278
  const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index) ?? await this.keepUnlinked(repo, wt);
35061
35279
  if (!kept)
35062
35280
  log.warn(wt.id, "could not keep its git state");
@@ -35088,16 +35306,16 @@ class WorktreeService {
35088
35306
  throw new Error(`git worktree remove --force ${wt.path} failed (${r.exit}): ${r.err}`);
35089
35307
  if (await deleteTree(wt.path))
35090
35308
  return;
35091
- await run("chmod", ["-R", "u+w", wt.path], dirname12(wt.path));
35309
+ await run("chmod", ["-R", "u+w", wt.path], dirname13(wt.path));
35092
35310
  if (!await deleteTree(wt.path))
35093
35311
  log.warn(wt.id, `its directory could not be deleted and stays at ${wt.path}`);
35094
35312
  }
35095
35313
  plansScratch(worktreeId) {
35096
- return join31(this.d.paths.archiveDir, `${worktreeId}.plans`);
35314
+ return join32(this.d.paths.archiveDir, `${worktreeId}.plans`);
35097
35315
  }
35098
35316
  keepPlans(wt) {
35099
- const from = join31(wt.path, PLANS_DIR);
35100
- if (!existsSync35(from))
35317
+ const from = join32(wt.path, PLANS_DIR);
35318
+ if (!existsSync36(from))
35101
35319
  return;
35102
35320
  try {
35103
35321
  cpSync(from, this.plansScratch(wt.id), { recursive: true });
@@ -35233,11 +35451,15 @@ class WorktreeService {
35233
35451
  }
35234
35452
  archivedTranscript(archiveId) {
35235
35453
  const files = this.archive.chatFiles(archiveId);
35236
- return files ? new Transcript(files.transcript, archiveId).entries : null;
35454
+ if (!files)
35455
+ return null;
35456
+ const { entries } = new Transcript(files.transcript, archiveId);
35457
+ const seal = sealOpenTurn(entries);
35458
+ return seal ? [...entries, { seq: (entries.at(-1)?.seq ?? -1) + 1, event: seal }] : entries;
35237
35459
  }
35238
35460
  archivedAttachment(archiveId, file) {
35239
35461
  const files = this.archive.chatFiles(archiveId);
35240
- return files && isAttachmentFile(file) ? join31(files.attachments, file) : null;
35462
+ return files && isAttachmentFile(file) ? join32(files.attachments, file) : null;
35241
35463
  }
35242
35464
  archivedGit(archiveId) {
35243
35465
  const rec = this.archive.get(archiveId);
@@ -35293,7 +35515,7 @@ class WorktreeService {
35293
35515
  throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
35294
35516
  if (rec.auto)
35295
35517
  log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
35296
- const path = existsSync35(old.path) ? join31(dirname12(old.path), freeSlot(dirname12(old.path)).dir) : old.path;
35518
+ const path = existsSync36(old.path) ? join32(dirname13(old.path), freeSlot(dirname13(old.path)).dir) : old.path;
35297
35519
  const { variant: _variant, ...rest } = old;
35298
35520
  const wt = {
35299
35521
  ...rest,
@@ -35332,7 +35554,7 @@ class WorktreeService {
35332
35554
  run.finish(0);
35333
35555
  this.arriving.delete(wt.id);
35334
35556
  wt.branch = branch;
35335
- move(chat.plans, join31(path, PLANS_DIR));
35557
+ move(chat.plans, join32(path, PLANS_DIR));
35336
35558
  const dropped = await git(repo.path, "update-ref", "-d", archiveRef(old.id));
35337
35559
  if (!dropped.ok)
35338
35560
  log.warn(old.id, `could not drop its archive ref: ${dropped.err}`);
@@ -35408,12 +35630,12 @@ class WorktreeService {
35408
35630
  return;
35409
35631
  const repo = this.repoOf(rec);
35410
35632
  const repoPath = repo?.path ?? rec.repoPath;
35411
- if (rec.kept && existsSync35(repoPath)) {
35633
+ if (rec.kept && existsSync36(repoPath)) {
35412
35634
  const r = await git(repoPath, "update-ref", "-d", archiveRef(archiveId));
35413
35635
  if (!r.ok)
35414
35636
  log.warn(archiveId, `could not drop its archive ref: ${r.err}`);
35415
35637
  }
35416
- if (rec.worktree.lands?.length && existsSync35(repoPath))
35638
+ if (rec.worktree.lands?.length && existsSync36(repoPath))
35417
35639
  await dropLandRefs(repoPath, archiveId);
35418
35640
  this.archive.delete(archiveId);
35419
35641
  this.d.drafts?.drop(archiveId);
@@ -35474,8 +35696,7 @@ class WorktreeService {
35474
35696
  throw new UserError(`${w.title} cannot be grafted`);
35475
35697
  if (w.repoId !== target.repoId)
35476
35698
  throw new UserError("worktrees must belong to one repo");
35477
- const status = this.d.runtime.agentFor(w.id)?.status;
35478
- if (status === "working" || status === "waiting")
35699
+ if (isBusy(this.d.runtime.agentFor(w.id)?.status))
35479
35700
  throw new UserError(`${w.title}'s agent is mid-turn; stop it first`);
35480
35701
  }
35481
35702
  for (const w of all) {
@@ -35521,10 +35742,10 @@ class WorktreeService {
35521
35742
  }
35522
35743
  }
35523
35744
  async mirrorBaseIgnore(repo) {
35524
- const src = join31(repo.path, ".gitignore");
35525
- const tracked = existsSync35(src) && (await git(repo.path, "ls-files", "--", ".gitignore")).out !== "";
35526
- const mirror = existsSync35(src) && !tracked;
35527
- const lines = mirror ? readFileSync20(src, "utf8").split(`
35745
+ const src = join32(repo.path, ".gitignore");
35746
+ const tracked = existsSync36(src) && (await git(repo.path, "ls-files", "--", ".gitignore")).out !== "";
35747
+ const mirror = existsSync36(src) && !tracked;
35748
+ const lines = mirror ? readFileSync21(src, "utf8").split(`
35528
35749
  `).filter((l) => l.trim() !== "") : [];
35529
35750
  await excludeBlock(repo.path, BASE_IGNORE, lines);
35530
35751
  }
@@ -35536,9 +35757,9 @@ class WorktreeService {
35536
35757
  await this.d.cache.restore(wt, repo, (line) => this.d.hub.emit("log", wt.id, "setup", line));
35537
35758
  run.finish(0);
35538
35759
  }
35539
- const srcNm = join31(depsSource, "node_modules");
35540
- const dstNm = join31(wt.path, "node_modules");
35541
- if (existsSync35(srcNm) && !existsSync35(dstNm)) {
35760
+ const srcNm = join32(depsSource, "node_modules");
35761
+ const dstNm = join32(wt.path, "node_modules");
35762
+ if (existsSync36(srcNm) && !existsSync36(dstNm)) {
35542
35763
  const run = this.runs.begin(wt.id, "setup", { timeoutMs: ceiling, stage: "copying dependencies" });
35543
35764
  const r = await cloneTree(srcNm, dstNm);
35544
35765
  run.finish(0);
@@ -35548,10 +35769,10 @@ class WorktreeService {
35548
35769
  log.warn(wt.id, "could not copy node_modules from the base checkout", r.error);
35549
35770
  }
35550
35771
  for (const f of LOCAL_CONFIG_FILES) {
35551
- const src = join31(depsSource, f);
35552
- if (!existsSync35(src) || existsSync35(join31(wt.path, f)))
35772
+ const src = join32(depsSource, f);
35773
+ if (!existsSync36(src) || existsSync36(join32(wt.path, f)))
35553
35774
  continue;
35554
- const r = await run("cp", [src, join31(wt.path, f)], wt.path);
35775
+ const r = await run("cp", [src, join32(wt.path, f)], wt.path);
35555
35776
  if (r.ok)
35556
35777
  this.d.hub.emit("log", wt.id, "setup", `copied ${f}`);
35557
35778
  else
@@ -35771,8 +35992,7 @@ ${wt.landing.body}` : s;
35771
35992
  });
35772
35993
  }
35773
35994
  async movedMidTurn(wt) {
35774
- const status = this.d.runtime.agentFor(wt.id)?.status;
35775
- if (status !== "working" && status !== "waiting")
35995
+ if (!isBusy(this.d.runtime.agentFor(wt.id)?.status))
35776
35996
  return null;
35777
35997
  const l = wt.landing;
35778
35998
  if (!l || l.fingerprint === await this.fingerprint(wt.path))
@@ -35821,8 +36041,7 @@ ${wt.landing.body}` : s;
35821
36041
  this.ops.delete(id);
35822
36042
  release?.();
35823
36043
  this.d.hub.emit("worktreesChanged");
35824
- const status = this.d.runtime.agentFor(id)?.status;
35825
- if (status !== "working" && status !== "waiting")
36044
+ if (!isBusy(this.d.runtime.agentFor(id)?.status))
35826
36045
  this.syncAfterTurn(id);
35827
36046
  }
35828
36047
  }
@@ -36112,8 +36331,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined);
36112
36331
  const repo = this.d.state.requireRepo(r.repoId);
36113
36332
  const own = r.wt ? hasOwnBranch(r.wt) : false;
36114
36333
  const carry = !!r.wt;
36115
- const status = this.d.runtime.agentFor(worktreeId)?.status;
36116
- if (carry && (status === "working" || status === "waiting") && (await statusFiles(r.path)).length > 0) {
36334
+ const busy = isBusy(this.d.runtime.agentFor(worktreeId)?.status);
36335
+ if (carry && busy && (await statusFiles(r.path)).length > 0) {
36117
36336
  this.syncQueued.add(worktreeId);
36118
36337
  this.d.hub.emit("worktreesChanged");
36119
36338
  return { result: { ok: true, message: `${r.name} syncs from ${r.base} once the turn ends` }, base: r.base };
@@ -36501,6 +36720,7 @@ ${wt.landing.body}` : wt.landing.subject : undefined);
36501
36720
  procs: rt?.procs?.states() ?? [],
36502
36721
  borrowed: rt?.borrowed.size ? [...rt.borrowed] : undefined,
36503
36722
  agent: rt?.agent.status ?? "idle",
36723
+ stopping: rt?.agent.stopping || undefined,
36504
36724
  login: !!rt?.login,
36505
36725
  ahead,
36506
36726
  behind,
@@ -36625,7 +36845,7 @@ class ArchiveSweep {
36625
36845
  process.on("unhandledRejection", (e) => log.error("daemon", "unhandled rejection", e));
36626
36846
  process.on("uncaughtException", (e) => log.error("daemon", "uncaught exception", e));
36627
36847
  log.info("daemon", `toyon ${package_default.version} on bun ${Bun.version}, pid ${process.pid}, ${process.argv[1]}`);
36628
- var here = dirname13(fileURLToPath(import.meta.url));
36848
+ var here = dirname14(fileURLToPath(import.meta.url));
36629
36849
  var {
36630
36850
  shellDist: SHELL_DIST,
36631
36851
  bridgeJs: BRIDGE_JS,
@@ -36641,17 +36861,17 @@ if (pruned > 0)
36641
36861
  var managed = await loadManaged();
36642
36862
  var paths = makePaths();
36643
36863
  ensureDirs(paths);
36644
- forwardTranscripts([paths.transcriptsDir, paths.archiveDir], join32(paths.home, "transcripts-forwarded"));
36864
+ forwardTranscripts([paths.transcriptsDir, paths.archiveDir], join33(paths.home, "transcripts-forwarded"));
36645
36865
  var token = loadOrCreateToken(paths);
36646
36866
  var port = Number(process.env.TOYON_PORT ?? DAEMON_DEFAULT_PORT);
36647
- var helperApp = join32(paths.home, DAEMON_FILES.helper);
36867
+ var helperApp = join33(paths.home, DAEMON_FILES.helper);
36648
36868
  var helper = helperWanted({ platform: process.platform, cloud: cloud.enabled, port, home: process.env.TOYON_HOME }) ? new Helper({
36649
36869
  dir: helperApp,
36650
36870
  stub: HELPER_STUB,
36651
36871
  bun: process.execPath,
36652
36872
  cli: CLI_ENTRY,
36653
- log: join32(paths.home, DAEMON_FILES.launcherLog),
36654
- icon: join32(SHELL_DIST, "icon.svg"),
36873
+ log: join33(paths.home, DAEMON_FILES.launcherLog),
36874
+ icon: join33(SHELL_DIST, "icon.svg"),
36655
36875
  version: package_default.version
36656
36876
  }) : null;
36657
36877
  if (helper) {
@@ -36799,7 +37019,15 @@ var opens = new OpenService({
36799
37019
  hub,
36800
37020
  register: (path) => repos.register(path),
36801
37021
  found: () => worktrees.discovered(),
36802
- refused: [paths.home]
37022
+ refused: [
37023
+ paths.stateFile,
37024
+ paths.draftsFile,
37025
+ paths.draftAttachmentsFile,
37026
+ paths.remoteFile,
37027
+ paths.pidFile,
37028
+ paths.tokenFile,
37029
+ paths.gitCredentialsFile
37030
+ ]
36803
37031
  });
36804
37032
  var idle = new IdlePolicy({
36805
37033
  runtime,
@@ -36945,7 +37173,7 @@ hub.on("remoteChanged", () => {
36945
37173
  fireAndForget("remote", tailnet.refresh(), "ask Tailscale");
36946
37174
  });
36947
37175
  fireAndForget("remote", tailnet.start(), "ask Tailscale");
36948
- writeFileSync13(paths.pidFile, `${process.pid}
37176
+ writeFileSync14(paths.pidFile, `${process.pid}
36949
37177
  `);
36950
37178
  var stopLagSampler = startLagSampler();
36951
37179
  fireAndForget("daemon", run("sh", ["-c", "ulimit -n"], homedir15()).then((r) => {