@standardagents/code 0.11.6 → 0.11.8

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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import os8, { homedir } from 'os';
3
3
  import path4 from 'path';
4
- import readline2 from 'readline/promises';
4
+ import readline3 from 'readline/promises';
5
5
  import { stdout, stdin } from 'process';
6
6
  import net from 'net';
7
7
  import fs5 from 'fs';
@@ -135,6 +135,36 @@ function parseSharedMessagingSnapshot(value) {
135
135
  }
136
136
  };
137
137
  }
138
+ function parseComposerDraft(value) {
139
+ const draft = record(value);
140
+ if (!draft || draft.version !== 1 || typeof draft.content !== "string") {
141
+ throw new TypeError("unsupported or malformed shared draft");
142
+ }
143
+ return {
144
+ version: 1,
145
+ revision: finiteNumber(draft.revision, "draft revision"),
146
+ content: draft.content,
147
+ attachments: attachments(draft.attachments),
148
+ updatedAt: finiteNumber(draft.updatedAt, "draft updatedAt"),
149
+ ...origin(draft)
150
+ };
151
+ }
152
+ function parseMessagingChangedEvent(value) {
153
+ const event = record(value);
154
+ if (!event || event.version !== 1) return null;
155
+ try {
156
+ return {
157
+ version: 1,
158
+ pendingRevision: finiteNumber(event.pendingRevision, "event pending revision"),
159
+ draftRevision: finiteNumber(event.draftRevision, "event draft revision"),
160
+ ...event.draft !== void 0 ? { draft: parseComposerDraft(event.draft) } : {},
161
+ ...event.draftOmitted === true ? { draftOmitted: true } : {},
162
+ ...origin(event)
163
+ };
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
138
168
 
139
169
  // src/api.ts
140
170
  var ApiHttpError = class extends Error {
@@ -908,6 +938,7 @@ var TunnelManager = class {
908
938
  let opened = false;
909
939
  const socket = net.connect({ host, port });
910
940
  socket.setNoDelay(true);
941
+ socket.setKeepAlive(true, 15e3);
911
942
  this.tunnels.set(id, { socket });
912
943
  socket.on("connect", () => {
913
944
  opened = true;
@@ -2690,7 +2721,7 @@ var McpManager = class {
2690
2721
  }
2691
2722
  }
2692
2723
  closeAll() {
2693
- for (const [, c5] of this.clients) c5.close();
2724
+ for (const [, c6] of this.clients) c6.close();
2694
2725
  this.clients.clear();
2695
2726
  }
2696
2727
  get(name) {
@@ -2701,13 +2732,13 @@ var McpManager = class {
2701
2732
  }
2702
2733
  toolCount() {
2703
2734
  let n = 0;
2704
- for (const [, c5] of this.clients) n += c5.tools.length;
2735
+ for (const [, c6] of this.clients) n += c6.tools.length;
2705
2736
  return n;
2706
2737
  }
2707
2738
  /** A JSON-serializable catalog of every connected server for the KV/context. */
2708
2739
  catalog() {
2709
2740
  return {
2710
- servers: Array.from(this.clients.values()).map((c5) => c5.catalogEntry()),
2741
+ servers: Array.from(this.clients.values()).map((c6) => c6.catalogEntry()),
2711
2742
  generatedAt: Date.now()
2712
2743
  };
2713
2744
  }
@@ -2797,9 +2828,9 @@ function flattenContent(content, structured) {
2797
2828
  }
2798
2829
  function flattenResourceContents(contents) {
2799
2830
  const parts = [];
2800
- for (const c5 of contents || []) {
2801
- if (typeof c5.text === "string") parts.push(c5.text);
2802
- else if (typeof c5.blob === "string") parts.push(`[binary resource ${String(c5.uri ?? "")} (${c5.blob.length} b64 chars)]`);
2831
+ for (const c6 of contents || []) {
2832
+ if (typeof c6.text === "string") parts.push(c6.text);
2833
+ else if (typeof c6.blob === "string") parts.push(`[binary resource ${String(c6.uri ?? "")} (${c6.blob.length} b64 chars)]`);
2803
2834
  }
2804
2835
  return parts.join("\n").trim();
2805
2836
  }
@@ -3849,17 +3880,17 @@ function wrapBlock(out, cols2, leadFirst, leadRest, leadWidth, text) {
3849
3880
  function renderTable(rows) {
3850
3881
  const cols2 = Math.max(...rows.map((r) => r.length));
3851
3882
  const widths = [];
3852
- for (let c5 = 0; c5 < cols2; c5++) {
3853
- widths[c5] = Math.max(...rows.map((r) => visibleWidth(inline(r[c5] ?? ""))));
3883
+ for (let c6 = 0; c6 < cols2; c6++) {
3884
+ widths[c6] = Math.max(...rows.map((r) => visibleWidth(inline(r[c6] ?? ""))));
3854
3885
  }
3855
3886
  const sep = `${GRAY} \u2502 ${R}`;
3856
3887
  const out = [];
3857
3888
  rows.forEach((r, ri) => {
3858
3889
  const cells = [];
3859
- for (let c5 = 0; c5 < cols2; c5++) {
3860
- const raw = r[c5] ?? "";
3890
+ for (let c6 = 0; c6 < cols2; c6++) {
3891
+ const raw = r[c6] ?? "";
3861
3892
  const styled = ri === 0 ? `${BOLD}${inline(raw)}${R}` : inline(raw);
3862
- cells.push(padEndVisible(styled, widths[c5]));
3893
+ cells.push(padEndVisible(styled, widths[c6]));
3863
3894
  }
3864
3895
  out.push((" " + cells.join(sep)).replace(/\s+$/, ""));
3865
3896
  if (ri === 0) {
@@ -4331,7 +4362,7 @@ function buildInputBoxRows(opts) {
4331
4362
  let di = 0;
4332
4363
  for (let ci = 0; ci < plainChars.length; ci++) {
4333
4364
  const ch = plainChars[ci];
4334
- const c5 = colorAt(perimeterIndex("top", xi++, W, bodyH));
4365
+ const c6 = colorAt(perimeterIndex("top", xi++, W, bodyH));
4335
4366
  if (ci >= agentSpanStart && ci < agentSpanEnd) {
4336
4367
  top += `\x1B[2m${themeGray}${ch}\x1B[0m`;
4337
4368
  continue;
@@ -4340,9 +4371,9 @@ function buildInputBoxRows(opts) {
4340
4371
  const levelN = Math.max(1, Math.min(5, level));
4341
4372
  const filled = di < levelN;
4342
4373
  di++;
4343
- top += (filled ? levelColor || c5 : "\x1B[38;5;240m") + (filled ? "\u25CF" : "\u25CB") + reset;
4374
+ top += (filled ? levelColor || c6 : "\x1B[38;5;240m") + (filled ? "\u25CF" : "\u25CB") + reset;
4344
4375
  } else {
4345
- top += c5 + ch + reset;
4376
+ top += c6 + ch + reset;
4346
4377
  }
4347
4378
  }
4348
4379
  }
@@ -4363,10 +4394,10 @@ function buildInputBoxRows(opts) {
4363
4394
  let bottom = "";
4364
4395
  for (let x = 0; x < W; x++) {
4365
4396
  const idx = perimeterIndex("bottom", W - 1 - x, W, bodyH);
4366
- const c5 = colorAt(idx);
4367
- if (x === 0) bottom += c5 + "\u2570" + reset;
4368
- else if (x === W - 1) bottom += c5 + "\u256F" + reset;
4369
- else bottom += c5 + "\u2500" + reset;
4397
+ const c6 = colorAt(idx);
4398
+ if (x === 0) bottom += c6 + "\u2570" + reset;
4399
+ else if (x === W - 1) bottom += c6 + "\u256F" + reset;
4400
+ else bottom += c6 + "\u2500" + reset;
4370
4401
  }
4371
4402
  return [pad + top, ...bodyRows, pad + bottom];
4372
4403
  }
@@ -5101,7 +5132,7 @@ var Tui = class _Tui {
5101
5132
  const q = this.inputBuffer.slice(1).trim().toLowerCase();
5102
5133
  if (q === "") return this.commands;
5103
5134
  return this.commands.filter(
5104
- (c5) => c5.name.startsWith(q) || c5.name.includes(q) || c5.label.toLowerCase().includes(q)
5135
+ (c6) => c6.name.startsWith(q) || c6.name.includes(q) || c6.label.toLowerCase().includes(q)
5105
5136
  );
5106
5137
  }
5107
5138
  runCommand(cmd) {
@@ -6460,7 +6491,7 @@ function readVersion() {
6460
6491
  if (typeof pkg.version === "string" && pkg.version) return pkg.version;
6461
6492
  } catch {
6462
6493
  }
6463
- return "0.11.6" ;
6494
+ return "0.11.8" ;
6464
6495
  }
6465
6496
  function isLocalHost(host) {
6466
6497
  return host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.endsWith(".localhost") || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
@@ -6819,7 +6850,7 @@ async function clearDaemon(api, identity) {
6819
6850
  function parseCommands(value) {
6820
6851
  if (!Array.isArray(value)) return [];
6821
6852
  return value.filter(
6822
- (c5) => !!c5 && typeof c5 === "object" && typeof c5.id === "string" && typeof c5.kind === "string"
6853
+ (c6) => !!c6 && typeof c6 === "object" && typeof c6.id === "string" && typeof c6.kind === "string"
6823
6854
  );
6824
6855
  }
6825
6856
  async function enqueueMachineCommand(api, machineId, kind, args) {
@@ -6842,23 +6873,23 @@ async function clearMachineCommands(api, machineId, appliedIds) {
6842
6873
  if (appliedIds.length === 0) return;
6843
6874
  const key = commandKey(machineId);
6844
6875
  const remaining = parseCommands(await api.userKvGet(key)).filter(
6845
- (c5) => !appliedIds.includes(c5.id)
6876
+ (c6) => !appliedIds.includes(c6.id)
6846
6877
  );
6847
6878
  await api.userKvSet(key, remaining.length ? remaining : null);
6848
6879
  }
6849
6880
  async function applyMachineCommand(api, identity, cmd) {
6850
6881
  switch (cmd.kind) {
6851
6882
  case "add_project": {
6852
- const path15 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
6853
- if (!path15) return "add_project: ignored (no path)";
6854
- await registerProject(api, identity, path15);
6855
- return `added project ${path15}`;
6883
+ const path16 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
6884
+ if (!path16) return "add_project: ignored (no path)";
6885
+ await registerProject(api, identity, path16);
6886
+ return `added project ${path16}`;
6856
6887
  }
6857
6888
  case "remove_project": {
6858
- const path15 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
6859
- if (!path15) return "remove_project: ignored (no path)";
6860
- await unregisterProject(api, identity, path15);
6861
- return `removed project ${path15}`;
6889
+ const path16 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
6890
+ if (!path16) return "remove_project: ignored (no path)";
6891
+ await unregisterProject(api, identity, path16);
6892
+ return `removed project ${path16}`;
6862
6893
  }
6863
6894
  case "update":
6864
6895
  return "update requested";
@@ -7228,7 +7259,7 @@ var HubSocket = class {
7228
7259
  }
7229
7260
  openSocket() {
7230
7261
  if (this.closed) return;
7231
- let url = `${this.api.wsEndpoint}/api/users/me/hub?token=${encodeURIComponent(this.api.bearer)}&client_id=${encodeURIComponent(this.clientId)}&client_kind=daemon`;
7262
+ let url = `${this.api.wsEndpoint}/api/users/me/stream?token=${encodeURIComponent(this.api.bearer)}&client_id=${encodeURIComponent(this.clientId)}&client_kind=daemon`;
7232
7263
  if (this.clientName) url += `&client_name=${encodeURIComponent(this.clientName)}`;
7233
7264
  let ws;
7234
7265
  try {
@@ -7257,8 +7288,15 @@ var HubSocket = class {
7257
7288
  } catch {
7258
7289
  return;
7259
7290
  }
7260
- if (msg && typeof msg === "object" && msg.type === "wake" && typeof msg.threadId === "string") {
7261
- this.hooks.onWake(msg.threadId);
7291
+ if (!msg || typeof msg !== "object") return;
7292
+ const frame = msg;
7293
+ if (frame.event === "standardagents.wake") {
7294
+ const threadId = frame.data?.thread_id;
7295
+ if (typeof threadId === "string") this.hooks.onWake(threadId);
7296
+ return;
7297
+ }
7298
+ if (frame.type === "wake" && typeof frame.threadId === "string") {
7299
+ this.hooks.onWake(frame.threadId);
7262
7300
  }
7263
7301
  }
7264
7302
  handleDrop(ws) {
@@ -8144,7 +8182,7 @@ async function installCommand(endpointFlag) {
8144
8182
  const identity = loadMachineIdentity();
8145
8183
  const existing = await loadMachine(api, identity.machine_id).catch(() => null);
8146
8184
  const suggested = machineDisplayName(existing ?? { hostname: os8.hostname(), id: identity.machine_id });
8147
- const rl = readline2.createInterface({ input: stdin, output: stdout });
8185
+ const rl = readline3.createInterface({ input: stdin, output: stdout });
8148
8186
  const answer = (await rl.question(
8149
8187
  `${c3.bold}Machine name${c3.reset} ${c3.dim}(shown in the session picker)${c3.reset} [${suggested}]: `
8150
8188
  )).trim();
@@ -8302,9 +8340,151 @@ async function runDaemonCommand(argv) {
8302
8340
  }
8303
8341
  }
8304
8342
  }
8343
+ var DIR2 = path4.join(os8.homedir(), ".standardagents");
8344
+ var FILE2 = path4.join(DIR2, "prefs.json");
8345
+ function loadPrefs(file2 = FILE2) {
8346
+ try {
8347
+ return JSON.parse(fs5.readFileSync(file2, "utf8"));
8348
+ } catch {
8349
+ return {};
8350
+ }
8351
+ }
8352
+ function savePrefs(update, file2 = FILE2) {
8353
+ const merged = { ...loadPrefs(file2), ...update };
8354
+ fs5.mkdirSync(path4.dirname(file2), { recursive: true });
8355
+ fs5.writeFileSync(file2, JSON.stringify(merged, null, 2));
8356
+ }
8357
+ function shouldOfferDaemonInstall(facts) {
8358
+ if (facts.optedOutEnv) return false;
8359
+ if (!facts.interactive) return false;
8360
+ if (facts.platform === "win32") return false;
8361
+ if (facts.serviceInstalled) return false;
8362
+ if (facts.daemonOnline) return false;
8363
+ if (facts.declinedAt !== void 0) return false;
8364
+ return true;
8365
+ }
8305
8366
 
8306
- // src/index.ts
8367
+ // src/auth-cli.ts
8307
8368
  var c4 = {
8369
+ reset: "\x1B[0m",
8370
+ dim: "\x1B[2m",
8371
+ bold: "\x1B[1m",
8372
+ green: "\x1B[32m",
8373
+ red: "\x1B[31m",
8374
+ teal: "\x1B[38;5;43m"
8375
+ };
8376
+ function parseAuthArgs(argv) {
8377
+ let endpoint = null;
8378
+ let token = null;
8379
+ for (let i = 0; i < argv.length; i += 1) {
8380
+ const arg = argv[i];
8381
+ if (arg === "--endpoint" || arg === "-e") {
8382
+ endpoint = argv[++i] ?? "";
8383
+ if (!endpoint) throw new Error("--endpoint requires a URL");
8384
+ } else if (arg.startsWith("--endpoint=")) {
8385
+ endpoint = arg.slice("--endpoint=".length);
8386
+ } else if (arg === "--token") {
8387
+ token = argv[++i] ?? "";
8388
+ if (!token) throw new Error("--token requires a value");
8389
+ } else if (arg.startsWith("--token=")) {
8390
+ token = arg.slice("--token=".length);
8391
+ } else {
8392
+ throw new Error(`Unknown option for login/logout: ${arg}`);
8393
+ }
8394
+ }
8395
+ const endpointExplicit = endpoint !== null;
8396
+ const resolved = normalizeEndpoint(endpoint ?? defaultEndpoint() ?? PRODUCTION_ENDPOINT);
8397
+ return { endpoint: resolved, endpointExplicit, token };
8398
+ }
8399
+ function hostLabel(endpoint) {
8400
+ return endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
8401
+ }
8402
+ async function signedInLabel(endpoint, token) {
8403
+ try {
8404
+ const res = await fetch(`${endpoint}/api/auth/me`, {
8405
+ headers: { Authorization: `Bearer ${token}` },
8406
+ signal: AbortSignal.timeout(1e4)
8407
+ });
8408
+ if (!res.ok) return null;
8409
+ const body = await res.json();
8410
+ return body.user?.email || body.user?.username || null;
8411
+ } catch {
8412
+ return null;
8413
+ }
8414
+ }
8415
+ async function runAuthCommand(command, argv) {
8416
+ let args;
8417
+ try {
8418
+ args = parseAuthArgs(argv);
8419
+ } catch (error) {
8420
+ process.stdout.write(`${c4.red}error:${c4.reset} ${error instanceof Error ? error.message : String(error)}
8421
+ `);
8422
+ process.stdout.write(`${c4.dim}usage: standardcode ${command} [--endpoint <url>]${command === "login" ? " [--token <key>]" : ""}${c4.reset}
8423
+ `);
8424
+ process.exitCode = 1;
8425
+ return;
8426
+ }
8427
+ const host = hostLabel(args.endpoint);
8428
+ relaxTlsForLocalEndpoint(args.endpoint);
8429
+ const existing = getCredential(args.endpoint);
8430
+ if (command === "logout") {
8431
+ if (!existing) {
8432
+ process.stdout.write(`You're not signed in to ${c4.teal}${host}${c4.reset} \u2014 nothing to do.
8433
+ `);
8434
+ return;
8435
+ }
8436
+ deleteCredential(args.endpoint);
8437
+ process.stdout.write(`${c4.green}\u2713${c4.reset} Signed out of ${c4.teal}${host}${c4.reset}.
8438
+ `);
8439
+ process.stdout.write(`${c4.dim}Run \`standardcode login\` to sign in again \u2014 with any account.${c4.reset}
8440
+ `);
8441
+ return;
8442
+ }
8443
+ if (existing) {
8444
+ const who2 = await signedInLabel(args.endpoint, existing.access_token);
8445
+ process.stdout.write(
8446
+ `${c4.dim}Currently signed in to ${host}${who2 ? ` as ${who2}` : ""} \u2014 replacing that sign-in.${c4.reset}
8447
+ `
8448
+ );
8449
+ }
8450
+ let token = args.token;
8451
+ if (!token) {
8452
+ process.stdout.write(`${c4.bold}Sign in to Standard Code${c4.reset}
8453
+ `);
8454
+ if (`https://${host}` !== PRODUCTION_ENDPOINT && `http://${host}` !== PRODUCTION_ENDPOINT) {
8455
+ process.stdout.write(`${c4.dim}Connecting to${c4.reset} ${c4.teal}${host}${c4.reset}
8456
+ `);
8457
+ }
8458
+ try {
8459
+ token = await deviceLogin(args.endpoint);
8460
+ } catch (error) {
8461
+ process.stdout.write(`${c4.red}\u2717${c4.reset} ${error instanceof Error ? error.message : String(error)}
8462
+ `);
8463
+ process.exitCode = 1;
8464
+ return;
8465
+ }
8466
+ }
8467
+ const api = new ApiClient(args.endpoint, token);
8468
+ const check = await api.verifyDetailed();
8469
+ if (!check.ok) {
8470
+ process.stdout.write(`${c4.red}\u2717${c4.reset} Sign-in didn't verify: ${check.reason}
8471
+ `);
8472
+ if (check.hint) process.stdout.write(` ${c4.dim}${check.hint}${c4.reset}
8473
+ `);
8474
+ process.exitCode = 1;
8475
+ return;
8476
+ }
8477
+ saveCredential(
8478
+ { endpoint: args.endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
8479
+ { updateDefault: !args.endpointExplicit }
8480
+ );
8481
+ const who = await signedInLabel(args.endpoint, token);
8482
+ process.stdout.write(`${c4.green}\u2713${c4.reset} Signed in to ${c4.teal}${host}${c4.reset}${who ? ` as ${c4.bold}${who}${c4.reset}` : ""}.
8483
+ `);
8484
+ }
8485
+
8486
+ // src/index.ts
8487
+ var c5 = {
8308
8488
  reset: "\x1B[0m",
8309
8489
  dim: "\x1B[2m",
8310
8490
  bold: "\x1B[1m",
@@ -8333,10 +8513,16 @@ function printUsage() {
8333
8513
  stdout.write(
8334
8514
  [
8335
8515
  "",
8336
- `${c4.bold}Usage${c4.reset}`,
8516
+ `${c5.bold}Usage${c5.reset}`,
8337
8517
  " standardcode [options] [dir]",
8518
+ " standardcode login [--endpoint <url>] [--token <key>]",
8519
+ " Sign in (or switch accounts \u2014 always re-runs the flow).",
8520
+ " standardcode logout [--endpoint <url>]",
8521
+ " Sign out of the saved endpoint.",
8522
+ " standardcode daemon <install|run|status|uninstall>",
8523
+ " Manage this machine's headless execution daemon.",
8338
8524
  "",
8339
- `${c4.bold}Options${c4.reset}`,
8525
+ `${c5.bold}Options${c5.reset}`,
8340
8526
  " -v, --version Print the Standard Code version and exit.",
8341
8527
  " -e, --endpoint [url] Use a different Standard Agents instance for this run",
8342
8528
  " (default: https://api.standardcode.ai).",
@@ -8405,15 +8591,15 @@ function parseArgs2(args) {
8405
8591
  }
8406
8592
  function printCommandBlock(tui, command, output4, ok, where) {
8407
8593
  tui.print("");
8408
- const note = where ? ` ${c4.dim}(ran on ${where})${c4.reset}` : "";
8409
- tui.print(`${c4.magenta}!${c4.reset} ${c4.bold}${command}${c4.reset}${note}`);
8594
+ const note = where ? ` ${c5.dim}(ran on ${where})${c5.reset}` : "";
8595
+ tui.print(`${c5.magenta}!${c5.reset} ${c5.bold}${command}${c5.reset}${note}`);
8410
8596
  const body = (output4 ?? "").replace(/\s+$/, "");
8411
8597
  if (body) {
8412
8598
  for (const line of body.split("\n")) {
8413
- tui.print(` ${ok ? c4.dim : c4.red}${line}${c4.reset}`);
8599
+ tui.print(` ${ok ? c5.dim : c5.red}${line}${c5.reset}`);
8414
8600
  }
8415
8601
  } else {
8416
- tui.print(` ${c4.dim}(no output)${c4.reset}`);
8602
+ tui.print(` ${c5.dim}(no output)${c5.reset}`);
8417
8603
  }
8418
8604
  tui.print("");
8419
8605
  }
@@ -8424,7 +8610,7 @@ function printAssistant(tui, text) {
8424
8610
  let dotted = false;
8425
8611
  for (const line of renderStreamingMarkdown(text, cols2)) {
8426
8612
  if (!dotted && line.trim()) {
8427
- tui.print(`${c4.gray}\u2022${c4.reset} ${line}`);
8613
+ tui.print(`${c5.gray}\u2022${c5.reset} ${line}`);
8428
8614
  dotted = true;
8429
8615
  } else {
8430
8616
  tui.print(` ${line}`);
@@ -8439,7 +8625,7 @@ function startLoader(label) {
8439
8625
  const draw = () => {
8440
8626
  const now = Date.now();
8441
8627
  const f = frames[Math.floor(now / 70) % frames.length];
8442
- stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${c4.reset} ${c4.dim}${label}\u2026${c4.reset}`);
8628
+ stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${c5.reset} ${c5.dim}${label}\u2026${c5.reset}`);
8443
8629
  };
8444
8630
  draw();
8445
8631
  const timer = setInterval(draw, 70);
@@ -8455,12 +8641,12 @@ function farewell(stoppedProcs = 0) {
8455
8641
  if (stoppedProcs > 0) {
8456
8642
  stdout.write(
8457
8643
  `
8458
- ${c4.cyan}\u2699${c4.reset} Stopped ${stoppedProcs} background process${stoppedProcs === 1 ? "" : "es"}.
8644
+ ${c5.cyan}\u2699${c5.reset} Stopped ${stoppedProcs} background process${stoppedProcs === 1 ? "" : "es"}.
8459
8645
  `
8460
8646
  );
8461
8647
  }
8462
8648
  stdout.write(`
8463
- ${c4.teal}\u25C7${c4.reset} ${c4.dim}Standard Code \u2014 see you soon.${c4.reset}
8649
+ ${c5.teal}\u25C7${c5.reset} ${c5.dim}Standard Code \u2014 see you soon.${c5.reset}
8464
8650
  `);
8465
8651
  }
8466
8652
  function printWelcome(endpoint, projectDir) {
@@ -8474,10 +8660,10 @@ function printWelcome(endpoint, projectDir) {
8474
8660
  const metaWidth = Math.max(1, terminalColumns - pad.length - markWidth - 3 - 1);
8475
8661
  const displayDir = truncateMiddle(dir2, metaWidth);
8476
8662
  const meta = [
8477
- `${c4.bold}${gradientText("Standard Code")}${c4.reset}${version ? ` ${c4.dim}v${version}${c4.reset}` : ""}`,
8478
- `${c4.dim}terminal coding agent${c4.reset}`,
8479
- ...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c4.teal}${host}${c4.reset}`],
8480
- `${c4.dim}${displayDir}${c4.reset}`
8663
+ `${c5.bold}${gradientText("Standard Code")}${c5.reset}${version ? ` ${c5.dim}v${version}${c5.reset}` : ""}`,
8664
+ `${c5.dim}terminal coding agent${c5.reset}`,
8665
+ ...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c5.teal}${host}${c5.reset}`],
8666
+ `${c5.dim}${displayDir}${c5.reset}`
8481
8667
  ];
8482
8668
  const metaTop = Math.floor((LOGO_MARK.length - meta.length) / 2);
8483
8669
  stdout.write("\n");
@@ -8491,11 +8677,11 @@ function printWelcome(endpoint, projectDir) {
8491
8677
  }
8492
8678
  function colorActivity(line) {
8493
8679
  const m = line.match(/^(\s*)([✓✗⛔])\s?([\s\S]*)$/);
8494
- if (!m) return `${c4.dim}${line}${c4.reset}`;
8680
+ if (!m) return `${c5.dim}${line}${c5.reset}`;
8495
8681
  const [, indent, glyph, rest] = m;
8496
8682
  if (glyph === "\u2713") {
8497
- const body = rest.replace(/\s(\([^()]*\))\s*$/, ` ${c4.dim}$1${c4.reset}`);
8498
- return `${indent}${c4.green}\u2713${c4.reset} ${body}`;
8683
+ const body = rest.replace(/\s(\([^()]*\))\s*$/, ` ${c5.dim}$1${c5.reset}`);
8684
+ return `${indent}${c5.green}\u2713${c5.reset} ${body}`;
8499
8685
  }
8500
8686
  if (glyph === "\u2717") {
8501
8687
  const ERR_MAX_LINES = 7;
@@ -8503,15 +8689,15 @@ function colorActivity(line) {
8503
8689
  const shown = lines.slice(0, ERR_MAX_LINES);
8504
8690
  const hidden = lines.length - shown.length;
8505
8691
  const body = shown.map(
8506
- (l, i) => i === 0 ? `${indent}${c4.red}\u2717 ${l}${c4.reset}` : `${indent}${c4.red}${c4.dim}${l}${c4.reset}`
8692
+ (l, i) => i === 0 ? `${indent}${c5.red}\u2717 ${l}${c5.reset}` : `${indent}${c5.red}${c5.dim}${l}${c5.reset}`
8507
8693
  ).join("\n");
8508
8694
  if (hidden > 0) {
8509
8695
  return `${body}
8510
- ${indent}${c4.dim}\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}${c4.reset}`;
8696
+ ${indent}${c5.dim}\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}${c5.reset}`;
8511
8697
  }
8512
8698
  return body;
8513
8699
  }
8514
- return `${indent}${c4.yellow}\u26D4 ${rest}${c4.reset}`;
8700
+ return `${indent}${c5.yellow}\u26D4 ${rest}${c5.reset}`;
8515
8701
  }
8516
8702
  async function main() {
8517
8703
  if (process.argv.slice(2).some((arg) => arg === "-v" || arg === "--version")) {
@@ -8523,11 +8709,15 @@ async function main() {
8523
8709
  await runDaemonCommand(process.argv.slice(3));
8524
8710
  return;
8525
8711
  }
8712
+ if (process.argv[2] === "login" || process.argv[2] === "logout") {
8713
+ await runAuthCommand(process.argv[2], process.argv.slice(3));
8714
+ return;
8715
+ }
8526
8716
  let cliArgs;
8527
8717
  try {
8528
8718
  cliArgs = parseArgs2(process.argv.slice(2));
8529
8719
  } catch (error) {
8530
- stdout.write(`${c4.red}error:${c4.reset} ${error instanceof Error ? error.message : String(error)}
8720
+ stdout.write(`${c5.red}error:${c5.reset} ${error instanceof Error ? error.message : String(error)}
8531
8721
  `);
8532
8722
  printUsage();
8533
8723
  process.exit(1);
@@ -8554,7 +8744,7 @@ async function main() {
8554
8744
  }
8555
8745
  preflightArmed = true;
8556
8746
  stdout.write(`
8557
- ${c4.dim}Press Control-C again to exit${c4.reset}
8747
+ ${c5.dim}Press Control-C again to exit${c5.reset}
8558
8748
  `);
8559
8749
  preflightTimer = setTimeout(() => {
8560
8750
  preflightArmed = false;
@@ -8563,7 +8753,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8563
8753
  };
8564
8754
  const ask = async (question) => {
8565
8755
  if (!reader.rl) {
8566
- reader.rl = readline2.createInterface({ input: stdin, output: stdout });
8756
+ reader.rl = readline3.createInterface({ input: stdin, output: stdout });
8567
8757
  reader.rl.on("SIGINT", onPreflightSigint);
8568
8758
  reader.rl.on("close", () => {
8569
8759
  if (handoffClosing) return;
@@ -8576,10 +8766,10 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8576
8766
  const askEndpoint = async () => {
8577
8767
  for (; ; ) {
8578
8768
  const answer = (await ask(
8579
- `${c4.cyan}Standard Agents instance URL${c4.reset} (e.g. http://localhost:5178): `
8769
+ `${c5.cyan}Standard Agents instance URL${c5.reset} (e.g. http://localhost:5178): `
8580
8770
  )).trim();
8581
8771
  if (answer) return answer;
8582
- stdout.write(`${c4.dim}An endpoint URL is required.${c4.reset}
8772
+ stdout.write(`${c5.dim}An endpoint URL is required.${c5.reset}
8583
8773
  `);
8584
8774
  }
8585
8775
  };
@@ -8594,7 +8784,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8594
8784
  const tlsRelaxed = relaxTlsForLocalEndpoint(endpoint);
8595
8785
  printWelcome(endpoint, projectDir);
8596
8786
  if (tlsRelaxed) {
8597
- stdout.write(`${c4.dim} TLS verification relaxed for local endpoint.${c4.reset}
8787
+ stdout.write(`${c5.dim} TLS verification relaxed for local endpoint.${c5.reset}
8598
8788
 
8599
8789
  `);
8600
8790
  }
@@ -8606,7 +8796,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8606
8796
  loading.stop();
8607
8797
  const applied = consumeAppliedUpdate(version);
8608
8798
  if (applied) {
8609
- stdout.write(` ${c4.green}\u2713${c4.reset} ${c4.dim}Standard Code updated to v${version}.${c4.reset}
8799
+ stdout.write(` ${c5.green}\u2713${c5.reset} ${c5.dim}Standard Code updated to v${version}.${c5.reset}
8610
8800
 
8611
8801
  `);
8612
8802
  }
@@ -8615,21 +8805,21 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8615
8805
  const decision = decideAutoUpdate(updateAvailable, { state: readAutoUpdateState(), pm });
8616
8806
  if (decision === "start" && pm && startBackgroundUpdate(updateAvailable.latest, pm)) {
8617
8807
  stdout.write(
8618
- ` ${c4.teal}\u27F3${c4.reset} ${c4.dim}Standard Code ${c4.reset}${c4.bold}v${updateAvailable.latest}${c4.reset}${c4.dim} is installing in the background \u2014 it applies on your next launch.${c4.reset}
8808
+ ` ${c5.teal}\u27F3${c5.reset} ${c5.dim}Standard Code ${c5.reset}${c5.bold}v${updateAvailable.latest}${c5.reset}${c5.dim} is installing in the background \u2014 it applies on your next launch.${c5.reset}
8619
8809
 
8620
8810
  `
8621
8811
  );
8622
8812
  } else if (decision === "in_flight") {
8623
8813
  stdout.write(
8624
- ` ${c4.teal}\u27F3${c4.reset} ${c4.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c4.reset}
8814
+ ` ${c5.teal}\u27F3${c5.reset} ${c5.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c5.reset}
8625
8815
 
8626
8816
  `
8627
8817
  );
8628
8818
  } else {
8629
8819
  const display = updateCommand(pm ?? "npm").display;
8630
8820
  stdout.write(
8631
- ` ${c4.teal}\u25C7${c4.reset} ${c4.dim}Update available:${c4.reset} ${c4.dim}v${updateAvailable.current}${c4.reset} \u2192 ${c4.bold}v${updateAvailable.latest}${c4.reset}
8632
- ${c4.dim}Run ${c4.reset}${c4.bold}${display}${c4.reset}${c4.dim} to update${c4.reset}
8821
+ ` ${c5.teal}\u25C7${c5.reset} ${c5.dim}Update available:${c5.reset} ${c5.dim}v${updateAvailable.current}${c5.reset} \u2192 ${c5.bold}v${updateAvailable.latest}${c5.reset}
8822
+ ${c5.dim}Run ${c5.reset}${c5.bold}${display}${c5.reset}${c5.dim} to update${c5.reset}
8633
8823
 
8634
8824
  `
8635
8825
  );
@@ -8647,39 +8837,39 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8647
8837
  if (!api || !storedCheck?.ok) {
8648
8838
  const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
8649
8839
  if (storedCheck && !storedCheck.ok) {
8650
- stdout.write(`${c4.red}\u2717${c4.reset} ${c4.dim}Saved sign-in for this endpoint failed:${c4.reset} ${storedCheck.reason}
8840
+ stdout.write(`${c5.red}\u2717${c5.reset} ${c5.dim}Saved sign-in for this endpoint failed:${c5.reset} ${storedCheck.reason}
8651
8841
  `);
8652
- if (storedCheck.hint) stdout.write(` ${c4.dim}${storedCheck.hint}${c4.reset}
8842
+ if (storedCheck.hint) stdout.write(` ${c5.dim}${storedCheck.hint}${c5.reset}
8653
8843
  `);
8654
8844
  stdout.write("\n");
8655
8845
  }
8656
8846
  const explainFailure = (result, prefix) => {
8657
- stdout.write(`${c4.red}\u2717${c4.reset} ${prefix}${result.reason}
8847
+ stdout.write(`${c5.red}\u2717${c5.reset} ${prefix}${result.reason}
8658
8848
  `);
8659
- if (result.hint) stdout.write(` ${c4.dim}${result.hint}${c4.reset}
8849
+ if (result.hint) stdout.write(` ${c5.dim}${result.hint}${c5.reset}
8660
8850
  `);
8661
8851
  };
8662
- stdout.write(`${c4.bold}Sign in to ${gradientText("Standard Code")}${c4.reset}
8852
+ stdout.write(`${c5.bold}Sign in to ${gradientText("Standard Code")}${c5.reset}
8663
8853
  `);
8664
8854
  if (`https://${host}` !== PRODUCTION_ENDPOINT) {
8665
- stdout.write(`${c4.dim}Connecting to${c4.reset} ${c4.teal}${host}${c4.reset}
8855
+ stdout.write(`${c5.dim}Connecting to${c5.reset} ${c5.teal}${host}${c5.reset}
8666
8856
  `);
8667
8857
  }
8668
8858
  stdout.write(
8669
- `${c4.dim}You'll only need to do this once on this machine.${c4.reset}
8859
+ `${c5.dim}You'll only need to do this once on this machine.${c5.reset}
8670
8860
 
8671
8861
  `
8672
8862
  );
8673
8863
  stdout.write(
8674
- `${c4.white}Press ${c4.bold}Enter${c4.reset}${c4.white} to open your browser and sign in.${c4.reset} ${c4.dim}(or paste an API token)${c4.reset}
8864
+ `${c5.white}Press ${c5.bold}Enter${c5.reset}${c5.white} to open your browser and sign in.${c5.reset} ${c5.dim}(or paste an API token)${c5.reset}
8675
8865
 
8676
8866
  `
8677
8867
  );
8678
8868
  for (; ; ) {
8679
- const token = (await ask(`${c4.teal}\u276F${c4.reset} `)).trim();
8869
+ const token = (await ask(`${c5.teal}\u276F${c5.reset} `)).trim();
8680
8870
  if (!token) {
8681
8871
  const got = await deviceLogin(endpoint).catch((e) => {
8682
- stdout.write(`${c4.red}\u2717${c4.reset} ${c4.dim}${e instanceof Error ? e.message : String(e)}${c4.reset}
8872
+ stdout.write(`${c5.red}\u2717${c5.reset} ${c5.dim}${e instanceof Error ? e.message : String(e)}${c5.reset}
8683
8873
  `);
8684
8874
  return null;
8685
8875
  });
@@ -8693,7 +8883,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8693
8883
  { endpoint, access_token: got, token_type: "Bearer", saved_at: Date.now() },
8694
8884
  { updateDefault: !endpointOverride }
8695
8885
  );
8696
- stdout.write(`${c4.green}\u2713${c4.reset} Connected to ${c4.teal}${host}${c4.reset}
8886
+ stdout.write(`${c5.green}\u2713${c5.reset} Connected to ${c5.teal}${host}${c5.reset}
8697
8887
  `);
8698
8888
  break;
8699
8889
  }
@@ -8709,7 +8899,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8709
8899
  { endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
8710
8900
  { updateDefault: !endpointOverride }
8711
8901
  );
8712
- stdout.write(`${c4.green}\u2713${c4.reset} Connected to ${c4.teal}${host}${c4.reset}
8902
+ stdout.write(`${c5.green}\u2713${c5.reset} Connected to ${c5.teal}${host}${c5.reset}
8713
8903
  `);
8714
8904
  break;
8715
8905
  }
@@ -8733,7 +8923,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8733
8923
  } else if (["unlimited", "standard", "unlimited_one", AGENT_ID].includes(wanted)) {
8734
8924
  agentOverride = AGENT_ID;
8735
8925
  } else {
8736
- stdout.write(`${c4.red}error:${c4.reset} Unknown agent "${cliArgs.agent}". Use unlimited or sama.
8926
+ stdout.write(`${c5.red}error:${c5.reset} Unknown agent "${cliArgs.agent}". Use unlimited or sama.
8737
8927
  `);
8738
8928
  process.exit(1);
8739
8929
  }
@@ -8746,6 +8936,42 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8746
8936
  session.suggestDaemonInstall = machines.every((m) => !m.daemon);
8747
8937
  const remoteTargets = machines.filter((m) => m.id !== identity.machine_id && daemonOnline(m));
8748
8938
  const self = machines.find((m) => m.id === identity.machine_id);
8939
+ if (shouldOfferDaemonInstall({
8940
+ platform: process.platform,
8941
+ interactive: Boolean(stdin.isTTY && stdout.isTTY),
8942
+ serviceInstalled: serviceStatus().installed,
8943
+ daemonOnline: Boolean(self && daemonOnline(self)),
8944
+ declinedAt: loadPrefs().daemon_install_declined_at,
8945
+ optedOutEnv: Boolean(process.env.STANDARD_CODE_NO_DAEMON_PROMPT)
8946
+ })) {
8947
+ stdout.write(
8948
+ `
8949
+ ${c5.bold}This machine has no always-on daemon${c5.reset} ${c5.dim}\u2014 sessions stop when this terminal closes.${c5.reset}
8950
+ `
8951
+ );
8952
+ const promptRl = readline3.createInterface({ input: stdin, output: stdout });
8953
+ let answer = "";
8954
+ try {
8955
+ answer = (await promptRl.question(
8956
+ `${c5.white}Install it now so sessions keep running and start from anywhere?${c5.reset} ${c5.dim}[Y/n]${c5.reset} `
8957
+ )).trim().toLowerCase();
8958
+ } catch {
8959
+ answer = "n";
8960
+ } finally {
8961
+ promptRl.close();
8962
+ }
8963
+ if (answer === "" || answer === "y" || answer === "yes") {
8964
+ await runDaemonCommand(endpointOverride ? ["install", "--endpoint", endpoint] : ["install"]);
8965
+ stdout.write("\n");
8966
+ } else {
8967
+ savePrefs({ daemon_install_declined_at: Date.now() });
8968
+ stdout.write(
8969
+ `${c5.dim}Okay \u2014 \`standardcode daemon install\` sets it up any time.${c5.reset}
8970
+
8971
+ `
8972
+ );
8973
+ }
8974
+ }
8749
8975
  const launchDir = projectDir;
8750
8976
  let tags = [];
8751
8977
  let resumeTags = [];
@@ -8771,7 +8997,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8771
8997
  flow: for (; ; ) {
8772
8998
  if (step === "machine") {
8773
8999
  const picked = await tui.select(
8774
- `${c4.bold}Where should this session run?${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9000
+ `${c5.bold}Where should this session run?${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8775
9001
  [
8776
9002
  {
8777
9003
  label: `This machine \u2014 ${self ? machineDisplayName(self) : machine}`,
@@ -8855,7 +9081,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8855
9081
  items.push({ label: "\uFF0B Start a new session", value: null });
8856
9082
  const whereLabel = session.mode === "remote" && session.runner ? `${session.runner.name}:${shortenPath(session.remotePath ?? "")}` : shortDir;
8857
9083
  const picked = await tui.select(
8858
- `${c4.bold}Resume a session${c4.reset} ${c4.gray}${whereLabel}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9084
+ `${c5.bold}Resume a session${c5.reset} ${c5.gray}${whereLabel}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8859
9085
  items
8860
9086
  );
8861
9087
  if (picked === void 0) {
@@ -8881,7 +9107,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8881
9107
  } else {
8882
9108
  if (!agentOverride) {
8883
9109
  const picked = await tui.select(
8884
- `${c4.bold}Which agent?${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9110
+ `${c5.bold}Which agent?${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8885
9111
  AGENT_CHOICES.map((choice) => ({
8886
9112
  label: choice.title,
8887
9113
  hint: choice.description,
@@ -8931,7 +9157,7 @@ async function ensureSamaAuth(tui, api) {
8931
9157
  checking.stop();
8932
9158
  if (already) return true;
8933
9159
  const picked = await tui.select(
8934
- `${c4.bold}Authenticate with ChatGPT${c4.reset} ${c4.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c4.reset}`,
9160
+ `${c5.bold}Authenticate with ChatGPT${c5.reset} ${c5.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c5.reset}`,
8935
9161
  [
8936
9162
  {
8937
9163
  label: "Continue with ChatGPT",
@@ -8944,7 +9170,7 @@ async function ensureSamaAuth(tui, api) {
8944
9170
  if (picked !== "continue") return false;
8945
9171
  openUrl("https://standardcode.ai/app?connect=sama");
8946
9172
  tui.print(
8947
- `${c4.dim}Finish connecting ChatGPT in the browser \u2014 waiting here for the authorization to land on your account\u2026${c4.reset}`
9173
+ `${c5.dim}Finish connecting ChatGPT in the browser \u2014 waiting here for the authorization to land on your account\u2026${c5.reset}`
8948
9174
  );
8949
9175
  const waiting = startLoader("Waiting for your ChatGPT authorization");
8950
9176
  const deadline = Date.now() + 5 * 60 * 1e3;
@@ -8953,20 +9179,20 @@ async function ensureSamaAuth(tui, api) {
8953
9179
  if (await api.openSamaStatus().catch(() => false)) {
8954
9180
  waiting.stop();
8955
9181
  tui.print(
8956
- `${c4.green}\u2713${c4.reset} ChatGPT connected \u2014 Sama One is now unlocked on your account (terminal, web, and macOS app).`
9182
+ `${c5.green}\u2713${c5.reset} ChatGPT connected \u2014 Sama One is now unlocked on your account (terminal, web, and macOS app).`
8957
9183
  );
8958
9184
  return true;
8959
9185
  }
8960
9186
  }
8961
9187
  waiting.stop();
8962
9188
  tui.print(
8963
- `${c4.yellow}Still not connected.${c4.reset} Finish the flow at ${c4.teal}standardcode.ai/app${c4.reset} and pick Sama One again.`
9189
+ `${c5.yellow}Still not connected.${c5.reset} Finish the flow at ${c5.teal}standardcode.ai/app${c5.reset} and pick Sama One again.`
8964
9190
  );
8965
9191
  return false;
8966
9192
  }
8967
9193
  async function runAgentSwitchMenu(tui, api, threadId) {
8968
9194
  const picked = await tui.select(
8969
- `${c4.bold}Switch agent${c4.reset} ${c4.dim}takes effect on the next message${c4.reset}`,
9195
+ `${c5.bold}Switch agent${c5.reset} ${c5.dim}takes effect on the next message${c5.reset}`,
8970
9196
  AGENT_CHOICES.map((choice) => ({
8971
9197
  label: choice.title,
8972
9198
  hint: choice.description,
@@ -8983,10 +9209,10 @@ async function runAgentSwitchMenu(tui, api, threadId) {
8983
9209
  try {
8984
9210
  await api.setThreadAgent(threadId, picked);
8985
9211
  tui.setAgentLabel(title);
8986
- tui.print(`${c4.green}\u2713${c4.reset} Session handed to ${c4.bold}${title}${c4.reset} \u2014 applies from your next message.`);
9212
+ tui.print(`${c5.green}\u2713${c5.reset} Session handed to ${c5.bold}${title}${c5.reset} \u2014 applies from your next message.`);
8987
9213
  } catch (e) {
8988
9214
  tui.print(
8989
- `${c4.red}\u2717 couldn't switch agent:${c4.reset} ${c4.gray}${e instanceof Error ? e.message : String(e)}${c4.reset}`
9215
+ `${c5.red}\u2717 couldn't switch agent:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`
8990
9216
  );
8991
9217
  }
8992
9218
  }
@@ -9011,7 +9237,7 @@ async function pickLocalProject(tui, api, self, cwd, machineName) {
9011
9237
  { label: "\uFF0B New project", hint: "browse or create a directory", value: NEW }
9012
9238
  ];
9013
9239
  const picked = await tui.select(
9014
- `${c4.bold}Project on ${label}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9240
+ `${c5.bold}Project on ${label}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
9015
9241
  items,
9016
9242
  { spaced: true }
9017
9243
  );
@@ -9035,7 +9261,7 @@ async function pickRemoteProject(tui, api, runner) {
9035
9261
  );
9036
9262
  items.push({ label: "\uFF0B New project", hint: "browse or create a directory", value: ENTER_PATH });
9037
9263
  const picked = await tui.select(
9038
- `${c4.bold}Project on ${runner.name}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9264
+ `${c5.bold}Project on ${runner.name}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
9039
9265
  items,
9040
9266
  { spaced: true }
9041
9267
  );
@@ -9068,7 +9294,7 @@ async function summarizeThreads(api, threads) {
9068
9294
  }
9069
9295
  function subagentLabel(s, titles) {
9070
9296
  const agentName = (s.agent_name || "").trim();
9071
- const title = (s.title || "").trim() || titles.get(agentName) || (agentName ? agentName.replace(/[_-]+/g, " ").replace(/\b\w/g, (c5) => c5.toUpperCase()) : "Subagent");
9297
+ const title = (s.title || "").trim() || titles.get(agentName) || (agentName ? agentName.replace(/[_-]+/g, " ").replace(/\b\w/g, (c6) => c6.toUpperCase()) : "Subagent");
9072
9298
  const tagged = (s.threadName || "").trim();
9073
9299
  return tagged ? `${title} \xB7 ${tagged}` : title;
9074
9300
  }
@@ -9091,8 +9317,8 @@ async function printHistory(api, threadId, tui) {
9091
9317
  ).sort((a, b) => (a.created_at ?? 0) - (b.created_at ?? 0));
9092
9318
  if (!convo.length) return;
9093
9319
  const shown = convo.slice(-24);
9094
- tui.print(`${c4.dim}\u2500\u2500 resuming session \xB7 ${convo.length} message${convo.length === 1 ? "" : "s"} \u2500\u2500${c4.reset}`);
9095
- if (shown.length < convo.length) tui.print(`${c4.dim} \u2026 earlier messages omitted${c4.reset}`);
9320
+ tui.print(`${c5.dim}\u2500\u2500 resuming session \xB7 ${convo.length} message${convo.length === 1 ? "" : "s"} \u2500\u2500${c5.reset}`);
9321
+ if (shown.length < convo.length) tui.print(`${c5.dim} \u2026 earlier messages omitted${c5.reset}`);
9096
9322
  for (const m of shown) {
9097
9323
  if (m.metadata?.user_command) {
9098
9324
  printCommandBlock(
@@ -9153,6 +9379,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
9153
9379
  let editingPendingId = null;
9154
9380
  let reconcileSharedMessaging = async () => {
9155
9381
  };
9382
+ let applySharedMessagingEvent = () => false;
9156
9383
  let refreshSessionProjection = async () => {
9157
9384
  };
9158
9385
  const shownIds = /* @__PURE__ */ new Set();
@@ -9210,21 +9437,21 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
9210
9437
  if (isOwner) {
9211
9438
  bridge?.setClaim("takeover");
9212
9439
  exec?.writeSessionInfo();
9213
- if (wasKnown && moved) tui.print(`${c4.dim}Tool execution moved to this terminal.${c4.reset}`);
9440
+ if (wasKnown && moved) tui.print(`${c5.dim}Tool execution moved to this terminal.${c5.reset}`);
9214
9441
  } else if (wasKnown && moved) {
9215
- tui.print(`${c4.dim}Tool execution moved to ${describeExecOwner(owner2)}.${c4.reset}`);
9442
+ tui.print(`${c5.dim}Tool execution moved to ${describeExecOwner(owner2)}.${c5.reset}`);
9216
9443
  }
9217
9444
  },
9218
9445
  onClaimRefused: (reason) => {
9219
9446
  if (reason === "in_flight") {
9220
9447
  tui.print(
9221
- `${c4.dim}The session's current client is mid-operation \u2014 execution stays there until it finishes.${c4.reset}`
9448
+ `${c5.dim}The session's current client is mid-operation \u2014 execution stays there until it finishes.${c5.reset}`
9222
9449
  );
9223
9450
  }
9224
9451
  },
9225
9452
  onSuperseded: () => {
9226
9453
  tui.print(
9227
- `${c4.yellow}\u26A0 Another Standard Code process on this machine took over this session \u2014 this terminal is watching.${c4.reset}`
9454
+ `${c5.yellow}\u26A0 Another Standard Code process on this machine took over this session \u2014 this terminal is watching.${c5.reset}`
9228
9455
  );
9229
9456
  },
9230
9457
  onStatus: (id, summary) => {
@@ -9286,7 +9513,7 @@ why: ${req.requestPermission}` : ""}`,
9286
9513
  } else if (eventType === "goal_updated" && data) {
9287
9514
  tui.setGoal(data);
9288
9515
  } else if (eventType === SHARED_MESSAGING_EVENT) {
9289
- void reconcileSharedMessaging(true);
9516
+ if (!applySharedMessagingEvent(data)) void reconcileSharedMessaging(true);
9290
9517
  }
9291
9518
  },
9292
9519
  // A failed turn whose message is the lease service's at-limit denial → offer
@@ -9382,7 +9609,7 @@ why: ${req.requestPermission}` : ""}`,
9382
9609
  const logout = async () => {
9383
9610
  deleteCredential(api.origin);
9384
9611
  const instanceHost = api.origin.replace(/^https?:\/\//, "");
9385
- tui.print(`${c4.gray}Signed out \u2014 removed the saved token for ${c4.teal}${instanceHost}${c4.reset}${c4.gray}. Run standardcode to sign in again.${c4.reset}`);
9612
+ tui.print(`${c5.gray}Signed out \u2014 removed the saved token for ${c5.teal}${instanceHost}${c5.reset}${c5.gray}. Run standardcode to sign in again.${c5.reset}`);
9386
9613
  await quit();
9387
9614
  };
9388
9615
  const bgMgr = {
@@ -9390,7 +9617,7 @@ why: ${req.requestPermission}` : ""}`,
9390
9617
  stop: async (id) => {
9391
9618
  if (!host) {
9392
9619
  tui.print(
9393
- `${c4.gray}That process runs on ${runnerName} \u2014 ask the agent to stop it (it manages processes there).${c4.reset}`
9620
+ `${c5.gray}That process runs on ${runnerName} \u2014 ask the agent to stop it (it manages processes there).${c5.reset}`
9394
9621
  );
9395
9622
  return;
9396
9623
  }
@@ -9471,17 +9698,25 @@ why: ${req.requestPermission}` : ""}`,
9471
9698
  } catch (error) {
9472
9699
  if (!sharedMessagingReady && !sharedMessagingUnavailableShown) {
9473
9700
  sharedMessagingUnavailableShown = true;
9474
- tui.print(`${c4.dim}shared messaging unavailable: ${error instanceof Error ? error.message : String(error)}${c4.reset}`);
9701
+ tui.print(`${c5.dim}shared messaging unavailable: ${error instanceof Error ? error.message : String(error)}${c5.reset}`);
9475
9702
  }
9476
9703
  }
9477
9704
  };
9705
+ applySharedMessagingEvent = (data) => {
9706
+ if (!sharedMessagingReady) return false;
9707
+ const event = parseMessagingChangedEvent(data);
9708
+ if (!event || !event.draft || event.draftOmitted) return false;
9709
+ if (event.pendingRevision > sharedMessaging.pending.revision) return false;
9710
+ applySharedMessaging({ version: 1, pending: sharedMessaging.pending, draft: event.draft }, true);
9711
+ return true;
9712
+ };
9478
9713
  const onTerminalResume = () => void reconcileSharedMessaging(true);
9479
9714
  process.on("SIGCONT", onTerminalResume);
9480
9715
  const applySharedMutation = (promise) => promise.then((snapshot) => {
9481
9716
  applySharedMessaging(snapshot, false);
9482
9717
  return true;
9483
9718
  }).catch((error) => {
9484
- tui.print(`${c4.dim}shared messaging failed: ${error instanceof Error ? error.message : String(error)}${c4.reset}`);
9719
+ tui.print(`${c5.dim}shared messaging failed: ${error instanceof Error ? error.message : String(error)}${c5.reset}`);
9485
9720
  return false;
9486
9721
  });
9487
9722
  const appendSharedPending = (text, images, refs = []) => applySharedMutation(api.appendPendingInput(threadId, {
@@ -9530,7 +9765,7 @@ why: ${req.requestPermission}` : ""}`,
9530
9765
  busy = false;
9531
9766
  optimisticBusyUntil = 0;
9532
9767
  tui.setWorking(false);
9533
- tui.print(`${c4.dim}failed to send: ${e instanceof Error ? e.message : String(e)}${c4.reset}`);
9768
+ tui.print(`${c5.dim}failed to send: ${e instanceof Error ? e.message : String(e)}${c5.reset}`);
9534
9769
  return false;
9535
9770
  }
9536
9771
  return true;
@@ -9539,11 +9774,11 @@ why: ${req.requestPermission}` : ""}`,
9539
9774
  let bangRunning = false;
9540
9775
  const runBangCommand = async (command) => {
9541
9776
  if (bangRunning) {
9542
- tui.print(`${c4.dim}a command is already running \u2014 one at a time.${c4.reset}`);
9777
+ tui.print(`${c5.dim}a command is already running \u2014 one at a time.${c5.reset}`);
9543
9778
  return;
9544
9779
  }
9545
9780
  bangRunning = true;
9546
- tui.print(`${c4.magenta}!${c4.reset} ${c4.dim}running on ${whereLabel}\u2026${c4.reset}`);
9781
+ tui.print(`${c5.magenta}!${c5.reset} ${c5.dim}running on ${whereLabel}\u2026${c5.reset}`);
9547
9782
  try {
9548
9783
  const res = await api.runCommand(threadId, command);
9549
9784
  if (res.messageId) shownIds.add(res.messageId);
@@ -9557,7 +9792,7 @@ why: ${req.requestPermission}` : ""}`,
9557
9792
  const openPendingMenu = async () => {
9558
9793
  const items = sharedMessaging.pending.items;
9559
9794
  if (!items.length) {
9560
- tui.print(`${c4.dim}No pending messages.${c4.reset}`);
9795
+ tui.print(`${c5.dim}No pending messages.${c5.reset}`);
9561
9796
  return;
9562
9797
  }
9563
9798
  const picked = await tui.select("Pending messages", items.map((item, index) => ({
@@ -9587,16 +9822,16 @@ why: ${req.requestPermission}` : ""}`,
9587
9822
  try {
9588
9823
  await api.compact(threadId);
9589
9824
  } catch (err) {
9590
- tui.print(`${c4.red}\u2717${c4.reset} couldn't start compaction: ${err.message}`);
9825
+ tui.print(`${c5.red}\u2717${c5.reset} couldn't start compaction: ${err.message}`);
9591
9826
  }
9592
9827
  };
9593
9828
  const runAccountCommand = async () => {
9594
- tui.print(`${c4.gray}Opening your account\u2026${c4.reset}`);
9829
+ tui.print(`${c5.gray}Opening your account\u2026${c5.reset}`);
9595
9830
  const link = await api.accountLink(threadId).catch(() => null);
9596
9831
  const target = link?.url ?? "https://standardcode.ai/account";
9597
9832
  openUrl(target);
9598
9833
  tui.print(
9599
- link?.preauthed ? `${c4.gray}\u2192 account dashboard opened in your browser (signed in)${c4.reset}` : `${c4.gray}\u2192 opened ${target} \u2014 sign in with your account email${c4.reset}`
9834
+ link?.preauthed ? `${c5.gray}\u2192 account dashboard opened in your browser (signed in)${c5.reset}` : `${c5.gray}\u2192 opened ${target} \u2014 sign in with your account email${c5.reset}`
9600
9835
  );
9601
9836
  };
9602
9837
  const ordinal = (n) => {
@@ -9607,24 +9842,24 @@ why: ${req.requestPermission}` : ""}`,
9607
9842
  const renderUpgradePanel = (q) => {
9608
9843
  const dots = [];
9609
9844
  for (let i = 0; i < q.max; i++) {
9610
- if (i < q.current) dots.push(`${c4.teal}\u25CF${c4.reset}`);
9611
- else if (i === q.current) dots.push(`${c4.bold}${gradientText("\uFF0B")}${c4.reset}`);
9612
- else dots.push(`${c4.dim}\xB7${c4.reset}`);
9845
+ if (i < q.current) dots.push(`${c5.teal}\u25CF${c5.reset}`);
9846
+ else if (i === q.current) dots.push(`${c5.bold}${gradientText("\uFF0B")}${c5.reset}`);
9847
+ else dots.push(`${c5.dim}\xB7${c5.reset}`);
9613
9848
  }
9614
9849
  const cost = fmtCost(q);
9615
9850
  const lines = [
9616
9851
  "",
9617
- `${c4.bold}\u2726 Add a parallel session${c4.reset}`,
9852
+ `${c5.bold}\u2726 Add a parallel session${c5.reset}`,
9618
9853
  "",
9619
- `${dots.join(" ")} ${c4.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c4.reset}`
9854
+ `${dots.join(" ")} ${c5.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c5.reset}`
9620
9855
  ];
9621
9856
  if (q.ends_trial) {
9622
9857
  lines.push(
9623
- `${c4.yellow}Your $5 trial covers 1 session. Adding a ${ordinal(q.sessions)} ends the trial${c4.reset}`,
9624
- `${c4.yellow}and starts your $49/mo plan now${cost ? ` \u2014 ${c4.bold}${cost} charged today${c4.reset}${c4.yellow}` : ""}.${c4.reset}`
9858
+ `${c5.yellow}Your $5 trial covers 1 session. Adding a ${ordinal(q.sessions)} ends the trial${c5.reset}`,
9859
+ `${c5.yellow}and starts your $49/mo plan now${cost ? ` \u2014 ${c5.bold}${cost} charged today${c5.reset}${c5.yellow}` : ""}.${c5.reset}`
9625
9860
  );
9626
9861
  } else if (cost) {
9627
- lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${c4.bold}${cost} charged now${c4.reset}.`);
9862
+ lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${c5.bold}${cost} charged now${c5.reset}.`);
9628
9863
  } else {
9629
9864
  lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 prorated on your next invoice.`);
9630
9865
  }
@@ -9637,7 +9872,7 @@ why: ${req.requestPermission}` : ""}`,
9637
9872
  try {
9638
9873
  if (opts.auto) {
9639
9874
  tui.print(
9640
- `${c4.yellow}You're out of simultaneous sessions \u2014 another Standard Code session is using your slot.${c4.reset}`
9875
+ `${c5.yellow}You're out of simultaneous sessions \u2014 another Standard Code session is using your slot.${c5.reset}`
9641
9876
  );
9642
9877
  }
9643
9878
  const quote = await api.sessionsQuote(threadId);
@@ -9645,16 +9880,16 @@ why: ${req.requestPermission}` : ""}`,
9645
9880
  const link = await api.accountLink(threadId).catch(() => null);
9646
9881
  const target = link?.url ?? "https://standardcode.ai/account";
9647
9882
  tui.print(
9648
- `${c4.gray}Close the other session (its slot frees within ~90s) \u2014 or add another simultaneous session to your plan, then resend your message.${c4.reset}`
9883
+ `${c5.gray}Close the other session (its slot frees within ~90s) \u2014 or add another simultaneous session to your plan, then resend your message.${c5.reset}`
9649
9884
  );
9650
9885
  openUrl(target);
9651
- tui.print(`${c4.gray}\u2192 opened ${target} to manage your plan${c4.reset}`);
9886
+ tui.print(`${c5.gray}\u2192 opened ${target} to manage your plan${c5.reset}`);
9652
9887
  return;
9653
9888
  }
9654
9889
  if (quote.current >= quote.max) {
9655
9890
  tui.print(
9656
- `${c4.yellow}You're at the maximum of ${quote.max} parallel session${quote.max === 1 ? "" : "s"}.${c4.reset}
9657
- ${c4.gray}Close another session (its slot frees within ~90s), then resend your message.${c4.reset}`
9891
+ `${c5.yellow}You're at the maximum of ${quote.max} parallel session${quote.max === 1 ? "" : "s"}.${c5.reset}
9892
+ ${c5.gray}Close another session (its slot frees within ~90s), then resend your message.${c5.reset}`
9658
9893
  );
9659
9894
  return;
9660
9895
  }
@@ -9666,25 +9901,25 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9666
9901
  { label: "Not now", value: "no" }
9667
9902
  ]);
9668
9903
  if (choice !== "go") {
9669
- tui.print(`${c4.gray}No change made \u2014 you can upgrade anytime with /upgrade.${c4.reset}`);
9904
+ tui.print(`${c5.gray}No change made \u2014 you can upgrade anytime with /upgrade.${c5.reset}`);
9670
9905
  return;
9671
9906
  }
9672
- tui.print(`${c4.gray}Applying\u2026${c4.reset}`);
9907
+ tui.print(`${c5.gray}Applying\u2026${c5.reset}`);
9673
9908
  let applied;
9674
9909
  try {
9675
9910
  applied = await api.sessionsUpgrade(threadId, quote.sessions);
9676
9911
  } catch (e) {
9677
- tui.print(`${c4.red}\u2717${c4.reset} Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
9912
+ tui.print(`${c5.red}\u2717${c5.reset} Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
9678
9913
  return;
9679
9914
  }
9680
9915
  if (!applied?.ok) {
9681
- tui.print(`${c4.red}\u2717${c4.reset} Upgrade failed: ${applied?.error ?? "unknown error"}`);
9916
+ tui.print(`${c5.red}\u2717${c5.reset} Upgrade failed: ${applied?.error ?? "unknown error"}`);
9682
9917
  return;
9683
9918
  }
9684
9919
  const n = applied.sessions ?? quote.sessions;
9685
- tui.print(`${c4.green}\u2713${c4.reset} ${c4.bold}${gradientText(`You now have ${n} parallel session${n === 1 ? "" : "s"}.`)}${c4.reset}`);
9920
+ tui.print(`${c5.green}\u2713${c5.reset} ${c5.bold}${gradientText(`You now have ${n} parallel session${n === 1 ? "" : "s"}.`)}${c5.reset}`);
9686
9921
  if (opts.auto && lastSent) {
9687
- tui.print(`${c4.gray}Continuing\u2026${c4.reset}`);
9922
+ tui.print(`${c5.gray}Continuing\u2026${c5.reset}`);
9688
9923
  await sendNow(lastSent.text, lastSent.images, lastSent.refs);
9689
9924
  }
9690
9925
  } finally {
@@ -9795,38 +10030,50 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9795
10030
  const history = await loadHistory(api, threadId, historySeedThreadId);
9796
10031
  tui.setHistory(history);
9797
10032
  await reconcileSharedMessaging(true);
9798
- let draftTimer;
9799
- const clearComposerDraft = () => {
9800
- if (draftTimer) clearTimeout(draftTimer);
9801
- draftTimer = void 0;
9802
- mirroredDraftRefs = [];
9803
- tui.setExternalAttachmentNames([]);
9804
- if (sharedMessagingReady) void applySharedMutation(api.clearSharedDraft(threadId, messagingOrigin));
9805
- };
9806
- tui.onDraftChange = (textVal, images) => {
9807
- if (draftTimer) clearTimeout(draftTimer);
9808
- draftTimer = setTimeout(() => {
9809
- draftTimer = void 0;
9810
- if (sharedMessagingReady) {
9811
- const hasDraft = !!textVal.trim() || images.length > 0 || mirroredDraftRefs.length > 0;
10033
+ let latestDraftPayload = null;
10034
+ let draftDirty = false;
10035
+ let draftInFlight = false;
10036
+ const pumpComposerDraft = async () => {
10037
+ if (draftInFlight) return;
10038
+ draftInFlight = true;
10039
+ try {
10040
+ while (draftDirty) {
10041
+ draftDirty = false;
10042
+ const payload = latestDraftPayload;
10043
+ if (!payload || !sharedMessagingReady) continue;
10044
+ const hasDraft = !!payload.text.trim() || payload.images.length > 0 || mirroredDraftRefs.length > 0;
9812
10045
  const mutation = {
9813
- content: textVal,
10046
+ content: payload.text,
9814
10047
  attachments: [
9815
10048
  ...hasDraft ? mirroredDraftRefs : [],
9816
- ...toSharedAttachments(images)
10049
+ ...toSharedAttachments(payload.images)
9817
10050
  ],
9818
10051
  ...messagingOrigin
9819
10052
  };
9820
- void applySharedMutation(
10053
+ await applySharedMutation(
9821
10054
  hasDraft ? api.putSharedDraft(threadId, mutation) : api.clearSharedDraft(threadId, messagingOrigin)
9822
10055
  );
9823
10056
  }
9824
- }, 150);
10057
+ } finally {
10058
+ draftInFlight = false;
10059
+ }
10060
+ };
10061
+ const clearComposerDraft = () => {
10062
+ mirroredDraftRefs = [];
10063
+ tui.setExternalAttachmentNames([]);
10064
+ latestDraftPayload = { text: "", images: [] };
10065
+ draftDirty = true;
10066
+ void pumpComposerDraft();
10067
+ };
10068
+ tui.onDraftChange = (textVal, images) => {
10069
+ latestDraftPayload = { text: textVal, images };
10070
+ draftDirty = true;
10071
+ void pumpComposerDraft();
9825
10072
  };
9826
10073
  const submitComposer = async (text, images, steer) => {
9827
10074
  const draftRefs = mirroredDraftRefs;
9828
10075
  if (!sharedMessagingReady && (busy || steer || editingPendingId !== null)) {
9829
- tui.print(`${c4.dim}Restoring shared message state \u2014 try again in a moment.${c4.reset}`);
10076
+ tui.print(`${c5.dim}Restoring shared message state \u2014 try again in a moment.${c5.reset}`);
9830
10077
  tui.setExternalAttachmentNames(draftRefs.map((attachment) => attachment.name));
9831
10078
  tui.setInput(text, images);
9832
10079
  return;
@@ -9849,7 +10096,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9849
10096
  const item = sharedMessaging.pending.items.find((candidate) => candidate.id === pendingId);
9850
10097
  editingPendingId = null;
9851
10098
  if (!item) {
9852
- tui.print(`${c4.dim}That pending message was already dispatched or dismissed.${c4.reset}`);
10099
+ tui.print(`${c5.dim}That pending message was already dispatched or dismissed.${c5.reset}`);
9853
10100
  return;
9854
10101
  }
9855
10102
  const updated = await editSharedPending(item, text, images, draftRefs);
@@ -9862,7 +10109,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9862
10109
  return;
9863
10110
  }
9864
10111
  if (steer) {
9865
- tui.print(`${c4.yellow}\u21AA steering now \u2014 stopping the current step${c4.reset}`);
10112
+ tui.print(`${c5.yellow}\u21AA steering now \u2014 stopping the current step${c5.reset}`);
9866
10113
  await Promise.all([
9867
10114
  api.stopThread(threadId).catch(() => {
9868
10115
  }),
@@ -9899,18 +10146,18 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9899
10146
  tui.onInterrupt = () => {
9900
10147
  const firstPending = sharedMessaging.pending.items[0];
9901
10148
  if (!busy && firstPending) {
9902
- tui.print(`${c4.yellow}\u21AA steering the first pending message\u2026${c4.reset}`);
10149
+ tui.print(`${c5.yellow}\u21AA steering the first pending message\u2026${c5.reset}`);
9903
10150
  void promoteSharedPending(firstPending);
9904
10151
  return;
9905
10152
  }
9906
10153
  if (busy) {
9907
10154
  const head = sharedMessaging.pending.items[0];
9908
10155
  if (head) {
9909
- tui.print(`${c4.yellow}\u21AA steering \u2014 stopping the current step to run the queued message${c4.reset}`);
10156
+ tui.print(`${c5.yellow}\u21AA steering \u2014 stopping the current step to run the queued message${c5.reset}`);
9910
10157
  void promoteSharedPending(head);
9911
10158
  return;
9912
10159
  }
9913
- tui.print(`${c4.yellow}\u25A0 stopping now${c4.reset}`);
10160
+ tui.print(`${c5.yellow}\u25A0 stopping now${c5.reset}`);
9914
10161
  const stops = [api.stopThread(threadId).catch(() => {
9915
10162
  })];
9916
10163
  for (const childId of activeSubagents.keys()) {
@@ -9940,7 +10187,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9940
10187
  void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
9941
10188
  });
9942
10189
  attaching.stop();
9943
- const header = `${c4.bold}${c4.magenta}Standard Code${c4.reset} ${c4.dim}\u2014 ${agentTitle}${c4.reset}`;
10190
+ const header = `${c5.bold}${c5.magenta}Standard Code${c5.reset} ${c5.dim}\u2014 ${agentTitle}${c5.reset}`;
9944
10191
  const remoteDaemonV = session.runner?.daemon?.version;
9945
10192
  const owner = currentOwner;
9946
10193
  let localOwnerDesc = ownsExecution ? "this terminal" : describeExecOwner(owner);
@@ -9948,24 +10195,24 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9948
10195
  const selfRec = await loadMachine(api, session.identity.machine_id).catch(() => null);
9949
10196
  if (selfRec?.daemon?.version) localOwnerDesc = `the daemon on this machine (v${selfRec.daemon.version})`;
9950
10197
  }
9951
- const execLine = remote ? `${c4.gray}tool execution:${c4.reset} daemon${remoteDaemonV ? ` v${remoteDaemonV}` : ""} on ${runnerName}` : `${c4.gray}tool execution:${c4.reset} ${localOwnerDesc}`;
10198
+ const execLine = remote ? `${c5.gray}tool execution:${c5.reset} daemon${remoteDaemonV ? ` v${remoteDaemonV}` : ""} on ${runnerName}` : `${c5.gray}tool execution:${c5.reset} ${localOwnerDesc}`;
9952
10199
  tui.setAgentLabel(agentTitle);
9953
10200
  tui.banner(
9954
10201
  remote ? [
9955
10202
  header,
9956
- `${c4.gray}project:${c4.reset} ${session.remotePath ?? "?"} ${c4.teal}on ${runnerName}${c4.reset}`,
9957
- `${c4.gray}machine:${c4.reset} ${runnerName} ${c4.gray}thread:${c4.reset} ${threadId.slice(0, 8)}`,
10203
+ `${c5.gray}project:${c5.reset} ${session.remotePath ?? "?"} ${c5.teal}on ${runnerName}${c5.reset}`,
10204
+ `${c5.gray}machine:${c5.reset} ${runnerName} ${c5.gray}thread:${c5.reset} ${threadId.slice(0, 8)}`,
9958
10205
  execLine
9959
10206
  ] : [
9960
10207
  header,
9961
- `${c4.gray}project:${c4.reset} ${projectDir}`,
9962
- `${c4.gray}machine:${c4.reset} ${machine} ${c4.gray}thread:${c4.reset} ${threadId.slice(0, 8)}`,
10208
+ `${c5.gray}project:${c5.reset} ${projectDir}`,
10209
+ `${c5.gray}machine:${c5.reset} ${machine} ${c5.gray}thread:${c5.reset} ${threadId.slice(0, 8)}`,
9963
10210
  execLine
9964
10211
  ]
9965
10212
  );
9966
10213
  if (!remote && session.suggestDaemonInstall && process.platform !== "win32" && !serviceStatus().installed) {
9967
10214
  tui.print(
9968
- `${c4.dim}Tip: install the always-on daemon (${c4.reset}standardcode daemon install${c4.dim}) to start sessions on this machine from anywhere.${c4.reset}`
10215
+ `${c5.dim}Tip: install the always-on daemon (${c5.reset}standardcode daemon install${c5.dim}) to start sessions on this machine from anywhere.${c5.reset}`
9969
10216
  );
9970
10217
  }
9971
10218
  if (resumed) await printHistory(api, threadId, tui);
@@ -9976,17 +10223,17 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9976
10223
  const runningProcs = (await registry.list()).filter((p) => p.status === "running");
9977
10224
  if (runningProcs.length) {
9978
10225
  tui.print(
9979
- `${c4.cyan}\u2699 ${runningProcs.length} background process${runningProcs.length === 1 ? "" : "es"} running:${c4.reset}`
10226
+ `${c5.cyan}\u2699 ${runningProcs.length} background process${runningProcs.length === 1 ? "" : "es"} running:${c5.reset}`
9980
10227
  );
9981
- for (const p of runningProcs) tui.print(`${c4.gray} ${p.id} ${p.description || p.command}${c4.reset}`);
10228
+ for (const p of runningProcs) tui.print(`${c5.gray} ${p.id} ${p.description || p.command}${c5.reset}`);
9982
10229
  }
9983
10230
  refreshBgCount();
9984
10231
  if (exec && ownsExecution) {
9985
10232
  for (const res of await exec.connectEnabledMcpServers()) {
9986
10233
  if (res.ok) {
9987
- tui.print(`${c4.cyan}\u26A1 MCP "${res.name}" connected${c4.reset} ${c4.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c4.reset}`);
10234
+ tui.print(`${c5.cyan}\u26A1 MCP "${res.name}" connected${c5.reset} ${c5.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c5.reset}`);
9988
10235
  } else {
9989
- tui.print(`${c4.red}\u26A0 MCP "${res.name}" failed:${c4.reset} ${c4.gray}${res.error}${c4.reset}`);
10236
+ tui.print(`${c5.red}\u26A0 MCP "${res.name}" failed:${c5.reset} ${c5.gray}${res.error}${c5.reset}`);
9990
10237
  }
9991
10238
  }
9992
10239
  }
@@ -10002,8 +10249,8 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
10002
10249
  try {
10003
10250
  const { choice, reason } = await tui.approval(
10004
10251
  `${request.summary}${request.permission ? `
10005
- ${c4.bold}why: ${request.permission}${c4.reset}` : ""}
10006
- ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10252
+ ${c5.bold}why: ${request.permission}${c5.reset}` : ""}
10253
+ ${c5.dim}runs on ${request.machine || runnerName}${c5.reset}`,
10007
10254
  request.risk
10008
10255
  );
10009
10256
  answeredApprovals.add(request.tool_call_id);
@@ -10030,7 +10277,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10030
10277
  if (snapshot.stalled && !stalledNoticeShown) {
10031
10278
  stalledNoticeShown = true;
10032
10279
  tui.print(
10033
- `${c4.yellow}\u26A0 session interrupted${c4.reset} ${c4.dim}\u2014 the last operation never finished (the runtime is attempting recovery). Send a message to continue.${c4.reset}`
10280
+ `${c5.yellow}\u26A0 session interrupted${c5.reset} ${c5.dim}\u2014 the last operation never finished (the runtime is attempting recovery). Send a message to continue.${c5.reset}`
10034
10281
  );
10035
10282
  } else if (!snapshot.stalled && stalledNoticeShown && snapshot.busy) {
10036
10283
  stalledNoticeShown = false;
@@ -10063,7 +10310,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10063
10310
  continue;
10064
10311
  }
10065
10312
  if (m.role === "assistant" && text) printAssistant(tui, text);
10066
- else if (m.role === "system" && text) tui.print(`${c4.dim}${text}${c4.reset}`);
10313
+ else if (m.role === "system" && text) tui.print(`${c5.dim}${text}${c5.reset}`);
10067
10314
  else if (m.role === "user" && text) {
10068
10315
  const pending = pendingSent.get(text) ?? 0;
10069
10316
  if (pending > 0) {
@@ -10157,16 +10404,16 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10157
10404
  tui.setStep(null, 0);
10158
10405
  tui.setBackgroundCount(0);
10159
10406
  if (killed > 0) {
10160
- tui.print(`${c4.cyan}\u2699${c4.reset} Stopped ${killed} background process${killed === 1 ? "" : "es"}.`);
10407
+ tui.print(`${c5.cyan}\u2699${c5.reset} Stopped ${killed} background process${killed === 1 ? "" : "es"}.`);
10161
10408
  }
10162
- tui.print(`${c4.dim}\u2500\u2500 conversation cleared \u2014 starting a fresh session \u2500\u2500${c4.reset}`);
10409
+ tui.print(`${c5.dim}\u2500\u2500 conversation cleared \u2014 starting a fresh session \u2500\u2500${c5.reset}`);
10163
10410
  }
10164
10411
  async function runSkillsMenu(tui, skills) {
10165
10412
  let list;
10166
10413
  try {
10167
10414
  list = await skills.list();
10168
10415
  } catch (e) {
10169
- tui.print(`${c4.red}\u2717 couldn't load skills:${c4.reset} ${c4.gray}${e instanceof Error ? e.message : String(e)}${c4.reset}`);
10416
+ tui.print(`${c5.red}\u2717 couldn't load skills:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`);
10170
10417
  return;
10171
10418
  }
10172
10419
  const INSTALL = "__install__";
@@ -10177,7 +10424,7 @@ async function runSkillsMenu(tui, skills) {
10177
10424
  }));
10178
10425
  items.push({ label: "\uFF0B Install a skill\u2026", hint: "find & install", value: INSTALL });
10179
10426
  const picked = await tui.select(
10180
- `${c4.bold}Agent skills${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c4.reset}`,
10427
+ `${c5.bold}Agent skills${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c5.reset}`,
10181
10428
  items
10182
10429
  );
10183
10430
  if (!picked) return;
@@ -10190,8 +10437,8 @@ async function runSkillsMenu(tui, skills) {
10190
10437
  return;
10191
10438
  }
10192
10439
  const skill = list.find((s) => s.name === picked);
10193
- tui.print(`${c4.cyan}${skill.name}${c4.reset}${skill.version ? ` ${c4.dim}v${skill.version}${c4.reset}` : ""} ${c4.gray}\u2014 ${skill.description}${c4.reset}`);
10194
- const action = await tui.select(`${c4.bold}${picked}${c4.reset}`, [
10440
+ tui.print(`${c5.cyan}${skill.name}${c5.reset}${skill.version ? ` ${c5.dim}v${skill.version}${c5.reset}` : ""} ${c5.gray}\u2014 ${skill.description}${c5.reset}`);
10441
+ const action = await tui.select(`${c5.bold}${picked}${c5.reset}`, [
10195
10442
  skill.enabled ? { label: "Disable (hide from the agent)", value: "disable" } : { label: "Enable", value: "enable" },
10196
10443
  { label: "View files", value: "files" },
10197
10444
  { label: "Remove this skill", value: "remove" },
@@ -10200,20 +10447,20 @@ async function runSkillsMenu(tui, skills) {
10200
10447
  try {
10201
10448
  if (action === "enable" || action === "disable") {
10202
10449
  await skills.setEnabled(picked, action === "enable");
10203
- tui.print(`${c4.gray}${action}d ${picked}${c4.reset}`);
10450
+ tui.print(`${c5.gray}${action}d ${picked}${c5.reset}`);
10204
10451
  } else if (action === "files") {
10205
- for (const f of skill.files) tui.print(` ${c4.gray}${f}${c4.reset}`);
10452
+ for (const f of skill.files) tui.print(` ${c5.gray}${f}${c5.reset}`);
10206
10453
  } else if (action === "remove") {
10207
10454
  await skills.remove(picked);
10208
- tui.print(`${c4.gray}removed ${picked}${c4.reset}`);
10455
+ tui.print(`${c5.gray}removed ${picked}${c5.reset}`);
10209
10456
  }
10210
10457
  } catch (e) {
10211
- tui.print(`${c4.red}\u2717 ${e instanceof Error ? e.message : String(e)}${c4.reset}`);
10458
+ tui.print(`${c5.red}\u2717 ${e instanceof Error ? e.message : String(e)}${c5.reset}`);
10212
10459
  }
10213
10460
  }
10214
10461
  async function runLevelMenu(tui, perm) {
10215
10462
  const picked = await tui.select(
10216
- `${c4.bold}Auto-accept level${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 shift-tab cycles)${c4.reset}`,
10463
+ `${c5.bold}Auto-accept level${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 shift-tab cycles)${c5.reset}`,
10217
10464
  LEVELS.map((l) => ({
10218
10465
  label: levelLabel(l),
10219
10466
  hint: l === tui.level ? "current" : "",
@@ -10230,16 +10477,16 @@ async function runMachinesMenu(tui, api, self) {
10230
10477
  try {
10231
10478
  machines = await loadMachines(api);
10232
10479
  } catch (e) {
10233
- tui.print(`${c4.red}\u2717 couldn't load machines:${c4.reset} ${c4.gray}${e instanceof Error ? e.message : String(e)}${c4.reset}`);
10480
+ tui.print(`${c5.red}\u2717 couldn't load machines:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`);
10234
10481
  return;
10235
10482
  }
10236
10483
  if (!machines.length) {
10237
- tui.print(`${c4.gray}No machines registered yet. Run standardcode on a machine (or install its daemon) to register it.${c4.reset}`);
10484
+ tui.print(`${c5.gray}No machines registered yet. Run standardcode on a machine (or install its daemon) to register it.${c5.reset}`);
10238
10485
  return;
10239
10486
  }
10240
10487
  machines.sort((a, b) => (b.updated_at ?? 0) - (a.updated_at ?? 0));
10241
10488
  const picked = await tui.select(
10242
- `${c4.bold}Your machines${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 esc)${c4.reset}`,
10489
+ `${c5.bold}Your machines${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc)${c5.reset}`,
10243
10490
  machines.map((m) => {
10244
10491
  const isSelf = m.id === self.machine_id;
10245
10492
  const online = daemonOnline(m);
@@ -10273,26 +10520,26 @@ async function manageMachine(tui, api, self, machine) {
10273
10520
  options.push({ label: "Back", value: "back" });
10274
10521
  if (!isSelf && !machine.daemon) {
10275
10522
  tui.print(
10276
- `${c4.dim}${machine.name} has no daemon \u2014 you can rename it here; update and project changes need its daemon installed.${c4.reset}`
10523
+ `${c5.dim}${machine.name} has no daemon \u2014 you can rename it here; update and project changes need its daemon installed.${c5.reset}`
10277
10524
  );
10278
10525
  } else if (!isSelf && machine.daemon && !online) {
10279
10526
  tui.print(
10280
- `${c4.dim}${machine.name}'s daemon is offline \u2014 queued changes apply when it next comes online.${c4.reset}`
10527
+ `${c5.dim}${machine.name}'s daemon is offline \u2014 queued changes apply when it next comes online.${c5.reset}`
10281
10528
  );
10282
10529
  }
10283
- const action = await tui.select(`${c4.bold}${machineIcon(machine)} ${machine.name}${c4.reset}`, options);
10530
+ const action = await tui.select(`${c5.bold}${machineIcon(machine)} ${machine.name}${c5.reset}`, options);
10284
10531
  if (!action || action === "back") return;
10285
10532
  if (action === "icon") {
10286
10533
  const current = machine.icon ?? "";
10287
10534
  const emoji = await tui.prompt(
10288
- `${c4.bold}Icon for ${machine.name}${c4.reset} ${c4.dim}(paste an emoji, blank to reset)${c4.reset}`,
10535
+ `${c5.bold}Icon for ${machine.name}${c5.reset} ${c5.dim}(paste an emoji, blank to reset)${c5.reset}`,
10289
10536
  current
10290
10537
  );
10291
10538
  if (emoji !== null) {
10292
10539
  const trimmed = emoji.trim();
10293
10540
  await setMachineIcon(api, machine.id, trimmed);
10294
10541
  machine.icon = trimmed || void 0;
10295
- tui.print(`${c4.green}\u2713${c4.reset} icon ${trimmed ? `set to ${trimmed}` : "reset"} for ${machine.name}`);
10542
+ tui.print(`${c5.green}\u2713${c5.reset} icon ${trimmed ? `set to ${trimmed}` : "reset"} for ${machine.name}`);
10296
10543
  }
10297
10544
  return manageMachine(tui, api, self, machine);
10298
10545
  }
@@ -10308,7 +10555,7 @@ async function manageMachine(tui, api, self, machine) {
10308
10555
  const name = await tui.prompt(`New name for ${machine.name}`, machine.name);
10309
10556
  if (name === null || !name.trim()) return;
10310
10557
  await setMachineName(api, machine.id, name.trim());
10311
- tui.print(`${c4.green}\u2713${c4.reset} Renamed ${c4.bold}${machine.name}${c4.reset} \u2192 ${c4.bold}${name.trim()}${c4.reset}.`);
10558
+ tui.print(`${c5.green}\u2713${c5.reset} Renamed ${c5.bold}${machine.name}${c5.reset} \u2192 ${c5.bold}${name.trim()}${c5.reset}.`);
10312
10559
  } else if (action === "update") {
10313
10560
  if (isSelf) {
10314
10561
  await runUpdateCommand(tui);
@@ -10319,7 +10566,7 @@ async function manageMachine(tui, api, self, machine) {
10319
10566
  ]);
10320
10567
  if (go !== "yes") return;
10321
10568
  await dispatch("update");
10322
- tui.print(`${c4.green}\u2713${c4.reset} Update ${c4.gray}${applyNote} (its daemon updates and restarts on the new version).${c4.reset}`);
10569
+ tui.print(`${c5.green}\u2713${c5.reset} Update ${c5.gray}${applyNote} (its daemon updates and restarts on the new version).${c5.reset}`);
10323
10570
  }
10324
10571
  } else if (action === "projects") {
10325
10572
  await manageMachineProjects(tui, api, self, machine, dispatch, applyNote);
@@ -10330,7 +10577,7 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
10330
10577
  const paths = Object.keys(machine.projects).sort();
10331
10578
  const projLabels = projectDisplayLabels(machine.projects);
10332
10579
  const picked = await tui.select(
10333
- `${c4.bold}Projects on ${machine.name}${c4.reset} ${c4.dim}(\u2191\u2193 \xB7 enter \xB7 esc)${c4.reset}`,
10580
+ `${c5.bold}Projects on ${machine.name}${c5.reset} ${c5.dim}(\u2191\u2193 \xB7 enter \xB7 esc)${c5.reset}`,
10334
10581
  [
10335
10582
  ...paths.map((p) => ({
10336
10583
  label: projLabels.get(p) ?? projectDisplayName(p, machine.projects[p]),
@@ -10351,12 +10598,12 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
10351
10598
  });
10352
10599
  if (!chosen) return;
10353
10600
  await dispatch("add_project", { path: chosen });
10354
- tui.print(`${c4.green}\u2713${c4.reset} Add ${chosen} ${c4.gray}${applyNote}.${c4.reset}`);
10601
+ tui.print(`${c5.green}\u2713${c5.reset} Add ${chosen} ${c5.gray}${applyNote}.${c5.reset}`);
10355
10602
  return;
10356
10603
  }
10357
10604
  const project = machine.projects[picked];
10358
10605
  const displayName = projectDisplayName(picked, project);
10359
- const action = await tui.select(`${c4.bold}${displayName}${c4.reset} ${c4.dim}${shortenPath(picked, 48)}${c4.reset}`, [
10606
+ const action = await tui.select(`${c5.bold}${displayName}${c5.reset} ${c5.dim}${shortenPath(picked, 48)}${c5.reset}`, [
10360
10607
  { label: "Rename", hint: "display name only \u2014 the directory is untouched", value: "rename" },
10361
10608
  { label: "Remove from this machine's projects", hint: "doesn't delete the directory", value: "remove" },
10362
10609
  { label: "Back", value: "back" }
@@ -10371,60 +10618,60 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
10371
10618
  await setProjectName(api, machine.id, picked, name);
10372
10619
  const now = name.trim() || projectDisplayName(picked, null);
10373
10620
  if (project) project.name = now;
10374
- tui.print(`${c4.green}\u2713${c4.reset} Renamed ${c4.bold}${displayName}${c4.reset} \u2192 ${c4.bold}${now}${c4.reset}.`);
10621
+ tui.print(`${c5.green}\u2713${c5.reset} Renamed ${c5.bold}${displayName}${c5.reset} \u2192 ${c5.bold}${now}${c5.reset}.`);
10375
10622
  } else {
10376
10623
  await dispatch("remove_project", { path: picked });
10377
- tui.print(`${c4.green}\u2713${c4.reset} Remove ${picked} ${c4.gray}${applyNote}.${c4.reset}`);
10624
+ tui.print(`${c5.green}\u2713${c5.reset} Remove ${picked} ${c5.gray}${applyNote}.${c5.reset}`);
10378
10625
  }
10379
10626
  }
10380
10627
  function showDaemonInfo(tui, session) {
10381
10628
  if (session.mode === "remote" && session.runner) {
10382
10629
  tui.print(
10383
- `${c4.gray}This session runs on${c4.reset} ${c4.bold}${session.runner.name}${c4.reset} ${c4.gray}(${session.runner.hostname}) \u2014 its daemon executes the tools.${c4.reset}`
10630
+ `${c5.gray}This session runs on${c5.reset} ${c5.bold}${session.runner.name}${c5.reset} ${c5.gray}(${session.runner.hostname}) \u2014 its daemon executes the tools.${c5.reset}`
10384
10631
  );
10385
10632
  } else {
10386
- tui.print(`${c4.gray}This session runs on this machine.${c4.reset}`);
10633
+ tui.print(`${c5.gray}This session runs on this machine.${c5.reset}`);
10387
10634
  }
10388
10635
  const status = serviceStatus();
10389
10636
  tui.print(
10390
- `${c4.gray}Daemon on this machine:${c4.reset} ${status.installed ? status.detail : "not installed"}`
10637
+ `${c5.gray}Daemon on this machine:${c5.reset} ${status.installed ? status.detail : "not installed"}`
10391
10638
  );
10392
10639
  if (!status.installed) {
10393
10640
  tui.print(
10394
- `${c4.gray}Install it to start sessions on this machine from anywhere:${c4.reset} ${c4.bold}standardcode daemon install${c4.reset}`
10641
+ `${c5.gray}Install it to start sessions on this machine from anywhere:${c5.reset} ${c5.bold}standardcode daemon install${c5.reset}`
10395
10642
  );
10396
10643
  tui.print(
10397
- `${c4.dim}The daemon keeps running after you close the terminal \u2014 it self-restarts, self-updates, and executes sessions you start from other machines.${c4.reset}`
10644
+ `${c5.dim}The daemon keeps running after you close the terminal \u2014 it self-restarts, self-updates, and executes sessions you start from other machines.${c5.reset}`
10398
10645
  );
10399
10646
  } else {
10400
- tui.print(`${c4.dim}Manage it with standardcode daemon status | uninstall | add-project <path>.${c4.reset}`);
10647
+ tui.print(`${c5.dim}Manage it with standardcode daemon status | uninstall | add-project <path>.${c5.reset}`);
10401
10648
  }
10402
10649
  }
10403
10650
  function showKeybindings(tui) {
10404
- tui.print(`${c4.gray}shortcuts:${c4.reset}`);
10405
- tui.print(`${c4.gray} shift-tab${c4.reset} cycle auto-accept level (1\u20135)`);
10406
- tui.print(`${c4.gray} shift-\u23CE${c4.reset} insert a newline (multiline input)`);
10407
- tui.print(`${c4.gray} option-\u23CE${c4.reset} steer now \u2014 stops the current step and plays your message`);
10408
- tui.print(`${c4.gray} /${c4.reset} open the command palette (type to filter)`);
10409
- tui.print(`${c4.gray} !cmd${c4.reset} run a shell command on the session's machine (e.g. !ls); !! to send a literal !`);
10410
- tui.print(`${c4.gray} ctrl-v${c4.reset} paste an image from the clipboard ([#Image 1])`);
10411
- tui.print(`${c4.gray} \u2191 / \u2193${c4.reset} cycle past messages (on the input's top line)`);
10412
- tui.print(`${c4.gray} \u2190${c4.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
10413
- tui.print(`${c4.gray} ctrl-c${c4.reset} quit`);
10651
+ tui.print(`${c5.gray}shortcuts:${c5.reset}`);
10652
+ tui.print(`${c5.gray} shift-tab${c5.reset} cycle auto-accept level (1\u20135)`);
10653
+ tui.print(`${c5.gray} shift-\u23CE${c5.reset} insert a newline (multiline input)`);
10654
+ tui.print(`${c5.gray} option-\u23CE${c5.reset} steer now \u2014 stops the current step and plays your message`);
10655
+ tui.print(`${c5.gray} /${c5.reset} open the command palette (type to filter)`);
10656
+ tui.print(`${c5.gray} !cmd${c5.reset} run a shell command on the session's machine (e.g. !ls); !! to send a literal !`);
10657
+ tui.print(`${c5.gray} ctrl-v${c5.reset} paste an image from the clipboard ([#Image 1])`);
10658
+ tui.print(`${c5.gray} \u2191 / \u2193${c5.reset} cycle past messages (on the input's top line)`);
10659
+ tui.print(`${c5.gray} \u2190${c5.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
10660
+ tui.print(`${c5.gray} ctrl-c${c5.reset} quit`);
10414
10661
  }
10415
10662
  async function runUpdateCommand(tui) {
10416
10663
  const version = readVersion();
10417
10664
  const result = await forceCheckForUpdate(version);
10418
10665
  if (!result) {
10419
- tui.print(`${c4.green}\u2713${c4.reset} ${c4.gray}@standardagents/code${c4.reset} is up to date (v${version})`);
10666
+ tui.print(`${c5.green}\u2713${c5.reset} ${c5.gray}@standardagents/code${c5.reset} is up to date (v${version})`);
10420
10667
  return;
10421
10668
  }
10422
10669
  const { latest } = result;
10423
10670
  tui.print(`
10424
- ${c4.yellow}\u27F3${c4.reset} Update available: ${c4.gray}v${version}${c4.reset} \u2192 ${c4.green}v${latest}${c4.reset}`);
10671
+ ${c5.yellow}\u27F3${c5.reset} Update available: ${c5.gray}v${version}${c5.reset} \u2192 ${c5.green}v${latest}${c5.reset}`);
10425
10672
  const pm = detectPackageManager();
10426
10673
  if (!pm) {
10427
- tui.print(` ${c4.gray}This is a source checkout \u2014 pull the repo to update.${c4.reset}`);
10674
+ tui.print(` ${c5.gray}This is a source checkout \u2014 pull the repo to update.${c5.reset}`);
10428
10675
  return;
10429
10676
  }
10430
10677
  const { display } = updateCommand(pm);
@@ -10433,28 +10680,28 @@ async function runUpdateCommand(tui) {
10433
10680
  { label: "No, skip", value: "no" }
10434
10681
  ]);
10435
10682
  if (choice === "yes") {
10436
- tui.print(` ${c4.gray}Running ${display}\u2026${c4.reset}`);
10683
+ tui.print(` ${c5.gray}Running ${display}\u2026${c5.reset}`);
10437
10684
  const { ok, output: pmOutput } = await runUpdate(pm);
10438
10685
  if (ok) {
10439
- tui.print(` ${c4.green}\u2713${c4.reset} Updated to v${latest}. Restart to use the new version.`);
10686
+ tui.print(` ${c5.green}\u2713${c5.reset} Updated to v${latest}. Restart to use the new version.`);
10440
10687
  } else {
10441
- tui.print(` ${c4.red}\u2717${c4.reset} Update failed:`);
10688
+ tui.print(` ${c5.red}\u2717${c5.reset} Update failed:`);
10442
10689
  for (const line of pmOutput.trim().split("\n").slice(-6)) {
10443
- tui.print(` ${c4.dim}${line}${c4.reset}`);
10690
+ tui.print(` ${c5.dim}${line}${c5.reset}`);
10444
10691
  }
10445
10692
  }
10446
10693
  } else {
10447
- tui.print(` ${c4.gray}Skipped. Run /update later.${c4.reset}`);
10694
+ tui.print(` ${c5.gray}Skipped. Run /update later.${c5.reset}`);
10448
10695
  }
10449
10696
  }
10450
10697
  async function runProcessMenu(tui, bg) {
10451
10698
  const procs = await bg.list();
10452
10699
  if (!procs.length) {
10453
- tui.print(`${c4.gray}No background processes for this session.${c4.reset}`);
10700
+ tui.print(`${c5.gray}No background processes for this session.${c5.reset}`);
10454
10701
  return;
10455
10702
  }
10456
10703
  const items = procs.map((p) => {
10457
- const status = p.status === "running" ? `${c4.green}running${c4.reset}` : `${c4.gray}${p.status}${typeof p.exitCode === "number" ? ` (exit ${p.exitCode})` : ""}${c4.reset}`;
10704
+ const status = p.status === "running" ? `${c5.green}running${c5.reset}` : `${c5.gray}${p.status}${typeof p.exitCode === "number" ? ` (exit ${p.exitCode})` : ""}${c5.reset}`;
10458
10705
  return {
10459
10706
  label: `${p.description || p.command}`,
10460
10707
  hint: `${p.id} \xB7 ${status}`,
@@ -10462,22 +10709,22 @@ async function runProcessMenu(tui, bg) {
10462
10709
  };
10463
10710
  });
10464
10711
  const picked = await tui.select(
10465
- `${c4.bold}Background processes${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter to manage \xB7 esc to close)${c4.reset}`,
10712
+ `${c5.bold}Background processes${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter to manage \xB7 esc to close)${c5.reset}`,
10466
10713
  items
10467
10714
  );
10468
10715
  if (!picked) return;
10469
10716
  const proc = procs.find((p) => p.id === picked);
10470
10717
  if (!proc || proc.status !== "running") {
10471
- tui.print(`${c4.gray}${picked} is not running.${c4.reset}`);
10718
+ tui.print(`${c5.gray}${picked} is not running.${c5.reset}`);
10472
10719
  return;
10473
10720
  }
10474
- const action = await tui.select(`${c4.bold}${proc.description || proc.command}${c4.reset}`, [
10721
+ const action = await tui.select(`${c5.bold}${proc.description || proc.command}${c5.reset}`, [
10475
10722
  { label: "Stop this process", value: "stop" },
10476
10723
  { label: "Leave it running", value: "leave" }
10477
10724
  ]);
10478
10725
  if (action === "stop") {
10479
10726
  await bg.stop(picked);
10480
- tui.print(`${c4.gray}stopped ${picked}${c4.reset}`);
10727
+ tui.print(`${c5.gray}stopped ${picked}${c5.reset}`);
10481
10728
  }
10482
10729
  }
10483
10730
  async function runApprovalsMenu(tui, perm, save) {
@@ -10485,7 +10732,7 @@ async function runApprovalsMenu(tui, perm, save) {
10485
10732
  const risks = Array.from(perm.allowRisk).sort((a, b) => a - b);
10486
10733
  if (!tools.length && !risks.length) {
10487
10734
  tui.print(
10488
- `${c4.gray}No pre-approved commands. At a permission prompt, choose "Always allow this tool" or "Allow all level N" to add some.${c4.reset}`
10735
+ `${c5.gray}No pre-approved commands. At a permission prompt, choose "Always allow this tool" or "Allow all level N" to add some.${c5.reset}`
10489
10736
  );
10490
10737
  return;
10491
10738
  }
@@ -10495,22 +10742,22 @@ async function runApprovalsMenu(tui, perm, save) {
10495
10742
  { label: "Clear all approvals", hint: "", value: "clear" }
10496
10743
  ];
10497
10744
  const picked = await tui.select(
10498
- `${c4.bold}Approved commands${c4.reset} ${c4.dim}(enter to revoke \xB7 esc to close)${c4.reset}`,
10745
+ `${c5.bold}Approved commands${c5.reset} ${c5.dim}(enter to revoke \xB7 esc to close)${c5.reset}`,
10499
10746
  items
10500
10747
  );
10501
10748
  if (!picked) return;
10502
10749
  if (picked === "clear") {
10503
10750
  perm.alwaysAllow.clear();
10504
10751
  perm.allowRisk.clear();
10505
- tui.print(`${c4.gray}cleared all approvals${c4.reset}`);
10752
+ tui.print(`${c5.gray}cleared all approvals${c5.reset}`);
10506
10753
  } else if (picked.startsWith("tool:")) {
10507
10754
  const t = picked.slice(5);
10508
10755
  perm.alwaysAllow.delete(t);
10509
- tui.print(`${c4.gray}revoked tool ${t}${c4.reset}`);
10756
+ tui.print(`${c5.gray}revoked tool ${t}${c5.reset}`);
10510
10757
  } else if (picked.startsWith("risk:")) {
10511
10758
  const r = Number(picked.slice(5));
10512
10759
  perm.allowRisk.delete(r);
10513
- tui.print(`${c4.gray}revoked level ${r}${c4.reset}`);
10760
+ tui.print(`${c5.gray}revoked level ${r}${c5.reset}`);
10514
10761
  }
10515
10762
  save();
10516
10763
  }
@@ -10528,7 +10775,7 @@ async function runMcpMenu(tui, mcp) {
10528
10775
  items.push({ label: "\uFF0B Install a new MCP server\u2026", hint: "find & install", value: INSTALL });
10529
10776
  items.push({ label: "Add manually (name: command)\u2026", hint: "advanced", value: ADD_MANUAL });
10530
10777
  const picked = await tui.select(
10531
- `${c4.bold}MCP servers${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c4.reset}`,
10778
+ `${c5.bold}MCP servers${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c5.reset}`,
10532
10779
  items
10533
10780
  );
10534
10781
  if (!picked) return;
@@ -10542,7 +10789,7 @@ async function runMcpMenu(tui, mcp) {
10542
10789
  }
10543
10790
  const server = configured.find((s) => s.name === picked);
10544
10791
  const isConnected = connected.has(picked);
10545
- const action = await tui.select(`${c4.bold}${picked}${c4.reset}`, [
10792
+ const action = await tui.select(`${c5.bold}${picked}${c5.reset}`, [
10546
10793
  { label: "View tools", value: "tools" },
10547
10794
  isConnected ? { label: "Disconnect", value: "disconnect" } : { label: "Connect", value: "connect" },
10548
10795
  server.enabled ? { label: "Disable (don't auto-connect)", value: "disable" } : { label: "Enable (auto-connect on start)", value: "enable" },
@@ -10552,29 +10799,29 @@ async function runMcpMenu(tui, mcp) {
10552
10799
  if (action === "tools") {
10553
10800
  const entry = mcp.catalog().servers.find((e) => e.name === picked);
10554
10801
  if (!entry || entry.status !== "connected") {
10555
- tui.print(`${c4.gray}${picked} is not connected \u2014 connect it to list tools.${c4.reset}`);
10802
+ tui.print(`${c5.gray}${picked} is not connected \u2014 connect it to list tools.${c5.reset}`);
10556
10803
  return;
10557
10804
  }
10558
- if (!entry.tools.length) tui.print(`${c4.gray}${picked} exposes no tools.${c4.reset}`);
10559
- for (const t of entry.tools) tui.print(` ${c4.cyan}${t.name}${c4.reset}${t.description ? ` ${c4.gray}\u2014 ${t.description}${c4.reset}` : ""}`);
10560
- if (entry.resources.length) tui.print(` ${c4.gray}${entry.resources.length} resource(s)${c4.reset}`);
10805
+ if (!entry.tools.length) tui.print(`${c5.gray}${picked} exposes no tools.${c5.reset}`);
10806
+ for (const t of entry.tools) tui.print(` ${c5.cyan}${t.name}${c5.reset}${t.description ? ` ${c5.gray}\u2014 ${t.description}${c5.reset}` : ""}`);
10807
+ if (entry.resources.length) tui.print(` ${c5.gray}${entry.resources.length} resource(s)${c5.reset}`);
10561
10808
  } else if (action === "connect") {
10562
10809
  const res = await mcp.connect(server);
10563
- tui.print(res.ok ? `${c4.cyan}\u26A1 connected (${res.tools} tools)${c4.reset}` : `${c4.red}\u26A0 ${res.error}${c4.reset}`);
10810
+ tui.print(res.ok ? `${c5.cyan}\u26A1 connected (${res.tools} tools)${c5.reset}` : `${c5.red}\u26A0 ${res.error}${c5.reset}`);
10564
10811
  } else if (action === "disconnect") {
10565
10812
  mcp.disconnect(picked);
10566
- tui.print(`${c4.gray}disconnected ${picked}${c4.reset}`);
10813
+ tui.print(`${c5.gray}disconnected ${picked}${c5.reset}`);
10567
10814
  } else if (action === "enable") {
10568
10815
  mcp.setEnabled(picked, true);
10569
10816
  const res = await mcp.connect(server);
10570
- tui.print(res.ok ? `${c4.cyan}\u26A1 enabled + connected (${res.tools} tools)${c4.reset}` : `${c4.red}\u26A0 enabled but failed: ${res.error}${c4.reset}`);
10817
+ tui.print(res.ok ? `${c5.cyan}\u26A1 enabled + connected (${res.tools} tools)${c5.reset}` : `${c5.red}\u26A0 enabled but failed: ${res.error}${c5.reset}`);
10571
10818
  } else if (action === "disable") {
10572
10819
  mcp.setEnabled(picked, false);
10573
10820
  mcp.disconnect(picked);
10574
- tui.print(`${c4.gray}disabled + disconnected ${picked}${c4.reset}`);
10821
+ tui.print(`${c5.gray}disabled + disconnected ${picked}${c5.reset}`);
10575
10822
  } else if (action === "remove") {
10576
10823
  mcp.remove(picked);
10577
- tui.print(`${c4.gray}removed ${picked}${c4.reset}`);
10824
+ tui.print(`${c5.gray}removed ${picked}${c5.reset}`);
10578
10825
  }
10579
10826
  }
10580
10827
  async function addMcpServer(tui, mcp) {
@@ -10585,13 +10832,13 @@ async function addMcpServer(tui, mcp) {
10585
10832
  if (!spec) return;
10586
10833
  const cfg = parseServerSpec(spec);
10587
10834
  if (!cfg) {
10588
- tui.print(`${c4.yellow}couldn't parse that. Use name: command [args]${c4.reset}`);
10835
+ tui.print(`${c5.yellow}couldn't parse that. Use name: command [args]${c5.reset}`);
10589
10836
  return;
10590
10837
  }
10591
- tui.print(`${c4.gray}connecting MCP "${cfg.name}" (${cfg.command} ${cfg.args.join(" ")})\u2026${c4.reset}`);
10838
+ tui.print(`${c5.gray}connecting MCP "${cfg.name}" (${cfg.command} ${cfg.args.join(" ")})\u2026${c5.reset}`);
10592
10839
  const res = await mcp.add(cfg);
10593
- if (res.ok) tui.print(`${c4.cyan}\u26A1 MCP "${cfg.name}" connected${c4.reset} ${c4.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c4.reset}`);
10594
- else tui.print(`${c4.red}\u26A0 MCP "${cfg.name}" failed:${c4.reset} ${c4.gray}${res.error}${c4.reset} ${c4.dim}(saved; retry from the MCP menu)${c4.reset}`);
10840
+ if (res.ok) tui.print(`${c5.cyan}\u26A1 MCP "${cfg.name}" connected${c5.reset} ${c5.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c5.reset}`);
10841
+ else tui.print(`${c5.red}\u26A0 MCP "${cfg.name}" failed:${c5.reset} ${c5.gray}${res.error}${c5.reset} ${c5.dim}(saved; retry from the MCP menu)${c5.reset}`);
10595
10842
  }
10596
10843
  async function installMcpServerFlow(tui, mcp) {
10597
10844
  const query = await tui.prompt(