@standardagents/code 0.11.5 → 0.11.7

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
@@ -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 {
@@ -1565,8 +1595,9 @@ var Bridge = class {
1565
1595
  } finally {
1566
1596
  this.hooks.onStatus?.(callKey, null);
1567
1597
  }
1568
- if (req.durable && req.toolCallId) {
1569
- this.ledger.record(req.toolCallId, {
1598
+ const ledgerKey = req.toolCallId ?? req.id;
1599
+ if (ledgerKey) {
1600
+ this.ledger.record(ledgerKey, {
1570
1601
  ok: result.ok,
1571
1602
  result: result.ok ? result.result ?? "" : void 0,
1572
1603
  error: result.ok ? void 0 : result.error
@@ -2689,7 +2720,7 @@ var McpManager = class {
2689
2720
  }
2690
2721
  }
2691
2722
  closeAll() {
2692
- for (const [, c5] of this.clients) c5.close();
2723
+ for (const [, c6] of this.clients) c6.close();
2693
2724
  this.clients.clear();
2694
2725
  }
2695
2726
  get(name) {
@@ -2700,13 +2731,13 @@ var McpManager = class {
2700
2731
  }
2701
2732
  toolCount() {
2702
2733
  let n = 0;
2703
- for (const [, c5] of this.clients) n += c5.tools.length;
2734
+ for (const [, c6] of this.clients) n += c6.tools.length;
2704
2735
  return n;
2705
2736
  }
2706
2737
  /** A JSON-serializable catalog of every connected server for the KV/context. */
2707
2738
  catalog() {
2708
2739
  return {
2709
- servers: Array.from(this.clients.values()).map((c5) => c5.catalogEntry()),
2740
+ servers: Array.from(this.clients.values()).map((c6) => c6.catalogEntry()),
2710
2741
  generatedAt: Date.now()
2711
2742
  };
2712
2743
  }
@@ -2796,9 +2827,9 @@ function flattenContent(content, structured) {
2796
2827
  }
2797
2828
  function flattenResourceContents(contents) {
2798
2829
  const parts = [];
2799
- for (const c5 of contents || []) {
2800
- if (typeof c5.text === "string") parts.push(c5.text);
2801
- else if (typeof c5.blob === "string") parts.push(`[binary resource ${String(c5.uri ?? "")} (${c5.blob.length} b64 chars)]`);
2830
+ for (const c6 of contents || []) {
2831
+ if (typeof c6.text === "string") parts.push(c6.text);
2832
+ else if (typeof c6.blob === "string") parts.push(`[binary resource ${String(c6.uri ?? "")} (${c6.blob.length} b64 chars)]`);
2802
2833
  }
2803
2834
  return parts.join("\n").trim();
2804
2835
  }
@@ -3299,6 +3330,7 @@ function parseToolCalls(message) {
3299
3330
  var EMPTY_BOUNDARY_STALE_MS = 15e4;
3300
3331
  var WAITING_FOR_CLIENT_MARKER = "Waiting for your machine to reconnect";
3301
3332
  var PENDING_TAIL_STALE_MS = 10 * 6e4;
3333
+ var FORWARDED_TOOL_STALE_MS = 6.5 * 6e4;
3302
3334
  var BUSY_TAIL_STALE_MS = 30 * 6e4;
3303
3335
  function toMs(raw) {
3304
3336
  return raw > 1e14 ? raw / 1e3 : raw < 1e12 ? raw * 1e3 : raw;
@@ -3328,7 +3360,17 @@ function deriveSessionActivity(messages, nowMs = Date.now()) {
3328
3360
  return { busy: true, currentTool: { ...tool ?? { id: last.tool_call_id ?? "", name: "forwarded", arguments: {} }, waitingForHost: true } };
3329
3361
  }
3330
3362
  const currentTool = unresolvedTool(visible);
3331
- if (currentTool) return { busy: true, currentTool };
3363
+ if (currentTool) {
3364
+ const carrier = [...visible].reverse().find((message) => {
3365
+ const raw = message.tool_calls;
3366
+ return typeof raw === "string" && raw.includes(currentTool.id);
3367
+ });
3368
+ const carrierMs = carrier ? toMs(createdAt(carrier)) : 0;
3369
+ if (carrierMs > 0 && nowMs - carrierMs > FORWARDED_TOOL_STALE_MS) {
3370
+ return { busy: false, currentTool: null, stalled: true };
3371
+ }
3372
+ return { busy: true, currentTool };
3373
+ }
3332
3374
  if (last.role === "user" || last.role === "tool") {
3333
3375
  const lastMs = toMs(createdAt(last));
3334
3376
  if (lastMs > 0 && nowMs - lastMs > BUSY_TAIL_STALE_MS) {
@@ -3837,17 +3879,17 @@ function wrapBlock(out, cols2, leadFirst, leadRest, leadWidth, text) {
3837
3879
  function renderTable(rows) {
3838
3880
  const cols2 = Math.max(...rows.map((r) => r.length));
3839
3881
  const widths = [];
3840
- for (let c5 = 0; c5 < cols2; c5++) {
3841
- widths[c5] = Math.max(...rows.map((r) => visibleWidth(inline(r[c5] ?? ""))));
3882
+ for (let c6 = 0; c6 < cols2; c6++) {
3883
+ widths[c6] = Math.max(...rows.map((r) => visibleWidth(inline(r[c6] ?? ""))));
3842
3884
  }
3843
3885
  const sep = `${GRAY} \u2502 ${R}`;
3844
3886
  const out = [];
3845
3887
  rows.forEach((r, ri) => {
3846
3888
  const cells = [];
3847
- for (let c5 = 0; c5 < cols2; c5++) {
3848
- const raw = r[c5] ?? "";
3889
+ for (let c6 = 0; c6 < cols2; c6++) {
3890
+ const raw = r[c6] ?? "";
3849
3891
  const styled = ri === 0 ? `${BOLD}${inline(raw)}${R}` : inline(raw);
3850
- cells.push(padEndVisible(styled, widths[c5]));
3892
+ cells.push(padEndVisible(styled, widths[c6]));
3851
3893
  }
3852
3894
  out.push((" " + cells.join(sep)).replace(/\s+$/, ""));
3853
3895
  if (ri === 0) {
@@ -4319,7 +4361,7 @@ function buildInputBoxRows(opts) {
4319
4361
  let di = 0;
4320
4362
  for (let ci = 0; ci < plainChars.length; ci++) {
4321
4363
  const ch = plainChars[ci];
4322
- const c5 = colorAt(perimeterIndex("top", xi++, W, bodyH));
4364
+ const c6 = colorAt(perimeterIndex("top", xi++, W, bodyH));
4323
4365
  if (ci >= agentSpanStart && ci < agentSpanEnd) {
4324
4366
  top += `\x1B[2m${themeGray}${ch}\x1B[0m`;
4325
4367
  continue;
@@ -4328,9 +4370,9 @@ function buildInputBoxRows(opts) {
4328
4370
  const levelN = Math.max(1, Math.min(5, level));
4329
4371
  const filled = di < levelN;
4330
4372
  di++;
4331
- top += (filled ? levelColor || c5 : "\x1B[38;5;240m") + (filled ? "\u25CF" : "\u25CB") + reset;
4373
+ top += (filled ? levelColor || c6 : "\x1B[38;5;240m") + (filled ? "\u25CF" : "\u25CB") + reset;
4332
4374
  } else {
4333
- top += c5 + ch + reset;
4375
+ top += c6 + ch + reset;
4334
4376
  }
4335
4377
  }
4336
4378
  }
@@ -4351,10 +4393,10 @@ function buildInputBoxRows(opts) {
4351
4393
  let bottom = "";
4352
4394
  for (let x = 0; x < W; x++) {
4353
4395
  const idx = perimeterIndex("bottom", W - 1 - x, W, bodyH);
4354
- const c5 = colorAt(idx);
4355
- if (x === 0) bottom += c5 + "\u2570" + reset;
4356
- else if (x === W - 1) bottom += c5 + "\u256F" + reset;
4357
- else bottom += c5 + "\u2500" + reset;
4396
+ const c6 = colorAt(idx);
4397
+ if (x === 0) bottom += c6 + "\u2570" + reset;
4398
+ else if (x === W - 1) bottom += c6 + "\u256F" + reset;
4399
+ else bottom += c6 + "\u2500" + reset;
4358
4400
  }
4359
4401
  return [pad + top, ...bodyRows, pad + bottom];
4360
4402
  }
@@ -4464,7 +4506,7 @@ var Tui = class _Tui {
4464
4506
  // The `[⚙ n bg]` prompt badge can be selected with ← from the start of the
4465
4507
  // input (rendered inverted); Enter then opens the background-process panel.
4466
4508
  bgBadgeSelected = false;
4467
- queuedCount = 0;
4509
+ queuedMessages = [];
4468
4510
  subagents = [];
4469
4511
  // active subagents (one line each)
4470
4512
  subagentColorByID = /* @__PURE__ */ new Map();
@@ -5089,7 +5131,7 @@ var Tui = class _Tui {
5089
5131
  const q = this.inputBuffer.slice(1).trim().toLowerCase();
5090
5132
  if (q === "") return this.commands;
5091
5133
  return this.commands.filter(
5092
- (c5) => c5.name.startsWith(q) || c5.name.includes(q) || c5.label.toLowerCase().includes(q)
5134
+ (c6) => c6.name.startsWith(q) || c6.name.includes(q) || c6.label.toLowerCase().includes(q)
5093
5135
  );
5094
5136
  }
5095
5137
  runCommand(cmd) {
@@ -5139,11 +5181,10 @@ var Tui = class _Tui {
5139
5181
  }
5140
5182
  /** The prompt line prefix (with ANSI colour) that precedes the typed text. */
5141
5183
  promptPrefix() {
5142
- const q = this.queuedCount > 0 ? `${C.yellow}[\u23F3 ${this.queuedCount} queued]${C.reset} ` : "";
5143
5184
  const attachments2 = this.externalAttachmentNames.length > 0 ? `${C.gray}[\u{1F4CE} ${this.externalAttachmentNames.length}]${C.reset} ` : "";
5144
5185
  const bgText = `[\u2699 ${this.bgCount} bg]`;
5145
5186
  const bg = this.bgCount > 0 ? this.bgBadgeSelected ? `${C.cyan}\x1B[7m${bgText}\x1B[27m${C.reset} ` : `${C.cyan}${bgText}${C.reset} ` : "";
5146
- return `${q}${attachments2}${bg}${this.levelColor()}\u276F${C.reset} `;
5187
+ return `${attachments2}${bg}${this.levelColor()}\u276F${C.reset} `;
5147
5188
  }
5148
5189
  visibleWidth(s) {
5149
5190
  let w = 0;
@@ -5331,6 +5372,13 @@ var Tui = class _Tui {
5331
5372
  }
5332
5373
  const goalLines = this.goalLines(workCols);
5333
5374
  for (const line of goalLines) writeHudRow(workPad + line);
5375
+ for (const [i, text] of this.queuedMessages.entries()) {
5376
+ const hint = i === 0 ? ` ${C.dim}esc to steer \xB7 /queue to edit${C.reset}` : "";
5377
+ const budget = workCols - 3 - (i === 0 ? 30 : 0);
5378
+ let msg = text.replace(/\s+/g, " ").trim();
5379
+ if (budget > 1 && msg.length > budget) msg = msg.slice(0, budget - 1) + "\u2026";
5380
+ writeHudRow(workPad + `${C.yellow}\u23F3${C.reset} ${C.gray}${msg}${C.reset}${hint}`);
5381
+ }
5334
5382
  const prefix = this.promptPrefix();
5335
5383
  const pw = this.visibleWidth(prefix);
5336
5384
  const borderAnimating = this.working;
@@ -5684,9 +5732,9 @@ var Tui = class _Tui {
5684
5732
  if (n === 0) this.bgBadgeSelected = false;
5685
5733
  this.renderBottom();
5686
5734
  }
5687
- setQueuedCount(n) {
5688
- if (n === this.queuedCount) return;
5689
- this.queuedCount = n;
5735
+ setQueuedMessages(texts) {
5736
+ if (texts.length === this.queuedMessages.length && texts.every((t, i) => t === this.queuedMessages[i])) return;
5737
+ this.queuedMessages = texts;
5690
5738
  this.renderBottom();
5691
5739
  }
5692
5740
  setConnected(connected) {
@@ -6442,7 +6490,7 @@ function readVersion() {
6442
6490
  if (typeof pkg.version === "string" && pkg.version) return pkg.version;
6443
6491
  } catch {
6444
6492
  }
6445
- return "0.11.5" ;
6493
+ return "0.11.7" ;
6446
6494
  }
6447
6495
  function isLocalHost(host) {
6448
6496
  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);
@@ -6801,7 +6849,7 @@ async function clearDaemon(api, identity) {
6801
6849
  function parseCommands(value) {
6802
6850
  if (!Array.isArray(value)) return [];
6803
6851
  return value.filter(
6804
- (c5) => !!c5 && typeof c5 === "object" && typeof c5.id === "string" && typeof c5.kind === "string"
6852
+ (c6) => !!c6 && typeof c6 === "object" && typeof c6.id === "string" && typeof c6.kind === "string"
6805
6853
  );
6806
6854
  }
6807
6855
  async function enqueueMachineCommand(api, machineId, kind, args) {
@@ -6824,7 +6872,7 @@ async function clearMachineCommands(api, machineId, appliedIds) {
6824
6872
  if (appliedIds.length === 0) return;
6825
6873
  const key = commandKey(machineId);
6826
6874
  const remaining = parseCommands(await api.userKvGet(key)).filter(
6827
- (c5) => !appliedIds.includes(c5.id)
6875
+ (c6) => !appliedIds.includes(c6.id)
6828
6876
  );
6829
6877
  await api.userKvSet(key, remaining.length ? remaining : null);
6830
6878
  }
@@ -7210,7 +7258,7 @@ var HubSocket = class {
7210
7258
  }
7211
7259
  openSocket() {
7212
7260
  if (this.closed) return;
7213
- let url = `${this.api.wsEndpoint}/api/users/me/hub?token=${encodeURIComponent(this.api.bearer)}&client_id=${encodeURIComponent(this.clientId)}&client_kind=daemon`;
7261
+ let url = `${this.api.wsEndpoint}/api/users/me/stream?token=${encodeURIComponent(this.api.bearer)}&client_id=${encodeURIComponent(this.clientId)}&client_kind=daemon`;
7214
7262
  if (this.clientName) url += `&client_name=${encodeURIComponent(this.clientName)}`;
7215
7263
  let ws;
7216
7264
  try {
@@ -7239,8 +7287,15 @@ var HubSocket = class {
7239
7287
  } catch {
7240
7288
  return;
7241
7289
  }
7242
- if (msg && typeof msg === "object" && msg.type === "wake" && typeof msg.threadId === "string") {
7243
- this.hooks.onWake(msg.threadId);
7290
+ if (!msg || typeof msg !== "object") return;
7291
+ const frame = msg;
7292
+ if (frame.event === "standardagents.wake") {
7293
+ const threadId = frame.data?.thread_id;
7294
+ if (typeof threadId === "string") this.hooks.onWake(threadId);
7295
+ return;
7296
+ }
7297
+ if (frame.type === "wake" && typeof frame.threadId === "string") {
7298
+ this.hooks.onWake(frame.threadId);
7244
7299
  }
7245
7300
  }
7246
7301
  handleDrop(ws) {
@@ -8285,8 +8340,127 @@ async function runDaemonCommand(argv) {
8285
8340
  }
8286
8341
  }
8287
8342
 
8288
- // src/index.ts
8343
+ // src/auth-cli.ts
8289
8344
  var c4 = {
8345
+ reset: "\x1B[0m",
8346
+ dim: "\x1B[2m",
8347
+ bold: "\x1B[1m",
8348
+ green: "\x1B[32m",
8349
+ red: "\x1B[31m",
8350
+ teal: "\x1B[38;5;43m"
8351
+ };
8352
+ function parseAuthArgs(argv) {
8353
+ let endpoint = null;
8354
+ let token = null;
8355
+ for (let i = 0; i < argv.length; i += 1) {
8356
+ const arg = argv[i];
8357
+ if (arg === "--endpoint" || arg === "-e") {
8358
+ endpoint = argv[++i] ?? "";
8359
+ if (!endpoint) throw new Error("--endpoint requires a URL");
8360
+ } else if (arg.startsWith("--endpoint=")) {
8361
+ endpoint = arg.slice("--endpoint=".length);
8362
+ } else if (arg === "--token") {
8363
+ token = argv[++i] ?? "";
8364
+ if (!token) throw new Error("--token requires a value");
8365
+ } else if (arg.startsWith("--token=")) {
8366
+ token = arg.slice("--token=".length);
8367
+ } else {
8368
+ throw new Error(`Unknown option for login/logout: ${arg}`);
8369
+ }
8370
+ }
8371
+ const endpointExplicit = endpoint !== null;
8372
+ const resolved = normalizeEndpoint(endpoint ?? defaultEndpoint() ?? PRODUCTION_ENDPOINT);
8373
+ return { endpoint: resolved, endpointExplicit, token };
8374
+ }
8375
+ function hostLabel(endpoint) {
8376
+ return endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
8377
+ }
8378
+ async function signedInLabel(endpoint, token) {
8379
+ try {
8380
+ const res = await fetch(`${endpoint}/api/auth/me`, {
8381
+ headers: { Authorization: `Bearer ${token}` },
8382
+ signal: AbortSignal.timeout(1e4)
8383
+ });
8384
+ if (!res.ok) return null;
8385
+ const body = await res.json();
8386
+ return body.user?.email || body.user?.username || null;
8387
+ } catch {
8388
+ return null;
8389
+ }
8390
+ }
8391
+ async function runAuthCommand(command, argv) {
8392
+ let args;
8393
+ try {
8394
+ args = parseAuthArgs(argv);
8395
+ } catch (error) {
8396
+ process.stdout.write(`${c4.red}error:${c4.reset} ${error instanceof Error ? error.message : String(error)}
8397
+ `);
8398
+ process.stdout.write(`${c4.dim}usage: standardcode ${command} [--endpoint <url>]${command === "login" ? " [--token <key>]" : ""}${c4.reset}
8399
+ `);
8400
+ process.exitCode = 1;
8401
+ return;
8402
+ }
8403
+ const host = hostLabel(args.endpoint);
8404
+ relaxTlsForLocalEndpoint(args.endpoint);
8405
+ const existing = getCredential(args.endpoint);
8406
+ if (command === "logout") {
8407
+ if (!existing) {
8408
+ process.stdout.write(`You're not signed in to ${c4.teal}${host}${c4.reset} \u2014 nothing to do.
8409
+ `);
8410
+ return;
8411
+ }
8412
+ deleteCredential(args.endpoint);
8413
+ process.stdout.write(`${c4.green}\u2713${c4.reset} Signed out of ${c4.teal}${host}${c4.reset}.
8414
+ `);
8415
+ process.stdout.write(`${c4.dim}Run \`standardcode login\` to sign in again \u2014 with any account.${c4.reset}
8416
+ `);
8417
+ return;
8418
+ }
8419
+ if (existing) {
8420
+ const who2 = await signedInLabel(args.endpoint, existing.access_token);
8421
+ process.stdout.write(
8422
+ `${c4.dim}Currently signed in to ${host}${who2 ? ` as ${who2}` : ""} \u2014 replacing that sign-in.${c4.reset}
8423
+ `
8424
+ );
8425
+ }
8426
+ let token = args.token;
8427
+ if (!token) {
8428
+ process.stdout.write(`${c4.bold}Sign in to Standard Code${c4.reset}
8429
+ `);
8430
+ if (`https://${host}` !== PRODUCTION_ENDPOINT && `http://${host}` !== PRODUCTION_ENDPOINT) {
8431
+ process.stdout.write(`${c4.dim}Connecting to${c4.reset} ${c4.teal}${host}${c4.reset}
8432
+ `);
8433
+ }
8434
+ try {
8435
+ token = await deviceLogin(args.endpoint);
8436
+ } catch (error) {
8437
+ process.stdout.write(`${c4.red}\u2717${c4.reset} ${error instanceof Error ? error.message : String(error)}
8438
+ `);
8439
+ process.exitCode = 1;
8440
+ return;
8441
+ }
8442
+ }
8443
+ const api = new ApiClient(args.endpoint, token);
8444
+ const check = await api.verifyDetailed();
8445
+ if (!check.ok) {
8446
+ process.stdout.write(`${c4.red}\u2717${c4.reset} Sign-in didn't verify: ${check.reason}
8447
+ `);
8448
+ if (check.hint) process.stdout.write(` ${c4.dim}${check.hint}${c4.reset}
8449
+ `);
8450
+ process.exitCode = 1;
8451
+ return;
8452
+ }
8453
+ saveCredential(
8454
+ { endpoint: args.endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
8455
+ { updateDefault: !args.endpointExplicit }
8456
+ );
8457
+ const who = await signedInLabel(args.endpoint, token);
8458
+ process.stdout.write(`${c4.green}\u2713${c4.reset} Signed in to ${c4.teal}${host}${c4.reset}${who ? ` as ${c4.bold}${who}${c4.reset}` : ""}.
8459
+ `);
8460
+ }
8461
+
8462
+ // src/index.ts
8463
+ var c5 = {
8290
8464
  reset: "\x1B[0m",
8291
8465
  dim: "\x1B[2m",
8292
8466
  bold: "\x1B[1m",
@@ -8315,10 +8489,16 @@ function printUsage() {
8315
8489
  stdout.write(
8316
8490
  [
8317
8491
  "",
8318
- `${c4.bold}Usage${c4.reset}`,
8492
+ `${c5.bold}Usage${c5.reset}`,
8319
8493
  " standardcode [options] [dir]",
8494
+ " standardcode login [--endpoint <url>] [--token <key>]",
8495
+ " Sign in (or switch accounts \u2014 always re-runs the flow).",
8496
+ " standardcode logout [--endpoint <url>]",
8497
+ " Sign out of the saved endpoint.",
8498
+ " standardcode daemon <install|run|status|uninstall>",
8499
+ " Manage this machine's headless execution daemon.",
8320
8500
  "",
8321
- `${c4.bold}Options${c4.reset}`,
8501
+ `${c5.bold}Options${c5.reset}`,
8322
8502
  " -v, --version Print the Standard Code version and exit.",
8323
8503
  " -e, --endpoint [url] Use a different Standard Agents instance for this run",
8324
8504
  " (default: https://api.standardcode.ai).",
@@ -8387,15 +8567,15 @@ function parseArgs2(args) {
8387
8567
  }
8388
8568
  function printCommandBlock(tui, command, output4, ok, where) {
8389
8569
  tui.print("");
8390
- const note = where ? ` ${c4.dim}(ran on ${where})${c4.reset}` : "";
8391
- tui.print(`${c4.magenta}!${c4.reset} ${c4.bold}${command}${c4.reset}${note}`);
8570
+ const note = where ? ` ${c5.dim}(ran on ${where})${c5.reset}` : "";
8571
+ tui.print(`${c5.magenta}!${c5.reset} ${c5.bold}${command}${c5.reset}${note}`);
8392
8572
  const body = (output4 ?? "").replace(/\s+$/, "");
8393
8573
  if (body) {
8394
8574
  for (const line of body.split("\n")) {
8395
- tui.print(` ${ok ? c4.dim : c4.red}${line}${c4.reset}`);
8575
+ tui.print(` ${ok ? c5.dim : c5.red}${line}${c5.reset}`);
8396
8576
  }
8397
8577
  } else {
8398
- tui.print(` ${c4.dim}(no output)${c4.reset}`);
8578
+ tui.print(` ${c5.dim}(no output)${c5.reset}`);
8399
8579
  }
8400
8580
  tui.print("");
8401
8581
  }
@@ -8406,7 +8586,7 @@ function printAssistant(tui, text) {
8406
8586
  let dotted = false;
8407
8587
  for (const line of renderStreamingMarkdown(text, cols2)) {
8408
8588
  if (!dotted && line.trim()) {
8409
- tui.print(`${c4.gray}\u2022${c4.reset} ${line}`);
8589
+ tui.print(`${c5.gray}\u2022${c5.reset} ${line}`);
8410
8590
  dotted = true;
8411
8591
  } else {
8412
8592
  tui.print(` ${line}`);
@@ -8421,7 +8601,7 @@ function startLoader(label) {
8421
8601
  const draw = () => {
8422
8602
  const now = Date.now();
8423
8603
  const f = frames[Math.floor(now / 70) % frames.length];
8424
- stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${c4.reset} ${c4.dim}${label}\u2026${c4.reset}`);
8604
+ stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${c5.reset} ${c5.dim}${label}\u2026${c5.reset}`);
8425
8605
  };
8426
8606
  draw();
8427
8607
  const timer = setInterval(draw, 70);
@@ -8437,12 +8617,12 @@ function farewell(stoppedProcs = 0) {
8437
8617
  if (stoppedProcs > 0) {
8438
8618
  stdout.write(
8439
8619
  `
8440
- ${c4.cyan}\u2699${c4.reset} Stopped ${stoppedProcs} background process${stoppedProcs === 1 ? "" : "es"}.
8620
+ ${c5.cyan}\u2699${c5.reset} Stopped ${stoppedProcs} background process${stoppedProcs === 1 ? "" : "es"}.
8441
8621
  `
8442
8622
  );
8443
8623
  }
8444
8624
  stdout.write(`
8445
- ${c4.teal}\u25C7${c4.reset} ${c4.dim}Standard Code \u2014 see you soon.${c4.reset}
8625
+ ${c5.teal}\u25C7${c5.reset} ${c5.dim}Standard Code \u2014 see you soon.${c5.reset}
8446
8626
  `);
8447
8627
  }
8448
8628
  function printWelcome(endpoint, projectDir) {
@@ -8456,10 +8636,10 @@ function printWelcome(endpoint, projectDir) {
8456
8636
  const metaWidth = Math.max(1, terminalColumns - pad.length - markWidth - 3 - 1);
8457
8637
  const displayDir = truncateMiddle(dir2, metaWidth);
8458
8638
  const meta = [
8459
- `${c4.bold}${gradientText("Standard Code")}${c4.reset}${version ? ` ${c4.dim}v${version}${c4.reset}` : ""}`,
8460
- `${c4.dim}terminal coding agent${c4.reset}`,
8461
- ...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c4.teal}${host}${c4.reset}`],
8462
- `${c4.dim}${displayDir}${c4.reset}`
8639
+ `${c5.bold}${gradientText("Standard Code")}${c5.reset}${version ? ` ${c5.dim}v${version}${c5.reset}` : ""}`,
8640
+ `${c5.dim}terminal coding agent${c5.reset}`,
8641
+ ...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c5.teal}${host}${c5.reset}`],
8642
+ `${c5.dim}${displayDir}${c5.reset}`
8463
8643
  ];
8464
8644
  const metaTop = Math.floor((LOGO_MARK.length - meta.length) / 2);
8465
8645
  stdout.write("\n");
@@ -8473,11 +8653,11 @@ function printWelcome(endpoint, projectDir) {
8473
8653
  }
8474
8654
  function colorActivity(line) {
8475
8655
  const m = line.match(/^(\s*)([✓✗⛔])\s?([\s\S]*)$/);
8476
- if (!m) return `${c4.dim}${line}${c4.reset}`;
8656
+ if (!m) return `${c5.dim}${line}${c5.reset}`;
8477
8657
  const [, indent, glyph, rest] = m;
8478
8658
  if (glyph === "\u2713") {
8479
- const body = rest.replace(/\s(\([^()]*\))\s*$/, ` ${c4.dim}$1${c4.reset}`);
8480
- return `${indent}${c4.green}\u2713${c4.reset} ${body}`;
8659
+ const body = rest.replace(/\s(\([^()]*\))\s*$/, ` ${c5.dim}$1${c5.reset}`);
8660
+ return `${indent}${c5.green}\u2713${c5.reset} ${body}`;
8481
8661
  }
8482
8662
  if (glyph === "\u2717") {
8483
8663
  const ERR_MAX_LINES = 7;
@@ -8485,15 +8665,15 @@ function colorActivity(line) {
8485
8665
  const shown = lines.slice(0, ERR_MAX_LINES);
8486
8666
  const hidden = lines.length - shown.length;
8487
8667
  const body = shown.map(
8488
- (l, i) => i === 0 ? `${indent}${c4.red}\u2717 ${l}${c4.reset}` : `${indent}${c4.red}${c4.dim}${l}${c4.reset}`
8668
+ (l, i) => i === 0 ? `${indent}${c5.red}\u2717 ${l}${c5.reset}` : `${indent}${c5.red}${c5.dim}${l}${c5.reset}`
8489
8669
  ).join("\n");
8490
8670
  if (hidden > 0) {
8491
8671
  return `${body}
8492
- ${indent}${c4.dim}\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}${c4.reset}`;
8672
+ ${indent}${c5.dim}\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}${c5.reset}`;
8493
8673
  }
8494
8674
  return body;
8495
8675
  }
8496
- return `${indent}${c4.yellow}\u26D4 ${rest}${c4.reset}`;
8676
+ return `${indent}${c5.yellow}\u26D4 ${rest}${c5.reset}`;
8497
8677
  }
8498
8678
  async function main() {
8499
8679
  if (process.argv.slice(2).some((arg) => arg === "-v" || arg === "--version")) {
@@ -8505,11 +8685,15 @@ async function main() {
8505
8685
  await runDaemonCommand(process.argv.slice(3));
8506
8686
  return;
8507
8687
  }
8688
+ if (process.argv[2] === "login" || process.argv[2] === "logout") {
8689
+ await runAuthCommand(process.argv[2], process.argv.slice(3));
8690
+ return;
8691
+ }
8508
8692
  let cliArgs;
8509
8693
  try {
8510
8694
  cliArgs = parseArgs2(process.argv.slice(2));
8511
8695
  } catch (error) {
8512
- stdout.write(`${c4.red}error:${c4.reset} ${error instanceof Error ? error.message : String(error)}
8696
+ stdout.write(`${c5.red}error:${c5.reset} ${error instanceof Error ? error.message : String(error)}
8513
8697
  `);
8514
8698
  printUsage();
8515
8699
  process.exit(1);
@@ -8536,7 +8720,7 @@ async function main() {
8536
8720
  }
8537
8721
  preflightArmed = true;
8538
8722
  stdout.write(`
8539
- ${c4.dim}Press Control-C again to exit${c4.reset}
8723
+ ${c5.dim}Press Control-C again to exit${c5.reset}
8540
8724
  `);
8541
8725
  preflightTimer = setTimeout(() => {
8542
8726
  preflightArmed = false;
@@ -8558,10 +8742,10 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8558
8742
  const askEndpoint = async () => {
8559
8743
  for (; ; ) {
8560
8744
  const answer = (await ask(
8561
- `${c4.cyan}Standard Agents instance URL${c4.reset} (e.g. http://localhost:5178): `
8745
+ `${c5.cyan}Standard Agents instance URL${c5.reset} (e.g. http://localhost:5178): `
8562
8746
  )).trim();
8563
8747
  if (answer) return answer;
8564
- stdout.write(`${c4.dim}An endpoint URL is required.${c4.reset}
8748
+ stdout.write(`${c5.dim}An endpoint URL is required.${c5.reset}
8565
8749
  `);
8566
8750
  }
8567
8751
  };
@@ -8576,7 +8760,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8576
8760
  const tlsRelaxed = relaxTlsForLocalEndpoint(endpoint);
8577
8761
  printWelcome(endpoint, projectDir);
8578
8762
  if (tlsRelaxed) {
8579
- stdout.write(`${c4.dim} TLS verification relaxed for local endpoint.${c4.reset}
8763
+ stdout.write(`${c5.dim} TLS verification relaxed for local endpoint.${c5.reset}
8580
8764
 
8581
8765
  `);
8582
8766
  }
@@ -8588,7 +8772,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8588
8772
  loading.stop();
8589
8773
  const applied = consumeAppliedUpdate(version);
8590
8774
  if (applied) {
8591
- stdout.write(` ${c4.green}\u2713${c4.reset} ${c4.dim}Standard Code updated to v${version}.${c4.reset}
8775
+ stdout.write(` ${c5.green}\u2713${c5.reset} ${c5.dim}Standard Code updated to v${version}.${c5.reset}
8592
8776
 
8593
8777
  `);
8594
8778
  }
@@ -8597,21 +8781,21 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8597
8781
  const decision = decideAutoUpdate(updateAvailable, { state: readAutoUpdateState(), pm });
8598
8782
  if (decision === "start" && pm && startBackgroundUpdate(updateAvailable.latest, pm)) {
8599
8783
  stdout.write(
8600
- ` ${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}
8784
+ ` ${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}
8601
8785
 
8602
8786
  `
8603
8787
  );
8604
8788
  } else if (decision === "in_flight") {
8605
8789
  stdout.write(
8606
- ` ${c4.teal}\u27F3${c4.reset} ${c4.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c4.reset}
8790
+ ` ${c5.teal}\u27F3${c5.reset} ${c5.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c5.reset}
8607
8791
 
8608
8792
  `
8609
8793
  );
8610
8794
  } else {
8611
8795
  const display = updateCommand(pm ?? "npm").display;
8612
8796
  stdout.write(
8613
- ` ${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}
8614
- ${c4.dim}Run ${c4.reset}${c4.bold}${display}${c4.reset}${c4.dim} to update${c4.reset}
8797
+ ` ${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}
8798
+ ${c5.dim}Run ${c5.reset}${c5.bold}${display}${c5.reset}${c5.dim} to update${c5.reset}
8615
8799
 
8616
8800
  `
8617
8801
  );
@@ -8629,39 +8813,39 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8629
8813
  if (!api || !storedCheck?.ok) {
8630
8814
  const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
8631
8815
  if (storedCheck && !storedCheck.ok) {
8632
- stdout.write(`${c4.red}\u2717${c4.reset} ${c4.dim}Saved sign-in for this endpoint failed:${c4.reset} ${storedCheck.reason}
8816
+ stdout.write(`${c5.red}\u2717${c5.reset} ${c5.dim}Saved sign-in for this endpoint failed:${c5.reset} ${storedCheck.reason}
8633
8817
  `);
8634
- if (storedCheck.hint) stdout.write(` ${c4.dim}${storedCheck.hint}${c4.reset}
8818
+ if (storedCheck.hint) stdout.write(` ${c5.dim}${storedCheck.hint}${c5.reset}
8635
8819
  `);
8636
8820
  stdout.write("\n");
8637
8821
  }
8638
8822
  const explainFailure = (result, prefix) => {
8639
- stdout.write(`${c4.red}\u2717${c4.reset} ${prefix}${result.reason}
8823
+ stdout.write(`${c5.red}\u2717${c5.reset} ${prefix}${result.reason}
8640
8824
  `);
8641
- if (result.hint) stdout.write(` ${c4.dim}${result.hint}${c4.reset}
8825
+ if (result.hint) stdout.write(` ${c5.dim}${result.hint}${c5.reset}
8642
8826
  `);
8643
8827
  };
8644
- stdout.write(`${c4.bold}Sign in to ${gradientText("Standard Code")}${c4.reset}
8828
+ stdout.write(`${c5.bold}Sign in to ${gradientText("Standard Code")}${c5.reset}
8645
8829
  `);
8646
8830
  if (`https://${host}` !== PRODUCTION_ENDPOINT) {
8647
- stdout.write(`${c4.dim}Connecting to${c4.reset} ${c4.teal}${host}${c4.reset}
8831
+ stdout.write(`${c5.dim}Connecting to${c5.reset} ${c5.teal}${host}${c5.reset}
8648
8832
  `);
8649
8833
  }
8650
8834
  stdout.write(
8651
- `${c4.dim}You'll only need to do this once on this machine.${c4.reset}
8835
+ `${c5.dim}You'll only need to do this once on this machine.${c5.reset}
8652
8836
 
8653
8837
  `
8654
8838
  );
8655
8839
  stdout.write(
8656
- `${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}
8840
+ `${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}
8657
8841
 
8658
8842
  `
8659
8843
  );
8660
8844
  for (; ; ) {
8661
- const token = (await ask(`${c4.teal}\u276F${c4.reset} `)).trim();
8845
+ const token = (await ask(`${c5.teal}\u276F${c5.reset} `)).trim();
8662
8846
  if (!token) {
8663
8847
  const got = await deviceLogin(endpoint).catch((e) => {
8664
- stdout.write(`${c4.red}\u2717${c4.reset} ${c4.dim}${e instanceof Error ? e.message : String(e)}${c4.reset}
8848
+ stdout.write(`${c5.red}\u2717${c5.reset} ${c5.dim}${e instanceof Error ? e.message : String(e)}${c5.reset}
8665
8849
  `);
8666
8850
  return null;
8667
8851
  });
@@ -8675,7 +8859,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8675
8859
  { endpoint, access_token: got, token_type: "Bearer", saved_at: Date.now() },
8676
8860
  { updateDefault: !endpointOverride }
8677
8861
  );
8678
- stdout.write(`${c4.green}\u2713${c4.reset} Connected to ${c4.teal}${host}${c4.reset}
8862
+ stdout.write(`${c5.green}\u2713${c5.reset} Connected to ${c5.teal}${host}${c5.reset}
8679
8863
  `);
8680
8864
  break;
8681
8865
  }
@@ -8691,7 +8875,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8691
8875
  { endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
8692
8876
  { updateDefault: !endpointOverride }
8693
8877
  );
8694
- stdout.write(`${c4.green}\u2713${c4.reset} Connected to ${c4.teal}${host}${c4.reset}
8878
+ stdout.write(`${c5.green}\u2713${c5.reset} Connected to ${c5.teal}${host}${c5.reset}
8695
8879
  `);
8696
8880
  break;
8697
8881
  }
@@ -8715,7 +8899,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8715
8899
  } else if (["unlimited", "standard", "unlimited_one", AGENT_ID].includes(wanted)) {
8716
8900
  agentOverride = AGENT_ID;
8717
8901
  } else {
8718
- stdout.write(`${c4.red}error:${c4.reset} Unknown agent "${cliArgs.agent}". Use unlimited or sama.
8902
+ stdout.write(`${c5.red}error:${c5.reset} Unknown agent "${cliArgs.agent}". Use unlimited or sama.
8719
8903
  `);
8720
8904
  process.exit(1);
8721
8905
  }
@@ -8753,7 +8937,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8753
8937
  flow: for (; ; ) {
8754
8938
  if (step === "machine") {
8755
8939
  const picked = await tui.select(
8756
- `${c4.bold}Where should this session run?${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8940
+ `${c5.bold}Where should this session run?${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8757
8941
  [
8758
8942
  {
8759
8943
  label: `This machine \u2014 ${self ? machineDisplayName(self) : machine}`,
@@ -8837,7 +9021,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8837
9021
  items.push({ label: "\uFF0B Start a new session", value: null });
8838
9022
  const whereLabel = session.mode === "remote" && session.runner ? `${session.runner.name}:${shortenPath(session.remotePath ?? "")}` : shortDir;
8839
9023
  const picked = await tui.select(
8840
- `${c4.bold}Resume a session${c4.reset} ${c4.gray}${whereLabel}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9024
+ `${c5.bold}Resume a session${c5.reset} ${c5.gray}${whereLabel}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8841
9025
  items
8842
9026
  );
8843
9027
  if (picked === void 0) {
@@ -8863,7 +9047,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8863
9047
  } else {
8864
9048
  if (!agentOverride) {
8865
9049
  const picked = await tui.select(
8866
- `${c4.bold}Which agent?${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9050
+ `${c5.bold}Which agent?${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8867
9051
  AGENT_CHOICES.map((choice) => ({
8868
9052
  label: choice.title,
8869
9053
  hint: choice.description,
@@ -8913,7 +9097,7 @@ async function ensureSamaAuth(tui, api) {
8913
9097
  checking.stop();
8914
9098
  if (already) return true;
8915
9099
  const picked = await tui.select(
8916
- `${c4.bold}Authenticate with ChatGPT${c4.reset} ${c4.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c4.reset}`,
9100
+ `${c5.bold}Authenticate with ChatGPT${c5.reset} ${c5.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c5.reset}`,
8917
9101
  [
8918
9102
  {
8919
9103
  label: "Continue with ChatGPT",
@@ -8926,7 +9110,7 @@ async function ensureSamaAuth(tui, api) {
8926
9110
  if (picked !== "continue") return false;
8927
9111
  openUrl("https://standardcode.ai/app?connect=sama");
8928
9112
  tui.print(
8929
- `${c4.dim}Finish connecting ChatGPT in the browser \u2014 waiting here for the authorization to land on your account\u2026${c4.reset}`
9113
+ `${c5.dim}Finish connecting ChatGPT in the browser \u2014 waiting here for the authorization to land on your account\u2026${c5.reset}`
8930
9114
  );
8931
9115
  const waiting = startLoader("Waiting for your ChatGPT authorization");
8932
9116
  const deadline = Date.now() + 5 * 60 * 1e3;
@@ -8935,20 +9119,20 @@ async function ensureSamaAuth(tui, api) {
8935
9119
  if (await api.openSamaStatus().catch(() => false)) {
8936
9120
  waiting.stop();
8937
9121
  tui.print(
8938
- `${c4.green}\u2713${c4.reset} ChatGPT connected \u2014 Sama One is now unlocked on your account (terminal, web, and macOS app).`
9122
+ `${c5.green}\u2713${c5.reset} ChatGPT connected \u2014 Sama One is now unlocked on your account (terminal, web, and macOS app).`
8939
9123
  );
8940
9124
  return true;
8941
9125
  }
8942
9126
  }
8943
9127
  waiting.stop();
8944
9128
  tui.print(
8945
- `${c4.yellow}Still not connected.${c4.reset} Finish the flow at ${c4.teal}standardcode.ai/app${c4.reset} and pick Sama One again.`
9129
+ `${c5.yellow}Still not connected.${c5.reset} Finish the flow at ${c5.teal}standardcode.ai/app${c5.reset} and pick Sama One again.`
8946
9130
  );
8947
9131
  return false;
8948
9132
  }
8949
9133
  async function runAgentSwitchMenu(tui, api, threadId) {
8950
9134
  const picked = await tui.select(
8951
- `${c4.bold}Switch agent${c4.reset} ${c4.dim}takes effect on the next message${c4.reset}`,
9135
+ `${c5.bold}Switch agent${c5.reset} ${c5.dim}takes effect on the next message${c5.reset}`,
8952
9136
  AGENT_CHOICES.map((choice) => ({
8953
9137
  label: choice.title,
8954
9138
  hint: choice.description,
@@ -8965,10 +9149,10 @@ async function runAgentSwitchMenu(tui, api, threadId) {
8965
9149
  try {
8966
9150
  await api.setThreadAgent(threadId, picked);
8967
9151
  tui.setAgentLabel(title);
8968
- tui.print(`${c4.green}\u2713${c4.reset} Session handed to ${c4.bold}${title}${c4.reset} \u2014 applies from your next message.`);
9152
+ tui.print(`${c5.green}\u2713${c5.reset} Session handed to ${c5.bold}${title}${c5.reset} \u2014 applies from your next message.`);
8969
9153
  } catch (e) {
8970
9154
  tui.print(
8971
- `${c4.red}\u2717 couldn't switch agent:${c4.reset} ${c4.gray}${e instanceof Error ? e.message : String(e)}${c4.reset}`
9155
+ `${c5.red}\u2717 couldn't switch agent:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`
8972
9156
  );
8973
9157
  }
8974
9158
  }
@@ -8993,7 +9177,7 @@ async function pickLocalProject(tui, api, self, cwd, machineName) {
8993
9177
  { label: "\uFF0B New project", hint: "browse or create a directory", value: NEW }
8994
9178
  ];
8995
9179
  const picked = await tui.select(
8996
- `${c4.bold}Project on ${label}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9180
+ `${c5.bold}Project on ${label}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
8997
9181
  items,
8998
9182
  { spaced: true }
8999
9183
  );
@@ -9017,7 +9201,7 @@ async function pickRemoteProject(tui, api, runner) {
9017
9201
  );
9018
9202
  items.push({ label: "\uFF0B New project", hint: "browse or create a directory", value: ENTER_PATH });
9019
9203
  const picked = await tui.select(
9020
- `${c4.bold}Project on ${runner.name}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9204
+ `${c5.bold}Project on ${runner.name}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
9021
9205
  items,
9022
9206
  { spaced: true }
9023
9207
  );
@@ -9050,7 +9234,7 @@ async function summarizeThreads(api, threads) {
9050
9234
  }
9051
9235
  function subagentLabel(s, titles) {
9052
9236
  const agentName = (s.agent_name || "").trim();
9053
- const title = (s.title || "").trim() || titles.get(agentName) || (agentName ? agentName.replace(/[_-]+/g, " ").replace(/\b\w/g, (c5) => c5.toUpperCase()) : "Subagent");
9237
+ const title = (s.title || "").trim() || titles.get(agentName) || (agentName ? agentName.replace(/[_-]+/g, " ").replace(/\b\w/g, (c6) => c6.toUpperCase()) : "Subagent");
9054
9238
  const tagged = (s.threadName || "").trim();
9055
9239
  return tagged ? `${title} \xB7 ${tagged}` : title;
9056
9240
  }
@@ -9073,8 +9257,8 @@ async function printHistory(api, threadId, tui) {
9073
9257
  ).sort((a, b) => (a.created_at ?? 0) - (b.created_at ?? 0));
9074
9258
  if (!convo.length) return;
9075
9259
  const shown = convo.slice(-24);
9076
- tui.print(`${c4.dim}\u2500\u2500 resuming session \xB7 ${convo.length} message${convo.length === 1 ? "" : "s"} \u2500\u2500${c4.reset}`);
9077
- if (shown.length < convo.length) tui.print(`${c4.dim} \u2026 earlier messages omitted${c4.reset}`);
9260
+ tui.print(`${c5.dim}\u2500\u2500 resuming session \xB7 ${convo.length} message${convo.length === 1 ? "" : "s"} \u2500\u2500${c5.reset}`);
9261
+ if (shown.length < convo.length) tui.print(`${c5.dim} \u2026 earlier messages omitted${c5.reset}`);
9078
9262
  for (const m of shown) {
9079
9263
  if (m.metadata?.user_command) {
9080
9264
  printCommandBlock(
@@ -9135,6 +9319,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
9135
9319
  let editingPendingId = null;
9136
9320
  let reconcileSharedMessaging = async () => {
9137
9321
  };
9322
+ let applySharedMessagingEvent = () => false;
9138
9323
  let refreshSessionProjection = async () => {
9139
9324
  };
9140
9325
  const shownIds = /* @__PURE__ */ new Set();
@@ -9192,21 +9377,21 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
9192
9377
  if (isOwner) {
9193
9378
  bridge?.setClaim("takeover");
9194
9379
  exec?.writeSessionInfo();
9195
- if (wasKnown && moved) tui.print(`${c4.dim}Tool execution moved to this terminal.${c4.reset}`);
9380
+ if (wasKnown && moved) tui.print(`${c5.dim}Tool execution moved to this terminal.${c5.reset}`);
9196
9381
  } else if (wasKnown && moved) {
9197
- tui.print(`${c4.dim}Tool execution moved to ${describeExecOwner(owner2)}.${c4.reset}`);
9382
+ tui.print(`${c5.dim}Tool execution moved to ${describeExecOwner(owner2)}.${c5.reset}`);
9198
9383
  }
9199
9384
  },
9200
9385
  onClaimRefused: (reason) => {
9201
9386
  if (reason === "in_flight") {
9202
9387
  tui.print(
9203
- `${c4.dim}The session's current client is mid-operation \u2014 execution stays there until it finishes.${c4.reset}`
9388
+ `${c5.dim}The session's current client is mid-operation \u2014 execution stays there until it finishes.${c5.reset}`
9204
9389
  );
9205
9390
  }
9206
9391
  },
9207
9392
  onSuperseded: () => {
9208
9393
  tui.print(
9209
- `${c4.yellow}\u26A0 Another Standard Code process on this machine took over this session \u2014 this terminal is watching.${c4.reset}`
9394
+ `${c5.yellow}\u26A0 Another Standard Code process on this machine took over this session \u2014 this terminal is watching.${c5.reset}`
9210
9395
  );
9211
9396
  },
9212
9397
  onStatus: (id, summary) => {
@@ -9268,7 +9453,7 @@ why: ${req.requestPermission}` : ""}`,
9268
9453
  } else if (eventType === "goal_updated" && data) {
9269
9454
  tui.setGoal(data);
9270
9455
  } else if (eventType === SHARED_MESSAGING_EVENT) {
9271
- void reconcileSharedMessaging(true);
9456
+ if (!applySharedMessagingEvent(data)) void reconcileSharedMessaging(true);
9272
9457
  }
9273
9458
  },
9274
9459
  // A failed turn whose message is the lease service's at-limit denial → offer
@@ -9364,7 +9549,7 @@ why: ${req.requestPermission}` : ""}`,
9364
9549
  const logout = async () => {
9365
9550
  deleteCredential(api.origin);
9366
9551
  const instanceHost = api.origin.replace(/^https?:\/\//, "");
9367
- 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}`);
9552
+ 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}`);
9368
9553
  await quit();
9369
9554
  };
9370
9555
  const bgMgr = {
@@ -9372,7 +9557,7 @@ why: ${req.requestPermission}` : ""}`,
9372
9557
  stop: async (id) => {
9373
9558
  if (!host) {
9374
9559
  tui.print(
9375
- `${c4.gray}That process runs on ${runnerName} \u2014 ask the agent to stop it (it manages processes there).${c4.reset}`
9560
+ `${c5.gray}That process runs on ${runnerName} \u2014 ask the agent to stop it (it manages processes there).${c5.reset}`
9376
9561
  );
9377
9562
  return;
9378
9563
  }
@@ -9435,7 +9620,7 @@ why: ${req.requestPermission}` : ""}`,
9435
9620
  sharedMessaging = firstSnapshot ? snapshot : mergeSharedMessagingSnapshot(sharedMessaging, snapshot);
9436
9621
  sharedMessagingReady = true;
9437
9622
  sharedMessagingUnavailableShown = false;
9438
- tui.setQueuedCount(sharedMessaging.pending.items.length);
9623
+ tui.setQueuedMessages(sharedMessaging.pending.items.map((item) => item.content));
9439
9624
  if (mirrorDraft && (firstSnapshot || sharedMessaging.draft.revision > previousDraftRevision) && (firstSnapshot || sharedMessaging.draft.originClientId !== messagingOrigin.originClientId)) {
9440
9625
  mirroredDraftRefs = sharedMessaging.draft.attachments.filter(isSharedAttachmentRef);
9441
9626
  tui.setExternalAttachmentNames(mirroredDraftRefs.map((attachment) => attachment.name));
@@ -9453,17 +9638,25 @@ why: ${req.requestPermission}` : ""}`,
9453
9638
  } catch (error) {
9454
9639
  if (!sharedMessagingReady && !sharedMessagingUnavailableShown) {
9455
9640
  sharedMessagingUnavailableShown = true;
9456
- tui.print(`${c4.dim}shared messaging unavailable: ${error instanceof Error ? error.message : String(error)}${c4.reset}`);
9641
+ tui.print(`${c5.dim}shared messaging unavailable: ${error instanceof Error ? error.message : String(error)}${c5.reset}`);
9457
9642
  }
9458
9643
  }
9459
9644
  };
9645
+ applySharedMessagingEvent = (data) => {
9646
+ if (!sharedMessagingReady) return false;
9647
+ const event = parseMessagingChangedEvent(data);
9648
+ if (!event || !event.draft || event.draftOmitted) return false;
9649
+ if (event.pendingRevision > sharedMessaging.pending.revision) return false;
9650
+ applySharedMessaging({ version: 1, pending: sharedMessaging.pending, draft: event.draft }, true);
9651
+ return true;
9652
+ };
9460
9653
  const onTerminalResume = () => void reconcileSharedMessaging(true);
9461
9654
  process.on("SIGCONT", onTerminalResume);
9462
9655
  const applySharedMutation = (promise) => promise.then((snapshot) => {
9463
9656
  applySharedMessaging(snapshot, false);
9464
9657
  return true;
9465
9658
  }).catch((error) => {
9466
- tui.print(`${c4.dim}shared messaging failed: ${error instanceof Error ? error.message : String(error)}${c4.reset}`);
9659
+ tui.print(`${c5.dim}shared messaging failed: ${error instanceof Error ? error.message : String(error)}${c5.reset}`);
9467
9660
  return false;
9468
9661
  });
9469
9662
  const appendSharedPending = (text, images, refs = []) => applySharedMutation(api.appendPendingInput(threadId, {
@@ -9512,7 +9705,7 @@ why: ${req.requestPermission}` : ""}`,
9512
9705
  busy = false;
9513
9706
  optimisticBusyUntil = 0;
9514
9707
  tui.setWorking(false);
9515
- tui.print(`${c4.dim}failed to send: ${e instanceof Error ? e.message : String(e)}${c4.reset}`);
9708
+ tui.print(`${c5.dim}failed to send: ${e instanceof Error ? e.message : String(e)}${c5.reset}`);
9516
9709
  return false;
9517
9710
  }
9518
9711
  return true;
@@ -9521,11 +9714,11 @@ why: ${req.requestPermission}` : ""}`,
9521
9714
  let bangRunning = false;
9522
9715
  const runBangCommand = async (command) => {
9523
9716
  if (bangRunning) {
9524
- tui.print(`${c4.dim}a command is already running \u2014 one at a time.${c4.reset}`);
9717
+ tui.print(`${c5.dim}a command is already running \u2014 one at a time.${c5.reset}`);
9525
9718
  return;
9526
9719
  }
9527
9720
  bangRunning = true;
9528
- tui.print(`${c4.magenta}!${c4.reset} ${c4.dim}running on ${whereLabel}\u2026${c4.reset}`);
9721
+ tui.print(`${c5.magenta}!${c5.reset} ${c5.dim}running on ${whereLabel}\u2026${c5.reset}`);
9529
9722
  try {
9530
9723
  const res = await api.runCommand(threadId, command);
9531
9724
  if (res.messageId) shownIds.add(res.messageId);
@@ -9539,7 +9732,7 @@ why: ${req.requestPermission}` : ""}`,
9539
9732
  const openPendingMenu = async () => {
9540
9733
  const items = sharedMessaging.pending.items;
9541
9734
  if (!items.length) {
9542
- tui.print(`${c4.dim}No pending messages.${c4.reset}`);
9735
+ tui.print(`${c5.dim}No pending messages.${c5.reset}`);
9543
9736
  return;
9544
9737
  }
9545
9738
  const picked = await tui.select("Pending messages", items.map((item, index) => ({
@@ -9569,16 +9762,16 @@ why: ${req.requestPermission}` : ""}`,
9569
9762
  try {
9570
9763
  await api.compact(threadId);
9571
9764
  } catch (err) {
9572
- tui.print(`${c4.red}\u2717${c4.reset} couldn't start compaction: ${err.message}`);
9765
+ tui.print(`${c5.red}\u2717${c5.reset} couldn't start compaction: ${err.message}`);
9573
9766
  }
9574
9767
  };
9575
9768
  const runAccountCommand = async () => {
9576
- tui.print(`${c4.gray}Opening your account\u2026${c4.reset}`);
9769
+ tui.print(`${c5.gray}Opening your account\u2026${c5.reset}`);
9577
9770
  const link = await api.accountLink(threadId).catch(() => null);
9578
9771
  const target = link?.url ?? "https://standardcode.ai/account";
9579
9772
  openUrl(target);
9580
9773
  tui.print(
9581
- 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}`
9774
+ 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}`
9582
9775
  );
9583
9776
  };
9584
9777
  const ordinal = (n) => {
@@ -9589,24 +9782,24 @@ why: ${req.requestPermission}` : ""}`,
9589
9782
  const renderUpgradePanel = (q) => {
9590
9783
  const dots = [];
9591
9784
  for (let i = 0; i < q.max; i++) {
9592
- if (i < q.current) dots.push(`${c4.teal}\u25CF${c4.reset}`);
9593
- else if (i === q.current) dots.push(`${c4.bold}${gradientText("\uFF0B")}${c4.reset}`);
9594
- else dots.push(`${c4.dim}\xB7${c4.reset}`);
9785
+ if (i < q.current) dots.push(`${c5.teal}\u25CF${c5.reset}`);
9786
+ else if (i === q.current) dots.push(`${c5.bold}${gradientText("\uFF0B")}${c5.reset}`);
9787
+ else dots.push(`${c5.dim}\xB7${c5.reset}`);
9595
9788
  }
9596
9789
  const cost = fmtCost(q);
9597
9790
  const lines = [
9598
9791
  "",
9599
- `${c4.bold}\u2726 Add a parallel session${c4.reset}`,
9792
+ `${c5.bold}\u2726 Add a parallel session${c5.reset}`,
9600
9793
  "",
9601
- `${dots.join(" ")} ${c4.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c4.reset}`
9794
+ `${dots.join(" ")} ${c5.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c5.reset}`
9602
9795
  ];
9603
9796
  if (q.ends_trial) {
9604
9797
  lines.push(
9605
- `${c4.yellow}Your $5 trial covers 1 session. Adding a ${ordinal(q.sessions)} ends the trial${c4.reset}`,
9606
- `${c4.yellow}and starts your $49/mo plan now${cost ? ` \u2014 ${c4.bold}${cost} charged today${c4.reset}${c4.yellow}` : ""}.${c4.reset}`
9798
+ `${c5.yellow}Your $5 trial covers 1 session. Adding a ${ordinal(q.sessions)} ends the trial${c5.reset}`,
9799
+ `${c5.yellow}and starts your $49/mo plan now${cost ? ` \u2014 ${c5.bold}${cost} charged today${c5.reset}${c5.yellow}` : ""}.${c5.reset}`
9607
9800
  );
9608
9801
  } else if (cost) {
9609
- lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${c4.bold}${cost} charged now${c4.reset}.`);
9802
+ lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${c5.bold}${cost} charged now${c5.reset}.`);
9610
9803
  } else {
9611
9804
  lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 prorated on your next invoice.`);
9612
9805
  }
@@ -9619,7 +9812,7 @@ why: ${req.requestPermission}` : ""}`,
9619
9812
  try {
9620
9813
  if (opts.auto) {
9621
9814
  tui.print(
9622
- `${c4.yellow}You're out of simultaneous sessions \u2014 another Standard Code session is using your slot.${c4.reset}`
9815
+ `${c5.yellow}You're out of simultaneous sessions \u2014 another Standard Code session is using your slot.${c5.reset}`
9623
9816
  );
9624
9817
  }
9625
9818
  const quote = await api.sessionsQuote(threadId);
@@ -9627,16 +9820,16 @@ why: ${req.requestPermission}` : ""}`,
9627
9820
  const link = await api.accountLink(threadId).catch(() => null);
9628
9821
  const target = link?.url ?? "https://standardcode.ai/account";
9629
9822
  tui.print(
9630
- `${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}`
9823
+ `${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}`
9631
9824
  );
9632
9825
  openUrl(target);
9633
- tui.print(`${c4.gray}\u2192 opened ${target} to manage your plan${c4.reset}`);
9826
+ tui.print(`${c5.gray}\u2192 opened ${target} to manage your plan${c5.reset}`);
9634
9827
  return;
9635
9828
  }
9636
9829
  if (quote.current >= quote.max) {
9637
9830
  tui.print(
9638
- `${c4.yellow}You're at the maximum of ${quote.max} parallel session${quote.max === 1 ? "" : "s"}.${c4.reset}
9639
- ${c4.gray}Close another session (its slot frees within ~90s), then resend your message.${c4.reset}`
9831
+ `${c5.yellow}You're at the maximum of ${quote.max} parallel session${quote.max === 1 ? "" : "s"}.${c5.reset}
9832
+ ${c5.gray}Close another session (its slot frees within ~90s), then resend your message.${c5.reset}`
9640
9833
  );
9641
9834
  return;
9642
9835
  }
@@ -9648,25 +9841,25 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9648
9841
  { label: "Not now", value: "no" }
9649
9842
  ]);
9650
9843
  if (choice !== "go") {
9651
- tui.print(`${c4.gray}No change made \u2014 you can upgrade anytime with /upgrade.${c4.reset}`);
9844
+ tui.print(`${c5.gray}No change made \u2014 you can upgrade anytime with /upgrade.${c5.reset}`);
9652
9845
  return;
9653
9846
  }
9654
- tui.print(`${c4.gray}Applying\u2026${c4.reset}`);
9847
+ tui.print(`${c5.gray}Applying\u2026${c5.reset}`);
9655
9848
  let applied;
9656
9849
  try {
9657
9850
  applied = await api.sessionsUpgrade(threadId, quote.sessions);
9658
9851
  } catch (e) {
9659
- tui.print(`${c4.red}\u2717${c4.reset} Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
9852
+ tui.print(`${c5.red}\u2717${c5.reset} Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
9660
9853
  return;
9661
9854
  }
9662
9855
  if (!applied?.ok) {
9663
- tui.print(`${c4.red}\u2717${c4.reset} Upgrade failed: ${applied?.error ?? "unknown error"}`);
9856
+ tui.print(`${c5.red}\u2717${c5.reset} Upgrade failed: ${applied?.error ?? "unknown error"}`);
9664
9857
  return;
9665
9858
  }
9666
9859
  const n = applied.sessions ?? quote.sessions;
9667
- tui.print(`${c4.green}\u2713${c4.reset} ${c4.bold}${gradientText(`You now have ${n} parallel session${n === 1 ? "" : "s"}.`)}${c4.reset}`);
9860
+ tui.print(`${c5.green}\u2713${c5.reset} ${c5.bold}${gradientText(`You now have ${n} parallel session${n === 1 ? "" : "s"}.`)}${c5.reset}`);
9668
9861
  if (opts.auto && lastSent) {
9669
- tui.print(`${c4.gray}Continuing\u2026${c4.reset}`);
9862
+ tui.print(`${c5.gray}Continuing\u2026${c5.reset}`);
9670
9863
  await sendNow(lastSent.text, lastSent.images, lastSent.refs);
9671
9864
  }
9672
9865
  } finally {
@@ -9777,38 +9970,50 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9777
9970
  const history = await loadHistory(api, threadId, historySeedThreadId);
9778
9971
  tui.setHistory(history);
9779
9972
  await reconcileSharedMessaging(true);
9780
- let draftTimer;
9781
- const clearComposerDraft = () => {
9782
- if (draftTimer) clearTimeout(draftTimer);
9783
- draftTimer = void 0;
9784
- mirroredDraftRefs = [];
9785
- tui.setExternalAttachmentNames([]);
9786
- if (sharedMessagingReady) void applySharedMutation(api.clearSharedDraft(threadId, messagingOrigin));
9787
- };
9788
- tui.onDraftChange = (textVal, images) => {
9789
- if (draftTimer) clearTimeout(draftTimer);
9790
- draftTimer = setTimeout(() => {
9791
- draftTimer = void 0;
9792
- if (sharedMessagingReady) {
9793
- const hasDraft = !!textVal.trim() || images.length > 0 || mirroredDraftRefs.length > 0;
9973
+ let latestDraftPayload = null;
9974
+ let draftDirty = false;
9975
+ let draftInFlight = false;
9976
+ const pumpComposerDraft = async () => {
9977
+ if (draftInFlight) return;
9978
+ draftInFlight = true;
9979
+ try {
9980
+ while (draftDirty) {
9981
+ draftDirty = false;
9982
+ const payload = latestDraftPayload;
9983
+ if (!payload || !sharedMessagingReady) continue;
9984
+ const hasDraft = !!payload.text.trim() || payload.images.length > 0 || mirroredDraftRefs.length > 0;
9794
9985
  const mutation = {
9795
- content: textVal,
9986
+ content: payload.text,
9796
9987
  attachments: [
9797
9988
  ...hasDraft ? mirroredDraftRefs : [],
9798
- ...toSharedAttachments(images)
9989
+ ...toSharedAttachments(payload.images)
9799
9990
  ],
9800
9991
  ...messagingOrigin
9801
9992
  };
9802
- void applySharedMutation(
9993
+ await applySharedMutation(
9803
9994
  hasDraft ? api.putSharedDraft(threadId, mutation) : api.clearSharedDraft(threadId, messagingOrigin)
9804
9995
  );
9805
9996
  }
9806
- }, 150);
9997
+ } finally {
9998
+ draftInFlight = false;
9999
+ }
10000
+ };
10001
+ const clearComposerDraft = () => {
10002
+ mirroredDraftRefs = [];
10003
+ tui.setExternalAttachmentNames([]);
10004
+ latestDraftPayload = { text: "", images: [] };
10005
+ draftDirty = true;
10006
+ void pumpComposerDraft();
10007
+ };
10008
+ tui.onDraftChange = (textVal, images) => {
10009
+ latestDraftPayload = { text: textVal, images };
10010
+ draftDirty = true;
10011
+ void pumpComposerDraft();
9807
10012
  };
9808
10013
  const submitComposer = async (text, images, steer) => {
9809
10014
  const draftRefs = mirroredDraftRefs;
9810
10015
  if (!sharedMessagingReady && (busy || steer || editingPendingId !== null)) {
9811
- tui.print(`${c4.dim}Restoring shared message state \u2014 try again in a moment.${c4.reset}`);
10016
+ tui.print(`${c5.dim}Restoring shared message state \u2014 try again in a moment.${c5.reset}`);
9812
10017
  tui.setExternalAttachmentNames(draftRefs.map((attachment) => attachment.name));
9813
10018
  tui.setInput(text, images);
9814
10019
  return;
@@ -9831,7 +10036,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9831
10036
  const item = sharedMessaging.pending.items.find((candidate) => candidate.id === pendingId);
9832
10037
  editingPendingId = null;
9833
10038
  if (!item) {
9834
- tui.print(`${c4.dim}That pending message was already dispatched or dismissed.${c4.reset}`);
10039
+ tui.print(`${c5.dim}That pending message was already dispatched or dismissed.${c5.reset}`);
9835
10040
  return;
9836
10041
  }
9837
10042
  const updated = await editSharedPending(item, text, images, draftRefs);
@@ -9844,7 +10049,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9844
10049
  return;
9845
10050
  }
9846
10051
  if (steer) {
9847
- tui.print(`${c4.yellow}\u21AA steering now \u2014 stopping the current step${c4.reset}`);
10052
+ tui.print(`${c5.yellow}\u21AA steering now \u2014 stopping the current step${c5.reset}`);
9848
10053
  await Promise.all([
9849
10054
  api.stopThread(threadId).catch(() => {
9850
10055
  }),
@@ -9859,9 +10064,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9859
10064
  return;
9860
10065
  }
9861
10066
  if (busy) {
9862
- if (await appendSharedPending(text, images, draftRefs)) {
9863
- tui.print(`${c4.gray}\u23F3 pending:${c4.reset} ${text} ${c4.dim}(/queue to edit, steer, or dismiss)${c4.reset}`);
9864
- } else {
10067
+ if (await appendSharedPending(text, images, draftRefs)) ; else {
9865
10068
  mirroredDraftRefs = draftRefs;
9866
10069
  tui.setExternalAttachmentNames(draftRefs.map((attachment) => attachment.name));
9867
10070
  tui.setInput(text, images);
@@ -9883,13 +10086,18 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9883
10086
  tui.onInterrupt = () => {
9884
10087
  const firstPending = sharedMessaging.pending.items[0];
9885
10088
  if (!busy && firstPending) {
9886
- tui.print(`${c4.yellow}\u21AA steering the first pending message\u2026${c4.reset}`);
10089
+ tui.print(`${c5.yellow}\u21AA steering the first pending message\u2026${c5.reset}`);
9887
10090
  void promoteSharedPending(firstPending);
9888
10091
  return;
9889
10092
  }
9890
10093
  if (busy) {
9891
- const queued = sharedMessaging.pending.items.length;
9892
- tui.print(`${c4.yellow}\u25A0 stopping now${queued > 0 ? ` \u2014 ${queued} queued message${queued === 1 ? "" : "s"} kept` : ""}${c4.reset}`);
10094
+ const head = sharedMessaging.pending.items[0];
10095
+ if (head) {
10096
+ tui.print(`${c5.yellow}\u21AA steering \u2014 stopping the current step to run the queued message${c5.reset}`);
10097
+ void promoteSharedPending(head);
10098
+ return;
10099
+ }
10100
+ tui.print(`${c5.yellow}\u25A0 stopping now${c5.reset}`);
9893
10101
  const stops = [api.stopThread(threadId).catch(() => {
9894
10102
  })];
9895
10103
  for (const childId of activeSubagents.keys()) {
@@ -9919,7 +10127,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9919
10127
  void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
9920
10128
  });
9921
10129
  attaching.stop();
9922
- const header = `${c4.bold}${c4.magenta}Standard Code${c4.reset} ${c4.dim}\u2014 ${agentTitle}${c4.reset}`;
10130
+ const header = `${c5.bold}${c5.magenta}Standard Code${c5.reset} ${c5.dim}\u2014 ${agentTitle}${c5.reset}`;
9923
10131
  const remoteDaemonV = session.runner?.daemon?.version;
9924
10132
  const owner = currentOwner;
9925
10133
  let localOwnerDesc = ownsExecution ? "this terminal" : describeExecOwner(owner);
@@ -9927,24 +10135,24 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9927
10135
  const selfRec = await loadMachine(api, session.identity.machine_id).catch(() => null);
9928
10136
  if (selfRec?.daemon?.version) localOwnerDesc = `the daemon on this machine (v${selfRec.daemon.version})`;
9929
10137
  }
9930
- const execLine = remote ? `${c4.gray}tool execution:${c4.reset} daemon${remoteDaemonV ? ` v${remoteDaemonV}` : ""} on ${runnerName}` : `${c4.gray}tool execution:${c4.reset} ${localOwnerDesc}`;
10138
+ const execLine = remote ? `${c5.gray}tool execution:${c5.reset} daemon${remoteDaemonV ? ` v${remoteDaemonV}` : ""} on ${runnerName}` : `${c5.gray}tool execution:${c5.reset} ${localOwnerDesc}`;
9931
10139
  tui.setAgentLabel(agentTitle);
9932
10140
  tui.banner(
9933
10141
  remote ? [
9934
10142
  header,
9935
- `${c4.gray}project:${c4.reset} ${session.remotePath ?? "?"} ${c4.teal}on ${runnerName}${c4.reset}`,
9936
- `${c4.gray}machine:${c4.reset} ${runnerName} ${c4.gray}thread:${c4.reset} ${threadId.slice(0, 8)}`,
10143
+ `${c5.gray}project:${c5.reset} ${session.remotePath ?? "?"} ${c5.teal}on ${runnerName}${c5.reset}`,
10144
+ `${c5.gray}machine:${c5.reset} ${runnerName} ${c5.gray}thread:${c5.reset} ${threadId.slice(0, 8)}`,
9937
10145
  execLine
9938
10146
  ] : [
9939
10147
  header,
9940
- `${c4.gray}project:${c4.reset} ${projectDir}`,
9941
- `${c4.gray}machine:${c4.reset} ${machine} ${c4.gray}thread:${c4.reset} ${threadId.slice(0, 8)}`,
10148
+ `${c5.gray}project:${c5.reset} ${projectDir}`,
10149
+ `${c5.gray}machine:${c5.reset} ${machine} ${c5.gray}thread:${c5.reset} ${threadId.slice(0, 8)}`,
9942
10150
  execLine
9943
10151
  ]
9944
10152
  );
9945
10153
  if (!remote && session.suggestDaemonInstall && process.platform !== "win32" && !serviceStatus().installed) {
9946
10154
  tui.print(
9947
- `${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}`
10155
+ `${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}`
9948
10156
  );
9949
10157
  }
9950
10158
  if (resumed) await printHistory(api, threadId, tui);
@@ -9955,17 +10163,17 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9955
10163
  const runningProcs = (await registry.list()).filter((p) => p.status === "running");
9956
10164
  if (runningProcs.length) {
9957
10165
  tui.print(
9958
- `${c4.cyan}\u2699 ${runningProcs.length} background process${runningProcs.length === 1 ? "" : "es"} running:${c4.reset}`
10166
+ `${c5.cyan}\u2699 ${runningProcs.length} background process${runningProcs.length === 1 ? "" : "es"} running:${c5.reset}`
9959
10167
  );
9960
- for (const p of runningProcs) tui.print(`${c4.gray} ${p.id} ${p.description || p.command}${c4.reset}`);
10168
+ for (const p of runningProcs) tui.print(`${c5.gray} ${p.id} ${p.description || p.command}${c5.reset}`);
9961
10169
  }
9962
10170
  refreshBgCount();
9963
10171
  if (exec && ownsExecution) {
9964
10172
  for (const res of await exec.connectEnabledMcpServers()) {
9965
10173
  if (res.ok) {
9966
- tui.print(`${c4.cyan}\u26A1 MCP "${res.name}" connected${c4.reset} ${c4.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c4.reset}`);
10174
+ tui.print(`${c5.cyan}\u26A1 MCP "${res.name}" connected${c5.reset} ${c5.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c5.reset}`);
9967
10175
  } else {
9968
- tui.print(`${c4.red}\u26A0 MCP "${res.name}" failed:${c4.reset} ${c4.gray}${res.error}${c4.reset}`);
10176
+ tui.print(`${c5.red}\u26A0 MCP "${res.name}" failed:${c5.reset} ${c5.gray}${res.error}${c5.reset}`);
9969
10177
  }
9970
10178
  }
9971
10179
  }
@@ -9981,8 +10189,8 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
9981
10189
  try {
9982
10190
  const { choice, reason } = await tui.approval(
9983
10191
  `${request.summary}${request.permission ? `
9984
- ${c4.bold}why: ${request.permission}${c4.reset}` : ""}
9985
- ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10192
+ ${c5.bold}why: ${request.permission}${c5.reset}` : ""}
10193
+ ${c5.dim}runs on ${request.machine || runnerName}${c5.reset}`,
9986
10194
  request.risk
9987
10195
  );
9988
10196
  answeredApprovals.add(request.tool_call_id);
@@ -9996,6 +10204,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
9996
10204
  approvalPromptOpen = false;
9997
10205
  }
9998
10206
  };
10207
+ let stalledNoticeShown = false;
9999
10208
  const poll = async () => {
10000
10209
  let msgs;
10001
10210
  let serverBusy = null;
@@ -10005,6 +10214,14 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10005
10214
  msgs = snapshot.messages.slice(-60);
10006
10215
  serverBusy = snapshot.busy;
10007
10216
  serverTool = snapshot.current_tool;
10217
+ if (snapshot.stalled && !stalledNoticeShown) {
10218
+ stalledNoticeShown = true;
10219
+ tui.print(
10220
+ `${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}`
10221
+ );
10222
+ } else if (!snapshot.stalled && stalledNoticeShown && snapshot.busy) {
10223
+ stalledNoticeShown = false;
10224
+ }
10008
10225
  } catch {
10009
10226
  try {
10010
10227
  msgs = await api.getMessages(threadId, 60);
@@ -10033,7 +10250,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10033
10250
  continue;
10034
10251
  }
10035
10252
  if (m.role === "assistant" && text) printAssistant(tui, text);
10036
- else if (m.role === "system" && text) tui.print(`${c4.dim}${text}${c4.reset}`);
10253
+ else if (m.role === "system" && text) tui.print(`${c5.dim}${text}${c5.reset}`);
10037
10254
  else if (m.role === "user" && text) {
10038
10255
  const pending = pendingSent.get(text) ?? 0;
10039
10256
  if (pending > 0) {
@@ -10122,21 +10339,21 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
10122
10339
  tui.setWorking(false);
10123
10340
  tui.setSubagents([]);
10124
10341
  tui.setGoal(null);
10125
- tui.setQueuedCount(0);
10342
+ tui.setQueuedMessages([]);
10126
10343
  tui.setContextPct(null);
10127
10344
  tui.setStep(null, 0);
10128
10345
  tui.setBackgroundCount(0);
10129
10346
  if (killed > 0) {
10130
- tui.print(`${c4.cyan}\u2699${c4.reset} Stopped ${killed} background process${killed === 1 ? "" : "es"}.`);
10347
+ tui.print(`${c5.cyan}\u2699${c5.reset} Stopped ${killed} background process${killed === 1 ? "" : "es"}.`);
10131
10348
  }
10132
- tui.print(`${c4.dim}\u2500\u2500 conversation cleared \u2014 starting a fresh session \u2500\u2500${c4.reset}`);
10349
+ tui.print(`${c5.dim}\u2500\u2500 conversation cleared \u2014 starting a fresh session \u2500\u2500${c5.reset}`);
10133
10350
  }
10134
10351
  async function runSkillsMenu(tui, skills) {
10135
10352
  let list;
10136
10353
  try {
10137
10354
  list = await skills.list();
10138
10355
  } catch (e) {
10139
- tui.print(`${c4.red}\u2717 couldn't load skills:${c4.reset} ${c4.gray}${e instanceof Error ? e.message : String(e)}${c4.reset}`);
10356
+ tui.print(`${c5.red}\u2717 couldn't load skills:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`);
10140
10357
  return;
10141
10358
  }
10142
10359
  const INSTALL = "__install__";
@@ -10147,7 +10364,7 @@ async function runSkillsMenu(tui, skills) {
10147
10364
  }));
10148
10365
  items.push({ label: "\uFF0B Install a skill\u2026", hint: "find & install", value: INSTALL });
10149
10366
  const picked = await tui.select(
10150
- `${c4.bold}Agent skills${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c4.reset}`,
10367
+ `${c5.bold}Agent skills${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c5.reset}`,
10151
10368
  items
10152
10369
  );
10153
10370
  if (!picked) return;
@@ -10160,8 +10377,8 @@ async function runSkillsMenu(tui, skills) {
10160
10377
  return;
10161
10378
  }
10162
10379
  const skill = list.find((s) => s.name === picked);
10163
- tui.print(`${c4.cyan}${skill.name}${c4.reset}${skill.version ? ` ${c4.dim}v${skill.version}${c4.reset}` : ""} ${c4.gray}\u2014 ${skill.description}${c4.reset}`);
10164
- const action = await tui.select(`${c4.bold}${picked}${c4.reset}`, [
10380
+ tui.print(`${c5.cyan}${skill.name}${c5.reset}${skill.version ? ` ${c5.dim}v${skill.version}${c5.reset}` : ""} ${c5.gray}\u2014 ${skill.description}${c5.reset}`);
10381
+ const action = await tui.select(`${c5.bold}${picked}${c5.reset}`, [
10165
10382
  skill.enabled ? { label: "Disable (hide from the agent)", value: "disable" } : { label: "Enable", value: "enable" },
10166
10383
  { label: "View files", value: "files" },
10167
10384
  { label: "Remove this skill", value: "remove" },
@@ -10170,20 +10387,20 @@ async function runSkillsMenu(tui, skills) {
10170
10387
  try {
10171
10388
  if (action === "enable" || action === "disable") {
10172
10389
  await skills.setEnabled(picked, action === "enable");
10173
- tui.print(`${c4.gray}${action}d ${picked}${c4.reset}`);
10390
+ tui.print(`${c5.gray}${action}d ${picked}${c5.reset}`);
10174
10391
  } else if (action === "files") {
10175
- for (const f of skill.files) tui.print(` ${c4.gray}${f}${c4.reset}`);
10392
+ for (const f of skill.files) tui.print(` ${c5.gray}${f}${c5.reset}`);
10176
10393
  } else if (action === "remove") {
10177
10394
  await skills.remove(picked);
10178
- tui.print(`${c4.gray}removed ${picked}${c4.reset}`);
10395
+ tui.print(`${c5.gray}removed ${picked}${c5.reset}`);
10179
10396
  }
10180
10397
  } catch (e) {
10181
- tui.print(`${c4.red}\u2717 ${e instanceof Error ? e.message : String(e)}${c4.reset}`);
10398
+ tui.print(`${c5.red}\u2717 ${e instanceof Error ? e.message : String(e)}${c5.reset}`);
10182
10399
  }
10183
10400
  }
10184
10401
  async function runLevelMenu(tui, perm) {
10185
10402
  const picked = await tui.select(
10186
- `${c4.bold}Auto-accept level${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 shift-tab cycles)${c4.reset}`,
10403
+ `${c5.bold}Auto-accept level${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 shift-tab cycles)${c5.reset}`,
10187
10404
  LEVELS.map((l) => ({
10188
10405
  label: levelLabel(l),
10189
10406
  hint: l === tui.level ? "current" : "",
@@ -10200,16 +10417,16 @@ async function runMachinesMenu(tui, api, self) {
10200
10417
  try {
10201
10418
  machines = await loadMachines(api);
10202
10419
  } catch (e) {
10203
- tui.print(`${c4.red}\u2717 couldn't load machines:${c4.reset} ${c4.gray}${e instanceof Error ? e.message : String(e)}${c4.reset}`);
10420
+ tui.print(`${c5.red}\u2717 couldn't load machines:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`);
10204
10421
  return;
10205
10422
  }
10206
10423
  if (!machines.length) {
10207
- tui.print(`${c4.gray}No machines registered yet. Run standardcode on a machine (or install its daemon) to register it.${c4.reset}`);
10424
+ tui.print(`${c5.gray}No machines registered yet. Run standardcode on a machine (or install its daemon) to register it.${c5.reset}`);
10208
10425
  return;
10209
10426
  }
10210
10427
  machines.sort((a, b) => (b.updated_at ?? 0) - (a.updated_at ?? 0));
10211
10428
  const picked = await tui.select(
10212
- `${c4.bold}Your machines${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 esc)${c4.reset}`,
10429
+ `${c5.bold}Your machines${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc)${c5.reset}`,
10213
10430
  machines.map((m) => {
10214
10431
  const isSelf = m.id === self.machine_id;
10215
10432
  const online = daemonOnline(m);
@@ -10243,26 +10460,26 @@ async function manageMachine(tui, api, self, machine) {
10243
10460
  options.push({ label: "Back", value: "back" });
10244
10461
  if (!isSelf && !machine.daemon) {
10245
10462
  tui.print(
10246
- `${c4.dim}${machine.name} has no daemon \u2014 you can rename it here; update and project changes need its daemon installed.${c4.reset}`
10463
+ `${c5.dim}${machine.name} has no daemon \u2014 you can rename it here; update and project changes need its daemon installed.${c5.reset}`
10247
10464
  );
10248
10465
  } else if (!isSelf && machine.daemon && !online) {
10249
10466
  tui.print(
10250
- `${c4.dim}${machine.name}'s daemon is offline \u2014 queued changes apply when it next comes online.${c4.reset}`
10467
+ `${c5.dim}${machine.name}'s daemon is offline \u2014 queued changes apply when it next comes online.${c5.reset}`
10251
10468
  );
10252
10469
  }
10253
- const action = await tui.select(`${c4.bold}${machineIcon(machine)} ${machine.name}${c4.reset}`, options);
10470
+ const action = await tui.select(`${c5.bold}${machineIcon(machine)} ${machine.name}${c5.reset}`, options);
10254
10471
  if (!action || action === "back") return;
10255
10472
  if (action === "icon") {
10256
10473
  const current = machine.icon ?? "";
10257
10474
  const emoji = await tui.prompt(
10258
- `${c4.bold}Icon for ${machine.name}${c4.reset} ${c4.dim}(paste an emoji, blank to reset)${c4.reset}`,
10475
+ `${c5.bold}Icon for ${machine.name}${c5.reset} ${c5.dim}(paste an emoji, blank to reset)${c5.reset}`,
10259
10476
  current
10260
10477
  );
10261
10478
  if (emoji !== null) {
10262
10479
  const trimmed = emoji.trim();
10263
10480
  await setMachineIcon(api, machine.id, trimmed);
10264
10481
  machine.icon = trimmed || void 0;
10265
- tui.print(`${c4.green}\u2713${c4.reset} icon ${trimmed ? `set to ${trimmed}` : "reset"} for ${machine.name}`);
10482
+ tui.print(`${c5.green}\u2713${c5.reset} icon ${trimmed ? `set to ${trimmed}` : "reset"} for ${machine.name}`);
10266
10483
  }
10267
10484
  return manageMachine(tui, api, self, machine);
10268
10485
  }
@@ -10278,7 +10495,7 @@ async function manageMachine(tui, api, self, machine) {
10278
10495
  const name = await tui.prompt(`New name for ${machine.name}`, machine.name);
10279
10496
  if (name === null || !name.trim()) return;
10280
10497
  await setMachineName(api, machine.id, name.trim());
10281
- tui.print(`${c4.green}\u2713${c4.reset} Renamed ${c4.bold}${machine.name}${c4.reset} \u2192 ${c4.bold}${name.trim()}${c4.reset}.`);
10498
+ tui.print(`${c5.green}\u2713${c5.reset} Renamed ${c5.bold}${machine.name}${c5.reset} \u2192 ${c5.bold}${name.trim()}${c5.reset}.`);
10282
10499
  } else if (action === "update") {
10283
10500
  if (isSelf) {
10284
10501
  await runUpdateCommand(tui);
@@ -10289,7 +10506,7 @@ async function manageMachine(tui, api, self, machine) {
10289
10506
  ]);
10290
10507
  if (go !== "yes") return;
10291
10508
  await dispatch("update");
10292
- tui.print(`${c4.green}\u2713${c4.reset} Update ${c4.gray}${applyNote} (its daemon updates and restarts on the new version).${c4.reset}`);
10509
+ tui.print(`${c5.green}\u2713${c5.reset} Update ${c5.gray}${applyNote} (its daemon updates and restarts on the new version).${c5.reset}`);
10293
10510
  }
10294
10511
  } else if (action === "projects") {
10295
10512
  await manageMachineProjects(tui, api, self, machine, dispatch, applyNote);
@@ -10300,7 +10517,7 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
10300
10517
  const paths = Object.keys(machine.projects).sort();
10301
10518
  const projLabels = projectDisplayLabels(machine.projects);
10302
10519
  const picked = await tui.select(
10303
- `${c4.bold}Projects on ${machine.name}${c4.reset} ${c4.dim}(\u2191\u2193 \xB7 enter \xB7 esc)${c4.reset}`,
10520
+ `${c5.bold}Projects on ${machine.name}${c5.reset} ${c5.dim}(\u2191\u2193 \xB7 enter \xB7 esc)${c5.reset}`,
10304
10521
  [
10305
10522
  ...paths.map((p) => ({
10306
10523
  label: projLabels.get(p) ?? projectDisplayName(p, machine.projects[p]),
@@ -10321,12 +10538,12 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
10321
10538
  });
10322
10539
  if (!chosen) return;
10323
10540
  await dispatch("add_project", { path: chosen });
10324
- tui.print(`${c4.green}\u2713${c4.reset} Add ${chosen} ${c4.gray}${applyNote}.${c4.reset}`);
10541
+ tui.print(`${c5.green}\u2713${c5.reset} Add ${chosen} ${c5.gray}${applyNote}.${c5.reset}`);
10325
10542
  return;
10326
10543
  }
10327
10544
  const project = machine.projects[picked];
10328
10545
  const displayName = projectDisplayName(picked, project);
10329
- const action = await tui.select(`${c4.bold}${displayName}${c4.reset} ${c4.dim}${shortenPath(picked, 48)}${c4.reset}`, [
10546
+ const action = await tui.select(`${c5.bold}${displayName}${c5.reset} ${c5.dim}${shortenPath(picked, 48)}${c5.reset}`, [
10330
10547
  { label: "Rename", hint: "display name only \u2014 the directory is untouched", value: "rename" },
10331
10548
  { label: "Remove from this machine's projects", hint: "doesn't delete the directory", value: "remove" },
10332
10549
  { label: "Back", value: "back" }
@@ -10341,60 +10558,60 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
10341
10558
  await setProjectName(api, machine.id, picked, name);
10342
10559
  const now = name.trim() || projectDisplayName(picked, null);
10343
10560
  if (project) project.name = now;
10344
- tui.print(`${c4.green}\u2713${c4.reset} Renamed ${c4.bold}${displayName}${c4.reset} \u2192 ${c4.bold}${now}${c4.reset}.`);
10561
+ tui.print(`${c5.green}\u2713${c5.reset} Renamed ${c5.bold}${displayName}${c5.reset} \u2192 ${c5.bold}${now}${c5.reset}.`);
10345
10562
  } else {
10346
10563
  await dispatch("remove_project", { path: picked });
10347
- tui.print(`${c4.green}\u2713${c4.reset} Remove ${picked} ${c4.gray}${applyNote}.${c4.reset}`);
10564
+ tui.print(`${c5.green}\u2713${c5.reset} Remove ${picked} ${c5.gray}${applyNote}.${c5.reset}`);
10348
10565
  }
10349
10566
  }
10350
10567
  function showDaemonInfo(tui, session) {
10351
10568
  if (session.mode === "remote" && session.runner) {
10352
10569
  tui.print(
10353
- `${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}`
10570
+ `${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}`
10354
10571
  );
10355
10572
  } else {
10356
- tui.print(`${c4.gray}This session runs on this machine.${c4.reset}`);
10573
+ tui.print(`${c5.gray}This session runs on this machine.${c5.reset}`);
10357
10574
  }
10358
10575
  const status = serviceStatus();
10359
10576
  tui.print(
10360
- `${c4.gray}Daemon on this machine:${c4.reset} ${status.installed ? status.detail : "not installed"}`
10577
+ `${c5.gray}Daemon on this machine:${c5.reset} ${status.installed ? status.detail : "not installed"}`
10361
10578
  );
10362
10579
  if (!status.installed) {
10363
10580
  tui.print(
10364
- `${c4.gray}Install it to start sessions on this machine from anywhere:${c4.reset} ${c4.bold}standardcode daemon install${c4.reset}`
10581
+ `${c5.gray}Install it to start sessions on this machine from anywhere:${c5.reset} ${c5.bold}standardcode daemon install${c5.reset}`
10365
10582
  );
10366
10583
  tui.print(
10367
- `${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}`
10584
+ `${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}`
10368
10585
  );
10369
10586
  } else {
10370
- tui.print(`${c4.dim}Manage it with standardcode daemon status | uninstall | add-project <path>.${c4.reset}`);
10587
+ tui.print(`${c5.dim}Manage it with standardcode daemon status | uninstall | add-project <path>.${c5.reset}`);
10371
10588
  }
10372
10589
  }
10373
10590
  function showKeybindings(tui) {
10374
- tui.print(`${c4.gray}shortcuts:${c4.reset}`);
10375
- tui.print(`${c4.gray} shift-tab${c4.reset} cycle auto-accept level (1\u20135)`);
10376
- tui.print(`${c4.gray} shift-\u23CE${c4.reset} insert a newline (multiline input)`);
10377
- tui.print(`${c4.gray} option-\u23CE${c4.reset} steer now \u2014 stops the current step and plays your message`);
10378
- tui.print(`${c4.gray} /${c4.reset} open the command palette (type to filter)`);
10379
- tui.print(`${c4.gray} !cmd${c4.reset} run a shell command on the session's machine (e.g. !ls); !! to send a literal !`);
10380
- tui.print(`${c4.gray} ctrl-v${c4.reset} paste an image from the clipboard ([#Image 1])`);
10381
- tui.print(`${c4.gray} \u2191 / \u2193${c4.reset} cycle past messages (on the input's top line)`);
10382
- tui.print(`${c4.gray} \u2190${c4.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
10383
- tui.print(`${c4.gray} ctrl-c${c4.reset} quit`);
10591
+ tui.print(`${c5.gray}shortcuts:${c5.reset}`);
10592
+ tui.print(`${c5.gray} shift-tab${c5.reset} cycle auto-accept level (1\u20135)`);
10593
+ tui.print(`${c5.gray} shift-\u23CE${c5.reset} insert a newline (multiline input)`);
10594
+ tui.print(`${c5.gray} option-\u23CE${c5.reset} steer now \u2014 stops the current step and plays your message`);
10595
+ tui.print(`${c5.gray} /${c5.reset} open the command palette (type to filter)`);
10596
+ tui.print(`${c5.gray} !cmd${c5.reset} run a shell command on the session's machine (e.g. !ls); !! to send a literal !`);
10597
+ tui.print(`${c5.gray} ctrl-v${c5.reset} paste an image from the clipboard ([#Image 1])`);
10598
+ tui.print(`${c5.gray} \u2191 / \u2193${c5.reset} cycle past messages (on the input's top line)`);
10599
+ tui.print(`${c5.gray} \u2190${c5.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
10600
+ tui.print(`${c5.gray} ctrl-c${c5.reset} quit`);
10384
10601
  }
10385
10602
  async function runUpdateCommand(tui) {
10386
10603
  const version = readVersion();
10387
10604
  const result = await forceCheckForUpdate(version);
10388
10605
  if (!result) {
10389
- tui.print(`${c4.green}\u2713${c4.reset} ${c4.gray}@standardagents/code${c4.reset} is up to date (v${version})`);
10606
+ tui.print(`${c5.green}\u2713${c5.reset} ${c5.gray}@standardagents/code${c5.reset} is up to date (v${version})`);
10390
10607
  return;
10391
10608
  }
10392
10609
  const { latest } = result;
10393
10610
  tui.print(`
10394
- ${c4.yellow}\u27F3${c4.reset} Update available: ${c4.gray}v${version}${c4.reset} \u2192 ${c4.green}v${latest}${c4.reset}`);
10611
+ ${c5.yellow}\u27F3${c5.reset} Update available: ${c5.gray}v${version}${c5.reset} \u2192 ${c5.green}v${latest}${c5.reset}`);
10395
10612
  const pm = detectPackageManager();
10396
10613
  if (!pm) {
10397
- tui.print(` ${c4.gray}This is a source checkout \u2014 pull the repo to update.${c4.reset}`);
10614
+ tui.print(` ${c5.gray}This is a source checkout \u2014 pull the repo to update.${c5.reset}`);
10398
10615
  return;
10399
10616
  }
10400
10617
  const { display } = updateCommand(pm);
@@ -10403,28 +10620,28 @@ async function runUpdateCommand(tui) {
10403
10620
  { label: "No, skip", value: "no" }
10404
10621
  ]);
10405
10622
  if (choice === "yes") {
10406
- tui.print(` ${c4.gray}Running ${display}\u2026${c4.reset}`);
10623
+ tui.print(` ${c5.gray}Running ${display}\u2026${c5.reset}`);
10407
10624
  const { ok, output: pmOutput } = await runUpdate(pm);
10408
10625
  if (ok) {
10409
- tui.print(` ${c4.green}\u2713${c4.reset} Updated to v${latest}. Restart to use the new version.`);
10626
+ tui.print(` ${c5.green}\u2713${c5.reset} Updated to v${latest}. Restart to use the new version.`);
10410
10627
  } else {
10411
- tui.print(` ${c4.red}\u2717${c4.reset} Update failed:`);
10628
+ tui.print(` ${c5.red}\u2717${c5.reset} Update failed:`);
10412
10629
  for (const line of pmOutput.trim().split("\n").slice(-6)) {
10413
- tui.print(` ${c4.dim}${line}${c4.reset}`);
10630
+ tui.print(` ${c5.dim}${line}${c5.reset}`);
10414
10631
  }
10415
10632
  }
10416
10633
  } else {
10417
- tui.print(` ${c4.gray}Skipped. Run /update later.${c4.reset}`);
10634
+ tui.print(` ${c5.gray}Skipped. Run /update later.${c5.reset}`);
10418
10635
  }
10419
10636
  }
10420
10637
  async function runProcessMenu(tui, bg) {
10421
10638
  const procs = await bg.list();
10422
10639
  if (!procs.length) {
10423
- tui.print(`${c4.gray}No background processes for this session.${c4.reset}`);
10640
+ tui.print(`${c5.gray}No background processes for this session.${c5.reset}`);
10424
10641
  return;
10425
10642
  }
10426
10643
  const items = procs.map((p) => {
10427
- const status = p.status === "running" ? `${c4.green}running${c4.reset}` : `${c4.gray}${p.status}${typeof p.exitCode === "number" ? ` (exit ${p.exitCode})` : ""}${c4.reset}`;
10644
+ const status = p.status === "running" ? `${c5.green}running${c5.reset}` : `${c5.gray}${p.status}${typeof p.exitCode === "number" ? ` (exit ${p.exitCode})` : ""}${c5.reset}`;
10428
10645
  return {
10429
10646
  label: `${p.description || p.command}`,
10430
10647
  hint: `${p.id} \xB7 ${status}`,
@@ -10432,22 +10649,22 @@ async function runProcessMenu(tui, bg) {
10432
10649
  };
10433
10650
  });
10434
10651
  const picked = await tui.select(
10435
- `${c4.bold}Background processes${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter to manage \xB7 esc to close)${c4.reset}`,
10652
+ `${c5.bold}Background processes${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter to manage \xB7 esc to close)${c5.reset}`,
10436
10653
  items
10437
10654
  );
10438
10655
  if (!picked) return;
10439
10656
  const proc = procs.find((p) => p.id === picked);
10440
10657
  if (!proc || proc.status !== "running") {
10441
- tui.print(`${c4.gray}${picked} is not running.${c4.reset}`);
10658
+ tui.print(`${c5.gray}${picked} is not running.${c5.reset}`);
10442
10659
  return;
10443
10660
  }
10444
- const action = await tui.select(`${c4.bold}${proc.description || proc.command}${c4.reset}`, [
10661
+ const action = await tui.select(`${c5.bold}${proc.description || proc.command}${c5.reset}`, [
10445
10662
  { label: "Stop this process", value: "stop" },
10446
10663
  { label: "Leave it running", value: "leave" }
10447
10664
  ]);
10448
10665
  if (action === "stop") {
10449
10666
  await bg.stop(picked);
10450
- tui.print(`${c4.gray}stopped ${picked}${c4.reset}`);
10667
+ tui.print(`${c5.gray}stopped ${picked}${c5.reset}`);
10451
10668
  }
10452
10669
  }
10453
10670
  async function runApprovalsMenu(tui, perm, save) {
@@ -10455,7 +10672,7 @@ async function runApprovalsMenu(tui, perm, save) {
10455
10672
  const risks = Array.from(perm.allowRisk).sort((a, b) => a - b);
10456
10673
  if (!tools.length && !risks.length) {
10457
10674
  tui.print(
10458
- `${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}`
10675
+ `${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}`
10459
10676
  );
10460
10677
  return;
10461
10678
  }
@@ -10465,22 +10682,22 @@ async function runApprovalsMenu(tui, perm, save) {
10465
10682
  { label: "Clear all approvals", hint: "", value: "clear" }
10466
10683
  ];
10467
10684
  const picked = await tui.select(
10468
- `${c4.bold}Approved commands${c4.reset} ${c4.dim}(enter to revoke \xB7 esc to close)${c4.reset}`,
10685
+ `${c5.bold}Approved commands${c5.reset} ${c5.dim}(enter to revoke \xB7 esc to close)${c5.reset}`,
10469
10686
  items
10470
10687
  );
10471
10688
  if (!picked) return;
10472
10689
  if (picked === "clear") {
10473
10690
  perm.alwaysAllow.clear();
10474
10691
  perm.allowRisk.clear();
10475
- tui.print(`${c4.gray}cleared all approvals${c4.reset}`);
10692
+ tui.print(`${c5.gray}cleared all approvals${c5.reset}`);
10476
10693
  } else if (picked.startsWith("tool:")) {
10477
10694
  const t = picked.slice(5);
10478
10695
  perm.alwaysAllow.delete(t);
10479
- tui.print(`${c4.gray}revoked tool ${t}${c4.reset}`);
10696
+ tui.print(`${c5.gray}revoked tool ${t}${c5.reset}`);
10480
10697
  } else if (picked.startsWith("risk:")) {
10481
10698
  const r = Number(picked.slice(5));
10482
10699
  perm.allowRisk.delete(r);
10483
- tui.print(`${c4.gray}revoked level ${r}${c4.reset}`);
10700
+ tui.print(`${c5.gray}revoked level ${r}${c5.reset}`);
10484
10701
  }
10485
10702
  save();
10486
10703
  }
@@ -10498,7 +10715,7 @@ async function runMcpMenu(tui, mcp) {
10498
10715
  items.push({ label: "\uFF0B Install a new MCP server\u2026", hint: "find & install", value: INSTALL });
10499
10716
  items.push({ label: "Add manually (name: command)\u2026", hint: "advanced", value: ADD_MANUAL });
10500
10717
  const picked = await tui.select(
10501
- `${c4.bold}MCP servers${c4.reset} ${c4.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c4.reset}`,
10718
+ `${c5.bold}MCP servers${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c5.reset}`,
10502
10719
  items
10503
10720
  );
10504
10721
  if (!picked) return;
@@ -10512,7 +10729,7 @@ async function runMcpMenu(tui, mcp) {
10512
10729
  }
10513
10730
  const server = configured.find((s) => s.name === picked);
10514
10731
  const isConnected = connected.has(picked);
10515
- const action = await tui.select(`${c4.bold}${picked}${c4.reset}`, [
10732
+ const action = await tui.select(`${c5.bold}${picked}${c5.reset}`, [
10516
10733
  { label: "View tools", value: "tools" },
10517
10734
  isConnected ? { label: "Disconnect", value: "disconnect" } : { label: "Connect", value: "connect" },
10518
10735
  server.enabled ? { label: "Disable (don't auto-connect)", value: "disable" } : { label: "Enable (auto-connect on start)", value: "enable" },
@@ -10522,29 +10739,29 @@ async function runMcpMenu(tui, mcp) {
10522
10739
  if (action === "tools") {
10523
10740
  const entry = mcp.catalog().servers.find((e) => e.name === picked);
10524
10741
  if (!entry || entry.status !== "connected") {
10525
- tui.print(`${c4.gray}${picked} is not connected \u2014 connect it to list tools.${c4.reset}`);
10742
+ tui.print(`${c5.gray}${picked} is not connected \u2014 connect it to list tools.${c5.reset}`);
10526
10743
  return;
10527
10744
  }
10528
- if (!entry.tools.length) tui.print(`${c4.gray}${picked} exposes no tools.${c4.reset}`);
10529
- for (const t of entry.tools) tui.print(` ${c4.cyan}${t.name}${c4.reset}${t.description ? ` ${c4.gray}\u2014 ${t.description}${c4.reset}` : ""}`);
10530
- if (entry.resources.length) tui.print(` ${c4.gray}${entry.resources.length} resource(s)${c4.reset}`);
10745
+ if (!entry.tools.length) tui.print(`${c5.gray}${picked} exposes no tools.${c5.reset}`);
10746
+ for (const t of entry.tools) tui.print(` ${c5.cyan}${t.name}${c5.reset}${t.description ? ` ${c5.gray}\u2014 ${t.description}${c5.reset}` : ""}`);
10747
+ if (entry.resources.length) tui.print(` ${c5.gray}${entry.resources.length} resource(s)${c5.reset}`);
10531
10748
  } else if (action === "connect") {
10532
10749
  const res = await mcp.connect(server);
10533
- tui.print(res.ok ? `${c4.cyan}\u26A1 connected (${res.tools} tools)${c4.reset}` : `${c4.red}\u26A0 ${res.error}${c4.reset}`);
10750
+ tui.print(res.ok ? `${c5.cyan}\u26A1 connected (${res.tools} tools)${c5.reset}` : `${c5.red}\u26A0 ${res.error}${c5.reset}`);
10534
10751
  } else if (action === "disconnect") {
10535
10752
  mcp.disconnect(picked);
10536
- tui.print(`${c4.gray}disconnected ${picked}${c4.reset}`);
10753
+ tui.print(`${c5.gray}disconnected ${picked}${c5.reset}`);
10537
10754
  } else if (action === "enable") {
10538
10755
  mcp.setEnabled(picked, true);
10539
10756
  const res = await mcp.connect(server);
10540
- tui.print(res.ok ? `${c4.cyan}\u26A1 enabled + connected (${res.tools} tools)${c4.reset}` : `${c4.red}\u26A0 enabled but failed: ${res.error}${c4.reset}`);
10757
+ tui.print(res.ok ? `${c5.cyan}\u26A1 enabled + connected (${res.tools} tools)${c5.reset}` : `${c5.red}\u26A0 enabled but failed: ${res.error}${c5.reset}`);
10541
10758
  } else if (action === "disable") {
10542
10759
  mcp.setEnabled(picked, false);
10543
10760
  mcp.disconnect(picked);
10544
- tui.print(`${c4.gray}disabled + disconnected ${picked}${c4.reset}`);
10761
+ tui.print(`${c5.gray}disabled + disconnected ${picked}${c5.reset}`);
10545
10762
  } else if (action === "remove") {
10546
10763
  mcp.remove(picked);
10547
- tui.print(`${c4.gray}removed ${picked}${c4.reset}`);
10764
+ tui.print(`${c5.gray}removed ${picked}${c5.reset}`);
10548
10765
  }
10549
10766
  }
10550
10767
  async function addMcpServer(tui, mcp) {
@@ -10555,13 +10772,13 @@ async function addMcpServer(tui, mcp) {
10555
10772
  if (!spec) return;
10556
10773
  const cfg = parseServerSpec(spec);
10557
10774
  if (!cfg) {
10558
- tui.print(`${c4.yellow}couldn't parse that. Use name: command [args]${c4.reset}`);
10775
+ tui.print(`${c5.yellow}couldn't parse that. Use name: command [args]${c5.reset}`);
10559
10776
  return;
10560
10777
  }
10561
- tui.print(`${c4.gray}connecting MCP "${cfg.name}" (${cfg.command} ${cfg.args.join(" ")})\u2026${c4.reset}`);
10778
+ tui.print(`${c5.gray}connecting MCP "${cfg.name}" (${cfg.command} ${cfg.args.join(" ")})\u2026${c5.reset}`);
10562
10779
  const res = await mcp.add(cfg);
10563
- 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}`);
10564
- 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}`);
10780
+ 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}`);
10781
+ 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}`);
10565
10782
  }
10566
10783
  async function installMcpServerFlow(tui, mcp) {
10567
10784
  const query = await tui.prompt(