@8gi-foundation/8gent-code 0.20.15 → 0.20.16

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/tui.js CHANGED
@@ -18697,8 +18697,6 @@ var init_allowlist = __esm(() => {
18697
18697
  "node",
18698
18698
  "npm",
18699
18699
  "npx",
18700
- "pnpm",
18701
- "yarn",
18702
18700
  "deno",
18703
18701
  "python",
18704
18702
  "python3",
@@ -18706,9 +18704,6 @@ var init_allowlist = __esm(() => {
18706
18704
  "pip3",
18707
18705
  "uv",
18708
18706
  "git",
18709
- "go",
18710
- "cargo",
18711
- "rustc",
18712
18707
  "tsc",
18713
18708
  "gh",
18714
18709
  "docker",
@@ -18733,20 +18728,18 @@ var init_allowlist = __esm(() => {
18733
18728
  "biome",
18734
18729
  "eslint",
18735
18730
  "prettier",
18736
- "vite"
18731
+ "vite",
18732
+ "8gent"
18737
18733
  ]);
18738
18734
  SHORT_VERSION = {
18739
18735
  node: "-v",
18740
18736
  bun: "-v",
18741
18737
  npm: "-v",
18742
- pnpm: "-v",
18743
- yarn: "-v",
18744
18738
  deno: "-V",
18745
18739
  python: "-V",
18746
18740
  python3: "-V",
18747
18741
  ffmpeg: "-version",
18748
- ffprobe: "-version",
18749
- go: "version"
18742
+ ffprobe: "-version"
18750
18743
  };
18751
18744
  SECRET_ARG = /(^|\/)\.env(\.[\w.-]+)?$|(^|\/)\.env\b|id_rsa|id_ed25519|id_ecdsa|(^|\/)\.ssh(\/|$)|\.aws\/|credentials|\.netrc|\.npmrc|\.git-credentials|keychain|\.pem$|\.key$|\.p12$/;
18752
18745
  REDIR_RE2 = /(?<![<>&\d])(\d?|&)(>>?|<)(\|?)\s*(&\d+|[^\s;|&<>()]+)/g;
@@ -29981,7 +29974,7 @@ var init_permissions = __esm(() => {
29981
29974
  var require_package = __commonJS(function(exports2, module2) {
29982
29975
  module2.exports = {
29983
29976
  name: "@8gi-foundation/8gent-code",
29984
- version: "0.20.15",
29977
+ version: "0.20.16",
29985
29978
  private: false,
29986
29979
  engines: {
29987
29980
  node: ">=22.5"
@@ -74427,8 +74420,31 @@ function emptyReplyStall(toolCalls) {
74427
74420
  function isEmptyReplyStall(line) {
74428
74421
  return line.startsWith(EMPTY_REPLY_STALL_PREFIX);
74429
74422
  }
74430
- function emptyReplyNote(toolCalls) {
74431
- return `[harness] No reply: ${emptyReplyStall(toolCalls)}.`;
74423
+ function refusedCalls(log, max = 3) {
74424
+ const out = [];
74425
+ let total = 0;
74426
+ for (const e of log) {
74427
+ const m = /^\[[^\]]*\b(?:BLOCKED|DENIED)\b[^\]]*\]\s*([^\n]*)/.exec((e.result ?? "").trimStart());
74428
+ if (!m)
74429
+ continue;
74430
+ total++;
74431
+ if (out.length >= max)
74432
+ continue;
74433
+ const a = e.args;
74434
+ const first = typeof a?.command === "string" ? a.command.trim().split(/\s+/)[0] ?? "" : "";
74435
+ const bin = /^[A-Za-z][\w.+-]{0,40}$/.test(first) ? ` ${first}` : "";
74436
+ const why = /declin/i.test(m[1]) ? "declined" : "blocked by policy";
74437
+ out.push(`${e.name}${bin}: ${why}`);
74438
+ }
74439
+ return { total, shown: out };
74440
+ }
74441
+ function emptyReplyNote(toolCalls, log = []) {
74442
+ const base = `[harness] No reply: ${emptyReplyStall(toolCalls)}.`;
74443
+ const { total, shown } = refusedCalls(log);
74444
+ if (total === 0)
74445
+ return base;
74446
+ const more = total > shown.length ? ` (and ${total - shown.length} more)` : "";
74447
+ return `${base} ${total} of the calls were refused, so they did not run: ${shown.join("; ")}${more}.`;
74432
74448
  }
74433
74449
  function isEmptyReplyNote(reply) {
74434
74450
  return reply.startsWith("[harness] No reply:");
@@ -74521,7 +74537,7 @@ ${summary}`;
74521
74537
  if (unverified.length === 0)
74522
74538
  return { content, rounds, toolLog, unverified: [] };
74523
74539
  const notes = [
74524
- empty ? emptyReplyNote(toolLog.length) : content.trimEnd(),
74540
+ empty ? emptyReplyNote(toolLog.length, toolLog) : content.trimEnd(),
74525
74541
  unfulfilled.length > 0 ? formatHarnessNote(unfulfilled) : ""
74526
74542
  ].filter((n) => n.trim() !== "");
74527
74543
  return { content: notes.join(`
@@ -557382,6 +557398,44 @@ var init_registry = __esm(() => {
557382
557398
  ];
557383
557399
  });
557384
557400
 
557401
+ // packages/eight/small-talk.ts
557402
+ function isSmallTalk(text) {
557403
+ const flat = text.toLowerCase().replace(/^\[mode: [^\]]*\]\s*/, "").replace(/['\u2019]/g, "").replace(/[^a-z0-9 ]+/g, " ").replace(/\s+/g, " ").trim();
557404
+ return flat.length > 0 && flat.length <= 40 && GREETING.test(flat);
557405
+ }
557406
+ var CORE, GREETING, SMALL_TALK_NOTE = "[SMALL TALK] The person said hello and asked for nothing. Reply in one or two plain sentences and ask what they want to work on. No tools this turn: do not look around, list files or describe their folders.";
557407
+ var init_small_talk = __esm(() => {
557408
+ CORE = [
557409
+ "h+i+",
557410
+ "h+e+y+",
557411
+ "h+e+l+o+",
557412
+ "hiya",
557413
+ "howdy",
557414
+ "y+o+",
557415
+ "sup",
557416
+ "wa+s+s?u+p+",
557417
+ "w?h?a+t?s? ?u+p+",
557418
+ "good (?:morning|afternoon|evening|night)",
557419
+ "gm",
557420
+ "gn",
557421
+ "morning",
557422
+ "evening",
557423
+ "how are you(?: doing| today)?",
557424
+ "how are things",
557425
+ "hows it going",
557426
+ "how you doing",
557427
+ "you there",
557428
+ "anyone there",
557429
+ "ping",
557430
+ "thanks(?: a lot| so much)?",
557431
+ "thank you(?: so much)?",
557432
+ "thx",
557433
+ "ty",
557434
+ "cheers"
557435
+ ].join("|");
557436
+ GREETING = new RegExp(`^(?:(?:hi+|hey+|hello+|yo+|hiya|howdy) )?(?:${CORE})(?: (?:there|again|everyone|all|team|mate|man|bro|friend|8gent|eight))?$`);
557437
+ });
557438
+
557385
557439
  // packages/eight/verify-gate.ts
557386
557440
  function isVerifyGateEnabled(env = process.env) {
557387
557441
  const v = (env.EIGHT_VERIFY_GATE ?? "").trim().toLowerCase();
@@ -558312,8 +558366,8 @@ __export(exports_system_prompt, {
558312
558366
  styleHasGuide: () => styleHasGuide,
558313
558367
  workingProjectNote: () => workingProjectNote
558314
558368
  });
558315
- import { readFileSync as readFileSync96 } from "fs";
558316
- import { basename as basename51, join as join154 } from "path";
558369
+ import { existsSync as existsSync105, readFileSync as readFileSync96 } from "fs";
558370
+ import { basename as basename51, join as join154, resolve as resolve73 } from "path";
558317
558371
  function buildBoardContextSegment(path = BOARD_CONTEXT_PATH) {
558318
558372
  let text;
558319
558373
  try {
@@ -558344,8 +558398,20 @@ function communicationStyleLine(style) {
558344
558398
  function styleHasGuide(style) {
558345
558399
  return isCommunicationStyle(style) && STYLE_GUIDE[style] !== "";
558346
558400
  }
558401
+ function isPlainHome(dir, home = resolveHome()) {
558402
+ try {
558403
+ return home !== "" && resolve73(dir) === resolve73(home) && !existsSync105(join154(dir, ".git"));
558404
+ } catch {
558405
+ return false;
558406
+ }
558407
+ }
558347
558408
  function workingProjectNote(dir) {
558348
558409
  const name = basename51(dir) || dir;
558410
+ if (isPlainHome(dir)) {
558411
+ return `
558412
+
558413
+ **Working folder: \`${dir}\` is the person's home folder, not a project. Do not list, search or summarise it unless they ask. Ask what they want to work on.**`;
558414
+ }
558349
558415
  if (name === "8gent-code") {
558350
558416
  return `
558351
558417
 
@@ -569507,14 +569573,15 @@ ${LOCAL_LOGIN_NOTE}`;
569507
569573
  instructions,
569508
569574
  localCoreTools,
569509
569575
  chatStartTime,
569510
- textForAgent
569576
+ textForAgent,
569577
+ smallTalk = false
569511
569578
  } = opts;
569512
569579
  const signal = this.armController().signal;
569513
569580
  const allow = new Set(localCoreTools);
569514
569581
  const specs = toolDefsToSpecs(this.executor.getToolDefinitions(), allow);
569515
569582
  let stepNumber = 0;
569516
569583
  let breakerStop = null;
569517
- if (this.extensionToolsAllowed()) {
569584
+ if (this.extensionToolsAllowed() && !smallTalk) {
569518
569585
  for (const t of this.extensionHost.listTools()) {
569519
569586
  if (this.modeAllows(t.name)) {
569520
569587
  specs.push({ name: t.name, description: t.description, parameters: t.parameters });
@@ -570092,6 +570159,10 @@ ${LOCAL_LOGIN_NOTE}`;
570092
570159
  this.turnGitBaseline = asksForCommit(userMessage) ? gitSnapshot(this.config.workingDirectory || process.cwd()) : null;
570093
570160
  this.executor.beginTurn(userMessage);
570094
570161
  const textForAgent = userMessage.trim() || (imageBase64 ? "The user attached an image with no text. Describe what you see and help with anything relevant in the image." : userMessage);
570162
+ const smallTalkTurn = isSmallTalk(textForAgent) && !imageBase64 && this.config.agentScope !== "__table__";
570163
+ if (smallTalkTurn) {
570164
+ this.messageHistory.push({ role: "user", content: harnessNote(SMALL_TALK_NOTE) });
570165
+ }
570095
570166
  let visionId = null;
570096
570167
  let visionFailed = false;
570097
570168
  if (imageBase64 && await this.promptImageSeen()) {
@@ -570162,7 +570233,7 @@ ${result.description.slice(0, 200)}${result.description.length > 200 ? "..." : "
570162
570233
  }
570163
570234
  }
570164
570235
  const quickPrompt = quickLaneEnabled() && classifyPrompt(textForAgent) === "quick";
570165
- if (needsClarification(textForAgent) && !imageBase64 && !quickPrompt && !isNoToolScope(this.config.agentScope)) {
570236
+ if (needsClarification(textForAgent) && !imageBase64 && !quickPrompt && !smallTalkTurn && !isNoToolScope(this.config.agentScope)) {
570166
570237
  this.proactiveGatherer = createGatherer(textForAgent);
570167
570238
  const question = this.proactiveGatherer.getCurrentQuestion();
570168
570239
  if (question) {
@@ -570184,7 +570255,7 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
570184
570255
  this.kanban.moveTask(this.currentBmadTask.id, "ready");
570185
570256
  this.kanban.moveTask(this.currentBmadTask.id, "in_progress");
570186
570257
  }
570187
- if (!this.proactiveGatherer && !quickPrompt) {
570258
+ if (!this.proactiveGatherer && !quickPrompt && !smallTalkTurn) {
570188
570259
  await this.tryRunPreToolRouter(textForAgent);
570189
570260
  }
570190
570261
  const needsPlanningGate2 = !isVerbatimScope(this.config.agentScope) && needsPlanningGate(textForAgent);
@@ -570329,9 +570400,9 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
570329
570400
  "search_symbols",
570330
570401
  "recall"
570331
570402
  ]);
570332
- const scopedTools = isNoToolScope(this.config.agentScope) ? {} : this.config.agentScope === "__table__" ? Object.fromEntries(Object.entries(providerTools).filter(([k]) => TABLE_SESSION_TOOLS.has(k))) : providerTools;
570403
+ const scopedTools = isNoToolScope(this.config.agentScope) || smallTalkTurn ? {} : this.config.agentScope === "__table__" ? Object.fromEntries(Object.entries(providerTools).filter(([k]) => TABLE_SESSION_TOOLS.has(k))) : providerTools;
570333
570404
  const withExtensions = { ...scopedTools };
570334
- if (this.extensionToolsAllowed()) {
570405
+ if (this.extensionToolsAllowed() && !smallTalkTurn) {
570335
570406
  for (const t of this.extensionHost.listTools()) {
570336
570407
  if (t.name in withExtensions)
570337
570408
  continue;
@@ -570343,7 +570414,7 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
570343
570414
  }
570344
570415
  }
570345
570416
  const effectiveTools = this.modeFilter(withExtensions);
570346
- const textToolAllowlist = (isNoToolScope(this.config.agentScope) ? [] : this.config.agentScope === "__table__" ? [...TABLE_SESSION_TOOLS] : localCoreTools).filter((name) => getModeManager().isToolAllowed(name));
570417
+ const textToolAllowlist = (isNoToolScope(this.config.agentScope) || smallTalkTurn ? [] : this.config.agentScope === "__table__" ? [...TABLE_SESSION_TOOLS] : localCoreTools).filter((name) => getModeManager().isToolAllowed(name));
570347
570418
  const harness = this.harnessProfileFor(providerConfig.model, isLocalProvider);
570348
570419
  Object.assign(this.runtimeParams, {
570349
570420
  voiceChatActive: getRuntimeParams().voiceChatActive,
@@ -570368,7 +570439,8 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
570368
570439
  `),
570369
570440
  localCoreTools: textToolAllowlist,
570370
570441
  chatStartTime,
570371
- textForAgent
570442
+ textForAgent,
570443
+ smallTalk: smallTalkTurn
570372
570444
  });
570373
570445
  await this.readServerContextWindow();
570374
570446
  await this.compactIfNeeded(providerConfig);
@@ -571053,7 +571125,7 @@ ${summary || " (no provider errors recorded)"}`);
571053
571125
  };
571054
571126
  }
571055
571127
  const blankNative = gatedNative.content.trim() === "";
571056
- const content = blankNative ? emptyReplyNote(this.turnToolLedger.length) : gatedNative.content;
571128
+ const content = blankNative ? emptyReplyNote(this.turnToolLedger.length, this.turnToolLedger) : gatedNative.content;
571057
571129
  const flavor = voice.getFlavor("complete");
571058
571130
  const flavoredContent = fixCompletedLine(gatedNative.violated || isVerbatimScope(this.config.agentScope) ? content : flavorResponse(content, flavor)).text;
571059
571131
  this.messageHistory.push({ role: "assistant", content: flavoredContent });
@@ -571768,6 +571840,7 @@ var init_agent2 = __esm(() => {
571768
571840
  init_modes();
571769
571841
  init_planning_gate();
571770
571842
  init_registry();
571843
+ init_small_talk();
571771
571844
  init_verify_gate();
571772
571845
  init_local_tool_scope();
571773
571846
  init_local_turn_error();
@@ -572263,7 +572336,7 @@ var init_install = __esm(() => {
572263
572336
 
572264
572337
  // packages/eight/source-update.ts
572265
572338
  import { execFile as execFile7 } from "child_process";
572266
- import { existsSync as existsSync109, realpathSync as realpathSync36, statSync as statSync36 } from "fs";
572339
+ import { existsSync as existsSync110, realpathSync as realpathSync36, statSync as statSync36 } from "fs";
572267
572340
  import { homedir as homedir84 } from "os";
572268
572341
  import * as nodePath from "path";
572269
572342
  function scrubbedEnv(base) {
@@ -572362,7 +572435,7 @@ function modulePathToFs(modulePath) {
572362
572435
  }
572363
572436
  return s;
572364
572437
  }
572365
- function findCheckoutRoot(modulePath, platform, hasGit = (d) => existsSync109(lib(platform).join(d, ".git"))) {
572438
+ function findCheckoutRoot(modulePath, platform, hasGit = (d) => existsSync110(lib(platform).join(d, ".git"))) {
572366
572439
  const p = lib(platform);
572367
572440
  const fsPath = modulePathToFs(modulePath);
572368
572441
  let dir = p.dirname(platform === "win32" ? fsPath.replace(/\//g, "\\") : fsPath);
@@ -572378,7 +572451,7 @@ function findCheckoutRoot(modulePath, platform, hasGit = (d) => existsSync109(li
572378
572451
  }
572379
572452
  function findBrowserCheckout(o) {
572380
572453
  const p = lib(o.platform);
572381
- const hasGit = o.hasGit ?? ((d) => existsSync109(p.join(d, ".git")));
572454
+ const hasGit = o.hasGit ?? ((d) => existsSync110(p.join(d, ".git")));
572382
572455
  const looked = [];
572383
572456
  let note;
572384
572457
  const cands = [];
@@ -572704,7 +572777,7 @@ async function runSourceUpdate(deps, opts, report) {
572704
572777
  git,
572705
572778
  bun: bun ?? "bun",
572706
572779
  platform,
572707
- fileExists: deps.fileExists ?? ((f) => existsSync109(f)),
572780
+ fileExists: deps.fileExists ?? ((f) => existsSync110(f)),
572708
572781
  now: deps.now ?? (() => new Date),
572709
572782
  baseEnv: env,
572710
572783
  forgeHost: env.EIGHT_FORGE_HOST,
@@ -573150,7 +573223,7 @@ var init_update_notice = __esm(() => {
573150
573223
  });
573151
573224
 
573152
573225
  // packages/voice/cloud-transcriber.ts
573153
- import { existsSync as existsSync110 } from "fs";
573226
+ import { existsSync as existsSync111 } from "fs";
573154
573227
  async function transcribeCloud(wavPath, options) {
573155
573228
  const {
573156
573229
  apiKey,
@@ -573162,7 +573235,7 @@ async function transcribeCloud(wavPath, options) {
573162
573235
  if (!apiKey) {
573163
573236
  throw new Error("OpenAI API key is required for cloud transcription. Set OPENAI_API_KEY environment variable.");
573164
573237
  }
573165
- if (!existsSync110(wavPath)) {
573238
+ if (!existsSync111(wavPath)) {
573166
573239
  throw new Error(`WAV file not found: ${wavPath}`);
573167
573240
  }
573168
573241
  const startTime = Date.now();
@@ -573266,11 +573339,11 @@ var init_types5 = __esm(() => {
573266
573339
 
573267
573340
  // packages/voice/model-manager.ts
573268
573341
  import { EventEmitter as EventEmitter15 } from "events";
573269
- import { existsSync as existsSync111, mkdirSync as mkdirSync80, realpathSync as realpathSync37, unlinkSync as unlinkSync3 } from "fs";
573342
+ import { existsSync as existsSync112, mkdirSync as mkdirSync80, realpathSync as realpathSync37, unlinkSync as unlinkSync3 } from "fs";
573270
573343
  import { homedir as homedir85 } from "os";
573271
573344
  import { join as join168, sep as sep30 } from "path";
573272
573345
  function resolveWhisperModel(opts = {}) {
573273
- const exists = opts.exists ?? existsSync111;
573346
+ const exists = opts.exists ?? existsSync112;
573274
573347
  const dirs = [
573275
573348
  resolveModelsPath(opts.modelsDir ?? "~/.8gent/models/whisper"),
573276
573349
  ...opts.extraDirs ?? []
@@ -573331,7 +573404,7 @@ var init_model_manager = __esm(() => {
573331
573404
  this.modelsDir = resolveModelsPath(modelsPath);
573332
573405
  }
573333
573406
  ensureModelsDir() {
573334
- if (!existsSync111(this.modelsDir)) {
573407
+ if (!existsSync112(this.modelsDir)) {
573335
573408
  mkdirSync80(this.modelsDir, { recursive: true });
573336
573409
  }
573337
573410
  }
@@ -573345,7 +573418,7 @@ var init_model_manager = __esm(() => {
573345
573418
  return join168(this.modelsDir, WHISPER_MODELS[name].filename);
573346
573419
  }
573347
573420
  isModelDownloaded(name) {
573348
- return existsSync111(this.getModelPath(name));
573421
+ return existsSync112(this.getModelPath(name));
573349
573422
  }
573350
573423
  listModels() {
573351
573424
  return Object.keys(WHISPER_MODELS).map((name) => ({
@@ -573360,7 +573433,7 @@ var init_model_manager = __esm(() => {
573360
573433
  async downloadModel(name) {
573361
573434
  const info = WHISPER_MODELS[name];
573362
573435
  const destPath = this.getModelPath(name);
573363
- if (existsSync111(destPath)) {
573436
+ if (existsSync112(destPath)) {
573364
573437
  return destPath;
573365
573438
  }
573366
573439
  this.ensureModelsDir();
@@ -573411,7 +573484,7 @@ var init_model_manager = __esm(() => {
573411
573484
  this.emit("download-complete", { model: name, path: destPath });
573412
573485
  return destPath;
573413
573486
  } catch (err) {
573414
- if (existsSync111(destPath)) {
573487
+ if (existsSync112(destPath)) {
573415
573488
  try {
573416
573489
  unlinkSync3(destPath);
573417
573490
  } catch {}
@@ -573423,7 +573496,7 @@ var init_model_manager = __esm(() => {
573423
573496
  }
573424
573497
  deleteModel(name) {
573425
573498
  const path = this.getModelPath(name);
573426
- if (existsSync111(path)) {
573499
+ if (existsSync112(path)) {
573427
573500
  unlinkSync3(path);
573428
573501
  return true;
573429
573502
  }
@@ -573434,7 +573507,7 @@ var init_model_manager = __esm(() => {
573434
573507
  let totalBytes = 0;
573435
573508
  for (const name of Object.keys(WHISPER_MODELS)) {
573436
573509
  const path = this.getModelPath(name);
573437
- if (existsSync111(path)) {
573510
+ if (existsSync112(path)) {
573438
573511
  try {
573439
573512
  const stat = Bun.file(path);
573440
573513
  models[name] = stat.size;
@@ -573499,7 +573572,7 @@ var init_termux = __esm(() => {
573499
573572
 
573500
573573
  // packages/voice/recorder.ts
573501
573574
  import { EventEmitter as EventEmitter16 } from "events";
573502
- import { closeSync as closeSync21, existsSync as existsSync112, openSync as openSync21, readSync as readSync13, statSync as statSync37, unlinkSync as unlinkSync4 } from "fs";
573575
+ import { closeSync as closeSync21, existsSync as existsSync113, openSync as openSync21, readSync as readSync13, statSync as statSync37, unlinkSync as unlinkSync4 } from "fs";
573503
573576
  import { tmpdir as tmpdir20 } from "os";
573504
573577
  import { join as join170 } from "path";
573505
573578
  var {spawn: spawn26 } = globalThis.Bun;
@@ -573701,7 +573774,7 @@ var init_recorder = __esm(() => {
573701
573774
  }
573702
573775
  static cleanupFile(path) {
573703
573776
  try {
573704
- if (existsSync112(path)) {
573777
+ if (existsSync113(path)) {
573705
573778
  unlinkSync4(path);
573706
573779
  }
573707
573780
  } catch {}
@@ -573721,7 +573794,7 @@ var init_recorder = __esm(() => {
573721
573794
  });
573722
573795
 
573723
573796
  // packages/voice/transcriber.ts
573724
- import { existsSync as existsSync113 } from "fs";
573797
+ import { existsSync as existsSync114 } from "fs";
573725
573798
  import { arch as arch2, homedir as homedir86, platform as platform9 } from "os";
573726
573799
  import { join as join171 } from "path";
573727
573800
  var {spawn: spawn27 } = globalThis.Bun;
@@ -573730,7 +573803,7 @@ function getWhisperBinaryPath() {
573730
573803
  }
573731
573804
  async function findWhisperBinary() {
573732
573805
  const managedPath = getWhisperBinaryPath();
573733
- if (existsSync113(managedPath)) {
573806
+ if (existsSync114(managedPath)) {
573734
573807
  return managedPath;
573735
573808
  }
573736
573809
  const names = ["whisper-cpp", "whisper-cli", "whisper", "main"];
@@ -573745,7 +573818,7 @@ async function findWhisperBinary() {
573745
573818
  } catch {}
573746
573819
  }
573747
573820
  for (const brewName of ["/opt/homebrew/bin/whisper-cpp", "/opt/homebrew/bin/whisper-cli"]) {
573748
- if (existsSync113(brewName)) {
573821
+ if (existsSync114(brewName)) {
573749
573822
  return brewName;
573750
573823
  }
573751
573824
  }
@@ -573753,13 +573826,13 @@ async function findWhisperBinary() {
573753
573826
  }
573754
573827
  async function transcribeLocal(wavPath, options) {
573755
573828
  const { binaryPath, modelPath, language = "en", timeoutMs = 30000, threads } = options;
573756
- if (!existsSync113(wavPath)) {
573829
+ if (!existsSync114(wavPath)) {
573757
573830
  throw new Error(`WAV file not found: ${wavPath}`);
573758
573831
  }
573759
- if (!existsSync113(binaryPath)) {
573832
+ if (!existsSync114(binaryPath)) {
573760
573833
  throw new Error(`Whisper binary not found: ${binaryPath}`);
573761
573834
  }
573762
- if (!existsSync113(modelPath)) {
573835
+ if (!existsSync114(modelPath)) {
573763
573836
  throw new Error(`Model file not found: ${modelPath}`);
573764
573837
  }
573765
573838
  const startTime = Date.now();
@@ -574035,7 +574108,7 @@ var init_neudecide = __esm(() => {
574035
574108
 
574036
574109
  // packages/runtime/node-runtime.ts
574037
574110
  import { execFile as execFile8 } from "child_process";
574038
- import { existsSync as existsSync118 } from "fs";
574111
+ import { existsSync as existsSync119 } from "fs";
574039
574112
  import { homedir as homedir88 } from "os";
574040
574113
  import { join as join178 } from "path";
574041
574114
  import { promisify as promisify6 } from "util";
@@ -574098,7 +574171,7 @@ async function isRuntimeReady(opts) {
574098
574171
  version: opts.version,
574099
574172
  platform
574100
574173
  });
574101
- if (!existsSync118(bin))
574174
+ if (!existsSync119(bin))
574102
574175
  return false;
574103
574176
  const min = opts.minVersion ?? opts.version;
574104
574177
  try {
@@ -574136,7 +574209,7 @@ var init_node_runtime = __esm(() => {
574136
574209
  import { spawn as spawn29 } from "child_process";
574137
574210
  import {
574138
574211
  createWriteStream,
574139
- existsSync as existsSync119,
574212
+ existsSync as existsSync120,
574140
574213
  mkdirSync as mkdirSync83,
574141
574214
  mkdtempSync as mkdtempSync12,
574142
574215
  readdirSync as readdirSync27,
@@ -574197,7 +574270,7 @@ async function installRuntime(input) {
574197
574270
  throw new Error(`could not find node-* dir inside ${extractRoot}`);
574198
574271
  }
574199
574272
  const extractedPath = join179(extractRoot, nodeDir);
574200
- if (existsSync119(plan.runtimeDir)) {
574273
+ if (existsSync120(plan.runtimeDir)) {
574201
574274
  rmSync17(plan.runtimeDir, { recursive: true, force: true });
574202
574275
  }
574203
574276
  renameSync17(extractedPath, plan.runtimeDir);
@@ -576179,7 +576252,7 @@ Nothing was changed. Type /update again to apply.`);
576179
576252
  // apps/tui/src/lib/telegram-setup.ts
576180
576253
  import {
576181
576254
  chmodSync as chmodSync6,
576182
- existsSync as existsSync134,
576255
+ existsSync as existsSync135,
576183
576256
  lstatSync as lstatSync31,
576184
576257
  mkdirSync as mkdirSync96,
576185
576258
  readFileSync as readFileSync120,
@@ -576301,7 +576374,7 @@ async function getSeenChats(key) {
576301
576374
  function mergeSettings(chatIds, envFile, tokenVar) {
576302
576375
  const path = join200(homeDir4(), ".8gent", "settings.json");
576303
576376
  let settings = {};
576304
- if (existsSync134(path)) {
576377
+ if (existsSync135(path)) {
576305
576378
  try {
576306
576379
  const parsed = JSON.parse(readFileSync120(path, "utf8"));
576307
576380
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
@@ -576668,7 +576741,7 @@ Personal 8gent mini-app. Iteration is driven by \`SKILL.md\`. Run tests with \`b
576668
576741
  }
576669
576742
 
576670
576743
  // packages/app-creator/creator.ts
576671
- import { existsSync as existsSync135, mkdirSync as mkdirSync98, writeFileSync as writeFileSync78 } from "fs";
576744
+ import { existsSync as existsSync136, mkdirSync as mkdirSync98, writeFileSync as writeFileSync78 } from "fs";
576672
576745
  import { homedir as homedir96 } from "os";
576673
576746
  import { dirname as dirname83, join as join202 } from "path";
576674
576747
  function defaultAppsRoot() {
@@ -576726,7 +576799,7 @@ function createApp(input) {
576726
576799
  return { ...draft, persisted: false, written: [] };
576727
576800
  }
576728
576801
  const dir = draft.dir;
576729
- const targetExists = existsSync135(dir);
576802
+ const targetExists = existsSync136(dir);
576730
576803
  if (targetExists && !input.allowOverwrite) {
576731
576804
  return {
576732
576805
  ...draft,
@@ -577573,7 +577646,7 @@ var init_producer = __esm(() => {
577573
577646
  });
577574
577647
 
577575
577648
  // packages/pet/companion.ts
577576
- import { existsSync as existsSync139, mkdirSync as mkdirSync104, readFileSync as readFileSync122, writeFileSync as writeFileSync80 } from "fs";
577649
+ import { existsSync as existsSync140, mkdirSync as mkdirSync104, readFileSync as readFileSync122, writeFileSync as writeFileSync80 } from "fs";
577577
577650
  import { join as join204 } from "path";
577578
577651
  function seedRng(seed) {
577579
577652
  let h = 0;
@@ -577717,7 +577790,7 @@ function getDeckPath() {
577717
577790
  function loadDeck() {
577718
577791
  const deckPath = getDeckPath();
577719
577792
  try {
577720
- if (existsSync139(deckPath)) {
577793
+ if (existsSync140(deckPath)) {
577721
577794
  return JSON.parse(readFileSync122(deckPath, "utf-8"));
577722
577795
  }
577723
577796
  } catch {}
@@ -598971,234 +599044,6 @@ function useImageInput(options = {}) {
598971
599044
  // apps/tui/src/components/message-list.tsx
598972
599045
  var import_react63 = __toESM(require_react(), 1);
598973
599046
 
598974
- // apps/tui/src/lib/chat-layout.ts
598975
- var SHELL_CHROME = 4;
598976
- var COLUMN_GAP = 1;
598977
- var ACTIVITY_RAIL_WIDTH = 34;
598978
- function chatColumnWidth(cols, shell, min = 24) {
598979
- let width = cols - SHELL_CHROME;
598980
- if (shell.planWidth && shell.planWidth > 0)
598981
- width -= shell.planWidth + COLUMN_GAP;
598982
- if (shell.activity)
598983
- width -= ACTIVITY_RAIL_WIDTH + COLUMN_GAP;
598984
- return Math.max(min, width);
598985
- }
598986
- var USER_BUBBLE_SHARE = 0.78;
598987
- function bubbleWidths(contentWidth, role) {
598988
- const inner = Math.max(16, contentWidth - 2);
598989
- const bubble = role === "user" ? Math.max(16, Math.floor(inner * USER_BUBBLE_SHARE)) : inner;
598990
- return { bubble, wrap: Math.max(8, bubble - 2) };
598991
- }
598992
-
598993
- // apps/tui/src/lib/tool-trail.ts
598994
- var COLLAPSE_THRESHOLD = 8;
598995
- var MAX_TRAIL_ROWS = 12;
598996
- var PATH_KEYS = ["path", "file_path", "filePath", "file", "directory", "dir"];
598997
- var COMMAND_KEYS = ["command", "cmd"];
598998
- var QUERY_KEYS = ["query", "pattern", "q", "search", "url"];
598999
- function oneLine(s) {
599000
- return s.replace(/\s+/g, " ").trim().slice(0, 200);
599001
- }
599002
- function pick3(args, keys) {
599003
- for (const k of keys) {
599004
- const v = args[k];
599005
- if (typeof v === "string" && v.trim())
599006
- return oneLine(v);
599007
- }
599008
- return null;
599009
- }
599010
- function summarizeToolArgs(tool, args) {
599011
- if (!args || typeof args !== "object")
599012
- return "";
599013
- const isCommand = /command|shell|exec|bash/.test(tool);
599014
- const order = isCommand ? [COMMAND_KEYS, PATH_KEYS, QUERY_KEYS] : /search|grep|glob|find|fetch/.test(tool) ? [QUERY_KEYS, PATH_KEYS, COMMAND_KEYS] : [PATH_KEYS, COMMAND_KEYS, QUERY_KEYS];
599015
- for (const keys of order) {
599016
- const hit = pick3(args, keys);
599017
- if (hit)
599018
- return hit;
599019
- }
599020
- for (const v of Object.values(args)) {
599021
- if (typeof v === "string" && v.trim())
599022
- return oneLine(v);
599023
- }
599024
- return "";
599025
- }
599026
- var GATE_PREFIX = /^\s*\[([A-Z0-9_ -]*\b(?:BLOCKED|DENIED)\b[A-Z0-9_ -]*)\]/;
599027
- var OTHER_BLOCK = /^\s*(?:Path blocked by path-guard|Path traversal blocked)|Hook blocked tool/;
599028
- var RULE_NAME = /\[([a-z0-9]+(?:-[a-z0-9]+)+)\]/;
599029
- function classifyToolResult(success, preview) {
599030
- const text = preview ?? "";
599031
- const gate = text.match(GATE_PREFIX);
599032
- if (gate || OTHER_BLOCK.test(text)) {
599033
- if (gate && /DENIED/.test(gate[1]))
599034
- return { status: "blocked", reason: "denied" };
599035
- const rule = text.match(RULE_NAME);
599036
- return { status: "blocked", reason: rule ? `blocked: [${rule[1]}]` : "blocked" };
599037
- }
599038
- const exit = text.match(/^\s*Exit code (-?\d+)/);
599039
- if (exit && exit[1] !== "0")
599040
- return { status: "fail", reason: `exit ${exit[1]}` };
599041
- if (!success || /^\s*(?:Error\b|Unknown tool:)/.test(text)) {
599042
- const first = (text.split(`
599043
- `).find((l) => l.trim()) ?? "").replace(/^\s*Error(?: running tool "[^"]*")?:?\s*/, "").trim();
599044
- if (!first)
599045
- return { status: "fail", reason: "error" };
599046
- return { status: "fail", reason: first.length > 40 ? `${first.slice(0, 39)}\u2026` : first };
599047
- }
599048
- return { status: "ok" };
599049
- }
599050
- function toTrailEntry(event) {
599051
- const { status, reason } = classifyToolResult(event.success !== false, event.resultPreview);
599052
- const entry = {
599053
- tool: event.toolName,
599054
- summary: summarizeToolArgs(event.toolName, event.args),
599055
- status
599056
- };
599057
- if (reason)
599058
- entry.reason = reason;
599059
- if (event.toolName === "update_plan") {
599060
- const items = event.args?.plan;
599061
- if (Array.isArray(items)) {
599062
- const plan = items.map((i) => i && typeof i === "object" ? i : {}).filter((i) => typeof i.step === "string" && i.step.trim() !== "").map((i) => ({ step: i.step, status: typeof i.status === "string" ? i.status : "pending" }));
599063
- if (plan.length > 0)
599064
- entry.plan = plan;
599065
- }
599066
- }
599067
- return entry;
599068
- }
599069
- function isReadTool(tool) {
599070
- return /^(?:read_|list_|get_|search|grep|glob|find|view|web_search|web_fetch)/.test(tool);
599071
- }
599072
- function commonDir(paths) {
599073
- const split = paths.map((p) => p.split("/").slice(0, -1));
599074
- const first = split[0] ?? [];
599075
- let n = 0;
599076
- while (n < first.length && split.every((s) => s[n] === first[n]))
599077
- n++;
599078
- return n > 0 ? `${first.slice(0, n).join("/")}/` : "";
599079
- }
599080
- function collapseTrail(entries, maxRows = MAX_TRAIL_ROWS) {
599081
- let rows = entries.map((e) => ({ ...e, count: 1 }));
599082
- if (entries.length > COLLAPSE_THRESHOLD) {
599083
- const out = [];
599084
- for (let i = 0;i < entries.length; ) {
599085
- const e = entries[i];
599086
- let j = i + 1;
599087
- if (e.status === "ok" && isReadTool(e.tool)) {
599088
- while (j < entries.length && entries[j].tool === e.tool && entries[j].status === "ok")
599089
- j++;
599090
- }
599091
- if (j - i > 1) {
599092
- const run = entries.slice(i, j);
599093
- out.push({
599094
- tool: e.tool,
599095
- status: "ok",
599096
- count: run.length,
599097
- summary: commonDir(run.map((r) => r.summary))
599098
- });
599099
- } else {
599100
- out.push({ ...e, count: 1 });
599101
- }
599102
- i = j;
599103
- }
599104
- rows = out;
599105
- }
599106
- const cap = Math.max(1, Math.floor(maxRows));
599107
- if (rows.length > cap) {
599108
- let toFold = rows.length - cap + 1;
599109
- const fold = new Set;
599110
- for (const wantOk of [true, false]) {
599111
- for (let i = 0;i < rows.length && toFold > 0; i++) {
599112
- if (fold.has(i) || wantOk && rows[i].status !== "ok")
599113
- continue;
599114
- fold.add(i);
599115
- toFold--;
599116
- }
599117
- }
599118
- const folded = rows.filter((_, i) => fold.has(i));
599119
- const count = (s) => folded.filter((r) => r.status === s).reduce((n, r) => n + r.count, 0);
599120
- const failed = count("fail");
599121
- const blocked = count("blocked");
599122
- const summary = {
599123
- tool: "",
599124
- summary: "",
599125
- status: failed > 0 ? "fail" : blocked > 0 ? "blocked" : "ok",
599126
- count: folded.reduce((n, r) => n + r.count, 0),
599127
- folded: true,
599128
- all: fold.size === rows.length
599129
- };
599130
- if (failed > 0)
599131
- summary.failed = failed;
599132
- if (blocked > 0)
599133
- summary.blocked = blocked;
599134
- rows = [summary, ...rows.filter((_, i) => !fold.has(i))];
599135
- }
599136
- return rows;
599137
- }
599138
- var ICONS = { ok: "\u2713", fail: "\u2717", blocked: "\u2298" };
599139
- function formatTrailRow(row, width) {
599140
- const icon = ICONS[row.status];
599141
- const budget = Math.max(1, width - icon.length - 1);
599142
- if (row.folded) {
599143
- const parts = [`${row.count} ${row.all ? "calls" : "earlier calls"}`];
599144
- if (row.failed)
599145
- parts.push(`${row.failed} failed`);
599146
- if (row.blocked)
599147
- parts.push(`${row.blocked} blocked`);
599148
- return { icon, text: clip(parts.join(", "), budget) };
599149
- }
599150
- const head = row.count > 1 ? `${row.tool} \xD7${row.count}` : row.tool;
599151
- const tail = row.reason ? ` (${row.reason})` : "";
599152
- let summary = row.count > 1 ? row.summary ? `(${row.summary}\u2026)` : "" : row.summary;
599153
- if (!summary)
599154
- return { icon, text: clip(`${head}${tail}`, budget) };
599155
- const room = budget - head.length - 1 - tail.length;
599156
- if (room < 4)
599157
- return { icon, text: clip(`${head}${tail}`, budget) };
599158
- if (summary.length > room)
599159
- summary = `${summary.slice(0, room - 1)}\u2026`;
599160
- return { icon, text: `${head} ${summary}${tail}` };
599161
- }
599162
- function clip(s, width) {
599163
- return s.length <= width ? s : `${s.slice(0, Math.max(0, width - 1))}\u2026`;
599164
- }
599165
- function buildChatItems(messages) {
599166
- const items = [];
599167
- let pending = [];
599168
- let pendingFirst = null;
599169
- const flush = () => {
599170
- if (pending.length > 0 && pendingFirst) {
599171
- items.push({
599172
- message: { ...pendingFirst, id: `trail-${pendingFirst.id}`, role: "tool", content: "" },
599173
- trail: pending
599174
- });
599175
- }
599176
- pending = [];
599177
- pendingFirst = null;
599178
- };
599179
- for (const m of messages) {
599180
- if (m.role === "tool") {
599181
- if (m.toolTrail) {
599182
- if (!pendingFirst)
599183
- pendingFirst = m;
599184
- pending.push(m.toolTrail);
599185
- }
599186
- continue;
599187
- }
599188
- if (m.role === "assistant") {
599189
- items.push({ message: m, trail: pending });
599190
- pending = [];
599191
- pendingFirst = null;
599192
- continue;
599193
- }
599194
- if (m.role === "user")
599195
- flush();
599196
- items.push({ message: m, trail: [] });
599197
- }
599198
- flush();
599199
- return items;
599200
- }
599201
-
599202
599047
  // apps/tui/src/hooks/useMouseScroll.ts
599203
599048
  var import_react57 = __toESM(require_react(), 1);
599204
599049
  function useMouseScroll({
@@ -599224,111 +599069,25 @@ function useMouseScroll({
599224
599069
  }, [enabled, step]);
599225
599070
  }
599226
599071
 
599227
- // apps/tui/src/components/fade-transition.tsx
599228
- var import_react58 = __toESM(require_react(), 1);
599229
- var jsx_dev_runtime30 = __toESM(require_jsx_dev_runtime(), 1);
599230
- function FadeIn({ children, duration = 300, delay = 0, onComplete }) {
599231
- const [started, setStarted] = import_react58.useState(false);
599232
- import_react58.useEffect(() => {
599233
- const timeout = setTimeout(() => {
599234
- setStarted(true);
599235
- }, delay);
599236
- return () => clearTimeout(timeout);
599237
- }, [delay]);
599238
- import_react58.useEffect(() => {
599239
- if (!started)
599240
- return;
599241
- const timeout = setTimeout(() => {
599242
- onComplete?.();
599243
- }, duration);
599244
- return () => clearTimeout(timeout);
599245
- }, [started, duration, onComplete]);
599246
- if (!started)
599247
- return null;
599248
- return /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Box_default, {
599249
- children
599250
- }, undefined, false, undefined, this);
599251
- }
599252
-
599253
- // apps/tui/src/components/ToolTrail.tsx
599254
- var import_react59 = __toESM(require_react(), 1);
599255
- var jsx_dev_runtime31 = __toESM(require_jsx_dev_runtime(), 1);
599256
- function iconColor(status) {
599257
- return status === "ok" ? t.green : status === "fail" ? t.red : t.orange;
599258
- }
599259
- function useLandingRows(total, animate) {
599260
- const [shown, setShown] = import_react59.useState(total);
599261
- const shownRef = import_react59.useRef(total);
599262
- shownRef.current = shown;
599263
- const landingRef = import_react59.useRef(null);
599264
- import_react59.useEffect(() => {
599265
- const from = shownRef.current;
599266
- if (total <= from + 1 || !motionEnabled(animate)) {
599267
- landingRef.current = null;
599268
- setShown(total);
599269
- return;
599270
- }
599271
- landingRef.current = total;
599272
- const step = staggerFor(total - from);
599273
- let n = from + 1;
599274
- setShown(n);
599275
- const id = setInterval(() => {
599276
- n += 1;
599277
- setShown(Math.min(total, n));
599278
- if (n >= total) {
599279
- landingRef.current = null;
599280
- clearInterval(id);
599281
- }
599282
- }, step);
599283
- return () => clearInterval(id);
599284
- }, [total, animate]);
599285
- if (landingRef.current === total)
599286
- return Math.min(shown, total);
599287
- if (total <= shown + 1)
599288
- return total;
599289
- return shown;
599290
- }
599291
- function ToolTrail({
599292
- entries,
599293
- width,
599294
- maxRows,
599295
- animate = true
599296
- }) {
599297
- const rows = collapseTrail(entries, maxRows);
599298
- const visible = useLandingRows(rows.length, animate);
599299
- if (rows.length === 0)
599300
- return null;
599301
- return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
599302
- flexDirection: "column",
599303
- flexShrink: 0,
599304
- children: rows.slice(0, visible).map((row, i) => {
599305
- const { text } = formatTrailRow(row, width);
599306
- const g = glyphs();
599307
- const icon = row.status === "ok" ? g.ok : row.status === "fail" ? g.fail : g.blocked;
599308
- return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
599309
- width,
599310
- children: [
599311
- /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
599312
- color: iconColor(row.status),
599313
- children: icon
599314
- }, undefined, false, undefined, this),
599315
- /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
599316
- color: row.status === "ok" ? t.muted : t.textPrimary,
599317
- wrap: "truncate-end",
599318
- children: ` ${text}`
599319
- }, undefined, false, undefined, this)
599320
- ]
599321
- }, i, true, undefined, this);
599322
- })
599323
- }, undefined, false, undefined, this);
599072
+ // apps/tui/src/lib/chat-layout.ts
599073
+ var SHELL_CHROME = 4;
599074
+ var COLUMN_GAP = 1;
599075
+ var ACTIVITY_RAIL_WIDTH = 34;
599076
+ function chatColumnWidth(cols, shell, min = 24) {
599077
+ let width = cols - SHELL_CHROME;
599078
+ if (shell.planWidth && shell.planWidth > 0)
599079
+ width -= shell.planWidth + COLUMN_GAP;
599080
+ if (shell.activity)
599081
+ width -= ACTIVITY_RAIL_WIDTH + COLUMN_GAP;
599082
+ return Math.max(min, width);
599324
599083
  }
599325
- function toolTrailRows(entries, maxRows) {
599326
- return entries.length === 0 ? 0 : collapseTrail(entries, maxRows).length;
599084
+ var USER_BUBBLE_SHARE = 0.78;
599085
+ function bubbleWidths(contentWidth, role) {
599086
+ const inner = Math.max(16, contentWidth - 2);
599087
+ const bubble = role === "user" ? Math.max(16, Math.floor(inner * USER_BUBBLE_SHARE)) : inner;
599088
+ return { bubble, wrap: Math.max(8, bubble - 2) };
599327
599089
  }
599328
599090
 
599329
- // apps/tui/src/components/TurnResults.tsx
599330
- var import_react60 = __toESM(require_react(), 1);
599331
-
599332
599091
  // apps/tui/src/lib/inline-markdown.ts
599333
599092
  var MAX_DEPTH3 = 3;
599334
599093
  var INDENT_STEP = 2;
@@ -599643,6 +599402,215 @@ function blockRows(block, width) {
599643
599402
  }
599644
599403
  }
599645
599404
 
599405
+ // apps/tui/src/lib/tool-trail.ts
599406
+ var COLLAPSE_THRESHOLD = 8;
599407
+ var MAX_TRAIL_ROWS = 12;
599408
+ var PATH_KEYS = ["path", "file_path", "filePath", "file", "directory", "dir"];
599409
+ var COMMAND_KEYS = ["command", "cmd"];
599410
+ var QUERY_KEYS = ["query", "pattern", "q", "search", "url"];
599411
+ function oneLine(s) {
599412
+ return s.replace(/\s+/g, " ").trim().slice(0, 200);
599413
+ }
599414
+ function pick3(args, keys) {
599415
+ for (const k of keys) {
599416
+ const v = args[k];
599417
+ if (typeof v === "string" && v.trim())
599418
+ return oneLine(v);
599419
+ }
599420
+ return null;
599421
+ }
599422
+ function summarizeToolArgs(tool, args) {
599423
+ if (!args || typeof args !== "object")
599424
+ return "";
599425
+ const isCommand = /command|shell|exec|bash/.test(tool);
599426
+ const order = isCommand ? [COMMAND_KEYS, PATH_KEYS, QUERY_KEYS] : /search|grep|glob|find|fetch/.test(tool) ? [QUERY_KEYS, PATH_KEYS, COMMAND_KEYS] : [PATH_KEYS, COMMAND_KEYS, QUERY_KEYS];
599427
+ for (const keys of order) {
599428
+ const hit = pick3(args, keys);
599429
+ if (hit)
599430
+ return hit;
599431
+ }
599432
+ for (const v of Object.values(args)) {
599433
+ if (typeof v === "string" && v.trim())
599434
+ return oneLine(v);
599435
+ }
599436
+ return "";
599437
+ }
599438
+ var GATE_PREFIX = /^\s*\[([A-Z0-9_ -]*\b(?:BLOCKED|DENIED)\b[A-Z0-9_ -]*)\]/;
599439
+ var OTHER_BLOCK = /^\s*(?:Path blocked by path-guard|Path traversal blocked)|Hook blocked tool/;
599440
+ var RULE_NAME = /\[([a-z0-9]+(?:-[a-z0-9]+)+)\]/;
599441
+ function classifyToolResult(success, preview) {
599442
+ const text = preview ?? "";
599443
+ const gate = text.match(GATE_PREFIX);
599444
+ if (gate || OTHER_BLOCK.test(text)) {
599445
+ if (gate && /DENIED/.test(gate[1]))
599446
+ return { status: "blocked", reason: "denied" };
599447
+ const rule = text.match(RULE_NAME);
599448
+ return { status: "blocked", reason: rule ? `blocked: [${rule[1]}]` : "blocked" };
599449
+ }
599450
+ const exit = text.match(/^\s*Exit code (-?\d+)/);
599451
+ if (exit && exit[1] !== "0")
599452
+ return { status: "fail", reason: `exit ${exit[1]}` };
599453
+ if (!success || /^\s*(?:Error\b|Unknown tool:)/.test(text)) {
599454
+ const first = (text.split(`
599455
+ `).find((l) => l.trim()) ?? "").replace(/^\s*Error(?: running tool "[^"]*")?:?\s*/, "").trim();
599456
+ if (!first)
599457
+ return { status: "fail", reason: "error" };
599458
+ return { status: "fail", reason: first.length > 40 ? `${first.slice(0, 39)}\u2026` : first };
599459
+ }
599460
+ return { status: "ok" };
599461
+ }
599462
+ function toTrailEntry(event) {
599463
+ const { status, reason } = classifyToolResult(event.success !== false, event.resultPreview);
599464
+ const entry = {
599465
+ tool: event.toolName,
599466
+ summary: summarizeToolArgs(event.toolName, event.args),
599467
+ status
599468
+ };
599469
+ if (reason)
599470
+ entry.reason = reason;
599471
+ if (event.toolName === "update_plan") {
599472
+ const items = event.args?.plan;
599473
+ if (Array.isArray(items)) {
599474
+ const plan = items.map((i) => i && typeof i === "object" ? i : {}).filter((i) => typeof i.step === "string" && i.step.trim() !== "").map((i) => ({ step: i.step, status: typeof i.status === "string" ? i.status : "pending" }));
599475
+ if (plan.length > 0)
599476
+ entry.plan = plan;
599477
+ }
599478
+ }
599479
+ return entry;
599480
+ }
599481
+ function isReadTool(tool) {
599482
+ return /^(?:read_|list_|get_|search|grep|glob|find|view|web_search|web_fetch)/.test(tool);
599483
+ }
599484
+ function commonDir(paths) {
599485
+ const split = paths.map((p) => p.split("/").slice(0, -1));
599486
+ const first = split[0] ?? [];
599487
+ let n = 0;
599488
+ while (n < first.length && split.every((s) => s[n] === first[n]))
599489
+ n++;
599490
+ return n > 0 ? `${first.slice(0, n).join("/")}/` : "";
599491
+ }
599492
+ function collapseTrail(entries, maxRows = MAX_TRAIL_ROWS) {
599493
+ let rows = entries.map((e) => ({ ...e, count: 1 }));
599494
+ if (entries.length > COLLAPSE_THRESHOLD) {
599495
+ const out = [];
599496
+ for (let i = 0;i < entries.length; ) {
599497
+ const e = entries[i];
599498
+ let j = i + 1;
599499
+ if (e.status === "ok" && isReadTool(e.tool)) {
599500
+ while (j < entries.length && entries[j].tool === e.tool && entries[j].status === "ok")
599501
+ j++;
599502
+ }
599503
+ if (j - i > 1) {
599504
+ const run = entries.slice(i, j);
599505
+ out.push({
599506
+ tool: e.tool,
599507
+ status: "ok",
599508
+ count: run.length,
599509
+ summary: commonDir(run.map((r) => r.summary))
599510
+ });
599511
+ } else {
599512
+ out.push({ ...e, count: 1 });
599513
+ }
599514
+ i = j;
599515
+ }
599516
+ rows = out;
599517
+ }
599518
+ const cap = Math.max(1, Math.floor(maxRows));
599519
+ if (rows.length > cap) {
599520
+ let toFold = rows.length - cap + 1;
599521
+ const fold = new Set;
599522
+ for (const wantOk of [true, false]) {
599523
+ for (let i = 0;i < rows.length && toFold > 0; i++) {
599524
+ if (fold.has(i) || wantOk && rows[i].status !== "ok")
599525
+ continue;
599526
+ fold.add(i);
599527
+ toFold--;
599528
+ }
599529
+ }
599530
+ const folded = rows.filter((_, i) => fold.has(i));
599531
+ const count = (s) => folded.filter((r) => r.status === s).reduce((n, r) => n + r.count, 0);
599532
+ const failed = count("fail");
599533
+ const blocked = count("blocked");
599534
+ const summary = {
599535
+ tool: "",
599536
+ summary: "",
599537
+ status: failed > 0 ? "fail" : blocked > 0 ? "blocked" : "ok",
599538
+ count: folded.reduce((n, r) => n + r.count, 0),
599539
+ folded: true,
599540
+ all: fold.size === rows.length
599541
+ };
599542
+ if (failed > 0)
599543
+ summary.failed = failed;
599544
+ if (blocked > 0)
599545
+ summary.blocked = blocked;
599546
+ rows = [summary, ...rows.filter((_, i) => !fold.has(i))];
599547
+ }
599548
+ return rows;
599549
+ }
599550
+ var ICONS = { ok: "\u2713", fail: "\u2717", blocked: "\u2298" };
599551
+ function formatTrailRow(row, width) {
599552
+ const icon = ICONS[row.status];
599553
+ const budget = Math.max(1, width - icon.length - 1);
599554
+ if (row.folded) {
599555
+ const parts = [`${row.count} ${row.all ? "calls" : "earlier calls"}`];
599556
+ if (row.failed)
599557
+ parts.push(`${row.failed} failed`);
599558
+ if (row.blocked)
599559
+ parts.push(`${row.blocked} blocked`);
599560
+ return { icon, text: clip(parts.join(", "), budget) };
599561
+ }
599562
+ const head = row.count > 1 ? `${row.tool} \xD7${row.count}` : row.tool;
599563
+ const tail = row.reason ? ` (${row.reason})` : "";
599564
+ let summary = row.count > 1 ? row.summary ? `(${row.summary}\u2026)` : "" : row.summary;
599565
+ if (!summary)
599566
+ return { icon, text: clip(`${head}${tail}`, budget) };
599567
+ const room = budget - head.length - 1 - tail.length;
599568
+ if (room < 4)
599569
+ return { icon, text: clip(`${head}${tail}`, budget) };
599570
+ if (summary.length > room)
599571
+ summary = `${summary.slice(0, room - 1)}\u2026`;
599572
+ return { icon, text: `${head} ${summary}${tail}` };
599573
+ }
599574
+ function clip(s, width) {
599575
+ return s.length <= width ? s : `${s.slice(0, Math.max(0, width - 1))}\u2026`;
599576
+ }
599577
+ function buildChatItems(messages) {
599578
+ const items = [];
599579
+ let pending = [];
599580
+ let pendingFirst = null;
599581
+ const flush = () => {
599582
+ if (pending.length > 0 && pendingFirst) {
599583
+ items.push({
599584
+ message: { ...pendingFirst, id: `trail-${pendingFirst.id}`, role: "tool", content: "" },
599585
+ trail: pending
599586
+ });
599587
+ }
599588
+ pending = [];
599589
+ pendingFirst = null;
599590
+ };
599591
+ for (const m of messages) {
599592
+ if (m.role === "tool") {
599593
+ if (m.toolTrail) {
599594
+ if (!pendingFirst)
599595
+ pendingFirst = m;
599596
+ pending.push(m.toolTrail);
599597
+ }
599598
+ continue;
599599
+ }
599600
+ if (m.role === "assistant") {
599601
+ items.push({ message: m, trail: pending });
599602
+ pending = [];
599603
+ pendingFirst = null;
599604
+ continue;
599605
+ }
599606
+ if (m.role === "user")
599607
+ flush();
599608
+ items.push({ message: m, trail: [] });
599609
+ }
599610
+ flush();
599611
+ return items;
599612
+ }
599613
+
599646
599614
  // apps/tui/src/lib/activity-rail-derivation.ts
599647
599615
  function deriveTools(messages, isProcessing, limit = 5) {
599648
599616
  const ended = new Map;
@@ -600114,8 +600082,85 @@ function turnResultRows(trail, maxRows, plan) {
600114
600082
  return buildTurnResults(trail, maxRows, plan).length;
600115
600083
  }
600116
600084
 
600085
+ // apps/tui/src/components/ToolTrail.tsx
600086
+ var import_react58 = __toESM(require_react(), 1);
600087
+ var jsx_dev_runtime30 = __toESM(require_jsx_dev_runtime(), 1);
600088
+ function iconColor(status) {
600089
+ return status === "ok" ? t.green : status === "fail" ? t.red : t.orange;
600090
+ }
600091
+ function useLandingRows(total, animate) {
600092
+ const [shown, setShown] = import_react58.useState(total);
600093
+ const shownRef = import_react58.useRef(total);
600094
+ shownRef.current = shown;
600095
+ const landingRef = import_react58.useRef(null);
600096
+ import_react58.useEffect(() => {
600097
+ const from = shownRef.current;
600098
+ if (total <= from + 1 || !motionEnabled(animate)) {
600099
+ landingRef.current = null;
600100
+ setShown(total);
600101
+ return;
600102
+ }
600103
+ landingRef.current = total;
600104
+ const step = staggerFor(total - from);
600105
+ let n = from + 1;
600106
+ setShown(n);
600107
+ const id = setInterval(() => {
600108
+ n += 1;
600109
+ setShown(Math.min(total, n));
600110
+ if (n >= total) {
600111
+ landingRef.current = null;
600112
+ clearInterval(id);
600113
+ }
600114
+ }, step);
600115
+ return () => clearInterval(id);
600116
+ }, [total, animate]);
600117
+ if (landingRef.current === total)
600118
+ return Math.min(shown, total);
600119
+ if (total <= shown + 1)
600120
+ return total;
600121
+ return shown;
600122
+ }
600123
+ function ToolTrail({
600124
+ entries,
600125
+ width,
600126
+ maxRows,
600127
+ animate = true
600128
+ }) {
600129
+ const rows = collapseTrail(entries, maxRows);
600130
+ const visible = useLandingRows(rows.length, animate);
600131
+ if (rows.length === 0)
600132
+ return null;
600133
+ return /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Box_default, {
600134
+ flexDirection: "column",
600135
+ flexShrink: 0,
600136
+ children: rows.slice(0, visible).map((row, i) => {
600137
+ const { text } = formatTrailRow(row, width);
600138
+ const g = glyphs();
600139
+ const icon = row.status === "ok" ? g.ok : row.status === "fail" ? g.fail : g.blocked;
600140
+ return /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Box_default, {
600141
+ width,
600142
+ children: [
600143
+ /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Text, {
600144
+ color: iconColor(row.status),
600145
+ children: icon
600146
+ }, undefined, false, undefined, this),
600147
+ /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Text, {
600148
+ color: row.status === "ok" ? t.muted : t.textPrimary,
600149
+ wrap: "truncate-end",
600150
+ children: ` ${text}`
600151
+ }, undefined, false, undefined, this)
600152
+ ]
600153
+ }, i, true, undefined, this);
600154
+ })
600155
+ }, undefined, false, undefined, this);
600156
+ }
600157
+ function toolTrailRows(entries, maxRows) {
600158
+ return entries.length === 0 ? 0 : collapseTrail(entries, maxRows).length;
600159
+ }
600160
+
600117
600161
  // apps/tui/src/components/TurnResults.tsx
600118
- var jsx_dev_runtime32 = __toESM(require_jsx_dev_runtime(), 1);
600162
+ var import_react59 = __toESM(require_react(), 1);
600163
+ var jsx_dev_runtime31 = __toESM(require_jsx_dev_runtime(), 1);
600119
600164
  function iconFor(status) {
600120
600165
  const g = glyphs();
600121
600166
  if (status === "ok")
@@ -600127,8 +600172,8 @@ function iconFor(status) {
600127
600172
  return { icon: g.pending, color: t.muted };
600128
600173
  }
600129
600174
  function useResultLanding(total, land) {
600130
- const [shown, setShown] = import_react60.useState(land ? 0 : total);
600131
- import_react60.useEffect(() => {
600175
+ const [shown, setShown] = import_react59.useState(land ? 0 : total);
600176
+ import_react59.useEffect(() => {
600132
600177
  if (!land) {
600133
600178
  setShown(total);
600134
600179
  return;
@@ -600168,12 +600213,12 @@ function TurnResults({
600168
600213
  const visible = useResultLanding(rows.length, land && motionEnabled(animate));
600169
600214
  if (rows.length === 0)
600170
600215
  return null;
600171
- return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
600216
+ return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
600172
600217
  flexDirection: "column",
600173
600218
  flexShrink: 0,
600174
600219
  children: rows.map((row, i) => {
600175
600220
  if (i >= visible) {
600176
- return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
600221
+ return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
600177
600222
  height: 1,
600178
600223
  width
600179
600224
  }, i, false, undefined, this);
@@ -600181,55 +600226,55 @@ function TurnResults({
600181
600226
  const { icon, color } = iconFor(row.status);
600182
600227
  const fit = fitResultRow(row, width);
600183
600228
  const quiet = row.status === "pending" || row.kind === "fold";
600184
- return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
600229
+ return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
600185
600230
  width,
600186
600231
  height: 1,
600187
- children: /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600232
+ children: /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600188
600233
  wrap: "truncate-end",
600189
600234
  children: [
600190
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600235
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600191
600236
  color,
600192
600237
  children: icon
600193
600238
  }, undefined, false, undefined, this),
600194
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600239
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600195
600240
  children: " "
600196
600241
  }, undefined, false, undefined, this),
600197
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600242
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600198
600243
  bold: !quiet,
600199
600244
  color: quiet ? t.muted : t.textPrimary,
600200
600245
  children: fit.verb
600201
600246
  }, undefined, false, undefined, this),
600202
- fit.spans ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(jsx_dev_runtime32.Fragment, {
600247
+ fit.spans ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(jsx_dev_runtime31.Fragment, {
600203
600248
  children: [
600204
- fit.verb ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600249
+ fit.verb ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600205
600250
  children: " "
600206
600251
  }, undefined, false, undefined, this) : null,
600207
- fit.spans.map((s, j) => s.code ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600252
+ fit.spans.map((s, j) => s.code ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600208
600253
  color: t.textPrimary,
600209
600254
  backgroundColor: t.border,
600210
600255
  children: s.text
600211
- }, j, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600256
+ }, j, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600212
600257
  color: quiet ? t.muted : t.textSecondary,
600213
600258
  children: s.text
600214
600259
  }, j, false, undefined, this))
600215
600260
  ]
600216
- }, undefined, true, undefined, this) : fit.text ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600261
+ }, undefined, true, undefined, this) : fit.text ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600217
600262
  color: quiet ? t.muted : t.textSecondary,
600218
600263
  children: ` ${fit.text}`
600219
600264
  }, undefined, false, undefined, this) : null,
600220
- fit.chip ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(jsx_dev_runtime32.Fragment, {
600265
+ fit.chip ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(jsx_dev_runtime31.Fragment, {
600221
600266
  children: [
600222
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600267
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600223
600268
  children: " "
600224
600269
  }, undefined, false, undefined, this),
600225
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600270
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600226
600271
  color: t.textPrimary,
600227
600272
  backgroundColor: t.border,
600228
600273
  children: ` ${fit.chip} `
600229
600274
  }, undefined, false, undefined, this)
600230
600275
  ]
600231
600276
  }, undefined, true, undefined, this) : null,
600232
- fit.note ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
600277
+ fit.note ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
600233
600278
  color: row.status === "fail" ? t.red : t.muted,
600234
600279
  children: `${" ".repeat(fit.noteGap ?? 2)}${fit.note}`
600235
600280
  }, undefined, false, undefined, this) : null
@@ -600240,6 +600285,32 @@ function TurnResults({
600240
600285
  }, undefined, false, undefined, this);
600241
600286
  }
600242
600287
 
600288
+ // apps/tui/src/components/fade-transition.tsx
600289
+ var import_react60 = __toESM(require_react(), 1);
600290
+ var jsx_dev_runtime32 = __toESM(require_jsx_dev_runtime(), 1);
600291
+ function FadeIn({ children, duration = 300, delay = 0, onComplete }) {
600292
+ const [started, setStarted] = import_react60.useState(false);
600293
+ import_react60.useEffect(() => {
600294
+ const timeout = setTimeout(() => {
600295
+ setStarted(true);
600296
+ }, delay);
600297
+ return () => clearTimeout(timeout);
600298
+ }, [delay]);
600299
+ import_react60.useEffect(() => {
600300
+ if (!started)
600301
+ return;
600302
+ const timeout = setTimeout(() => {
600303
+ onComplete?.();
600304
+ }, duration);
600305
+ return () => clearTimeout(timeout);
600306
+ }, [started, duration, onComplete]);
600307
+ if (!started)
600308
+ return null;
600309
+ return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
600310
+ children
600311
+ }, undefined, false, undefined, this);
600312
+ }
600313
+
600243
600314
  // apps/tui/src/components/sound-effects.tsx
600244
600315
  var import_react61 = __toESM(require_react(), 1);
600245
600316
  var BELL = "\x07";
@@ -600652,6 +600723,7 @@ function MessageList({
600652
600723
  showAnimations,
600653
600724
  isLatestAssistant: message.id === lastAssistantId,
600654
600725
  trail: trailById.get(message.id) ?? [],
600726
+ turnRunning,
600655
600727
  trailMaxRows: trailCaps.get(message.id),
600656
600728
  onAnimationStart: (id) => {
600657
600729
  animatedIdsRef.current.add(id);
@@ -600683,6 +600755,7 @@ function MessageItem({
600683
600755
  showAnimations,
600684
600756
  isLatestAssistant = false,
600685
600757
  trail = [],
600758
+ turnRunning = false,
600686
600759
  trailMaxRows,
600687
600760
  onAnimationStart
600688
600761
  }) {
@@ -600868,7 +600941,7 @@ function MessageItem({
600868
600941
  animate: showAnimations
600869
600942
  }, undefined, false, undefined, this)
600870
600943
  }, undefined, false, undefined, this),
600871
- !isUser && !message.content.trim() && trail.length > 0 ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(MutedText, {
600944
+ !isUser && !turnRunning && !message.content.trim() && trail.length > 0 ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(MutedText, {
600872
600945
  children: "No reply."
600873
600946
  }, undefined, false, undefined, this) : null,
600874
600947
  message.content.trim() || trail.length === 0 ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
@@ -601030,7 +601103,11 @@ function MarkdownBody({
601030
601103
  })
601031
601104
  }, undefined, false, undefined, this);
601032
601105
  }
601033
- function InlineSpans({ spans, color, adhdMode }) {
601106
+ function InlineSpans({
601107
+ spans,
601108
+ color,
601109
+ adhdMode
601110
+ }) {
601034
601111
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
601035
601112
  color,
601036
601113
  wrap: "wrap",
@@ -603441,7 +603518,7 @@ init_cloud_transcriber();
603441
603518
  init_types5();
603442
603519
 
603443
603520
  // packages/voice/voice-chat.ts
603444
- import { existsSync as existsSync114, unlinkSync as unlinkSync5 } from "fs";
603521
+ import { existsSync as existsSync115, unlinkSync as unlinkSync5 } from "fs";
603445
603522
  import { tmpdir as tmpdir21 } from "os";
603446
603523
  import { join as join172 } from "path";
603447
603524
 
@@ -603900,7 +603977,7 @@ class VoiceChatLoop {
603900
603977
  cleanupFile(wavPath);
603901
603978
  throw new VoiceChatFatalError(describeRecFailure(exitCode, why));
603902
603979
  }
603903
- if (!existsSync114(wavPath)) {
603980
+ if (!existsSync115(wavPath)) {
603904
603981
  this.reportError("No audio was recorded. Check that your microphone is the system input.");
603905
603982
  return null;
603906
603983
  }
@@ -604024,7 +604101,7 @@ function sleep2(ms) {
604024
604101
  }
604025
604102
  function cleanupFile(path) {
604026
604103
  try {
604027
- if (existsSync114(path))
604104
+ if (existsSync115(path))
604028
604105
  unlinkSync5(path);
604029
604106
  } catch {}
604030
604107
  }
@@ -604084,7 +604161,7 @@ function splitIntoSentences(text) {
604084
604161
  }
604085
604162
  // packages/voice/full-duplex-provider.ts
604086
604163
  import { execSync as execSync6 } from "child_process";
604087
- import { existsSync as existsSync115 } from "fs";
604164
+ import { existsSync as existsSync116 } from "fs";
604088
604165
  import { homedir as homedir87 } from "os";
604089
604166
  import { join as join173 } from "path";
604090
604167
  var MOSHI_HF_CACHE_PATH = join173(homedir87(), ".cache", "huggingface", "hub", "models--kyutai--moshiko-pytorch-bf16", "snapshots");
@@ -604102,7 +604179,7 @@ async function detectCapabilities() {
604102
604179
  const hasAppleSilicon = platform === "darwin" && arch === "arm64";
604103
604180
  const hasMLX = hasAppleSilicon && tryExec('python3 -c "import mlx"');
604104
604181
  const hasMoshiInstalled = tryExec('python3 -c "import moshi"');
604105
- const hasMoshiModel = existsSync115(MOSHI_HF_CACHE_PATH);
604182
+ const hasMoshiModel = existsSync116(MOSHI_HF_CACHE_PATH);
604106
604183
  const hasCUDA = tryExec("nvidia-smi");
604107
604184
  const hasNIMKey = !!process.env.NVIDIA_NIM_KEY;
604108
604185
  return {
@@ -605133,7 +605210,7 @@ function useWorkspaceTabs() {
605133
605210
  }
605134
605211
 
605135
605212
  // apps/tui/src/lib/adhd-audio.ts
605136
- import { existsSync as existsSync117, mkdirSync as mkdirSync82 } from "fs";
605213
+ import { existsSync as existsSync118, mkdirSync as mkdirSync82 } from "fs";
605137
605214
  import { join as join176 } from "path";
605138
605215
  var {spawn: spawn28 } = globalThis.Bun;
605139
605216
  var DEFAULT_CONFIG4 = {
@@ -605178,7 +605255,7 @@ var CACHE_DIR3 = join176(process.env.HOME || "~", ".8gent", "adhd-audio");
605178
605255
  var CONFIG_PATH = join176(CACHE_DIR3, "config.json");
605179
605256
  function loadConfig2() {
605180
605257
  try {
605181
- if (existsSync117(CONFIG_PATH)) {
605258
+ if (existsSync118(CONFIG_PATH)) {
605182
605259
  const raw = JSON.parse(__require("fs").readFileSync(CONFIG_PATH, "utf-8"));
605183
605260
  return { ...DEFAULT_CONFIG4, ...raw };
605184
605261
  }
@@ -605187,7 +605264,7 @@ function loadConfig2() {
605187
605264
  }
605188
605265
  function saveConfig2(config) {
605189
605266
  try {
605190
- if (!existsSync117(CACHE_DIR3))
605267
+ if (!existsSync118(CACHE_DIR3))
605191
605268
  mkdirSync82(CACHE_DIR3, { recursive: true });
605192
605269
  __require("fs").writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
605193
605270
  } catch {}
@@ -605199,7 +605276,7 @@ class ADHDAudio {
605199
605276
  generating = false;
605200
605277
  _config;
605201
605278
  constructor() {
605202
- if (!existsSync117(CACHE_DIR3)) {
605279
+ if (!existsSync118(CACHE_DIR3)) {
605203
605280
  mkdirSync82(CACHE_DIR3, { recursive: true });
605204
605281
  }
605205
605282
  this._config = loadConfig2();
@@ -605226,7 +605303,7 @@ class ADHDAudio {
605226
605303
  for (const soundscape of Object.keys(SOUNDSCAPES)) {
605227
605304
  const path = this.getCachePath(soundscape);
605228
605305
  try {
605229
- if (existsSync117(path))
605306
+ if (existsSync118(path))
605230
605307
  __require("fs").unlinkSync(path);
605231
605308
  } catch {}
605232
605309
  }
@@ -605245,7 +605322,7 @@ class ADHDAudio {
605245
605322
  return join176(CACHE_DIR3, `${soundscape}.mp3`);
605246
605323
  }
605247
605324
  hasCached(soundscape) {
605248
- return existsSync117(this.getCachePath(soundscape));
605325
+ return existsSync118(this.getCachePath(soundscape));
605249
605326
  }
605250
605327
  async generate(soundscape) {
605251
605328
  const preset = SOUNDSCAPES[soundscape];
@@ -610150,7 +610227,7 @@ function snapshot(manager) {
610150
610227
 
610151
610228
  // apps/tui/src/hooks/useBodyParts.ts
610152
610229
  var import_react83 = __toESM(require_react(), 1);
610153
- import { existsSync as existsSync122 } from "fs";
610230
+ import { existsSync as existsSync123 } from "fs";
610154
610231
  import { execSync as execSync8 } from "child_process";
610155
610232
  import { homedir as homedir90 } from "os";
610156
610233
  import { join as join187 } from "path";
@@ -610164,7 +610241,7 @@ function hasCliclick() {
610164
610241
  }
610165
610242
  }
610166
610243
  function hasAxBridge() {
610167
- return existsSync122(join187(homedir90(), ".8gent", "bin", "8gent-ax-bridge"));
610244
+ return existsSync123(join187(homedir90(), ".8gent", "bin", "8gent-ax-bridge"));
610168
610245
  }
610169
610246
  function detectDefaultBodyPartsState() {
610170
610247
  const handsOk = hasCliclick();
@@ -611752,6 +611829,24 @@ ${r.message}`;
611752
611829
  // apps/tui/src/app.tsx
611753
611830
  init_slash_commands();
611754
611831
 
611832
+ // apps/tui/src/lib/term-intent.ts
611833
+ var VERBS2 = "run|open|start|launch|fire up|spin up|boot";
611834
+ var POLITE = "(?:please\\s+|can you\\s+|could you\\s+)?";
611835
+ var WHERE = "(?:\\s+in\\s+(?:a\\s+)?(?:new\\s+)?(?:tab|terminal(?:\\s+tab)?))?";
611836
+ var PRESET_INTENT = new RegExp(`^${POLITE}(?:${VERBS2})\\s+(?:up\\s+)?([a-z0-9][a-z0-9_-]*)(${WHERE})(?:\\s+please)?$`);
611837
+ function detectTermPresetIntent(input, presetIds) {
611838
+ const text = input.trim().toLowerCase().replace(/[.!?]+$/, "").replace(/\s+/g, " ");
611839
+ if (!text || text.length > 60)
611840
+ return null;
611841
+ const m = PRESET_INTENT.exec(text);
611842
+ if (!m)
611843
+ return null;
611844
+ const id = presetIds.find((p) => p.toLowerCase() === m[1]);
611845
+ if (!id)
611846
+ return null;
611847
+ return m[2] ? [id, "--in-tab"] : [id];
611848
+ }
611849
+
611755
611850
  // apps/tui/src/lib/turn-outcome.ts
611756
611851
  init_local_turn_error();
611757
611852
  function turnEndedInError(messages) {
@@ -611769,7 +611864,7 @@ function turnEndedInError(messages) {
611769
611864
  }
611770
611865
 
611771
611866
  // apps/tui/src/screens/BTWView.tsx
611772
- import { existsSync as existsSync124, mkdirSync as mkdirSync89, readFileSync as readFileSync112, writeFileSync as writeFileSync69 } from "fs";
611867
+ import { existsSync as existsSync125, mkdirSync as mkdirSync89, readFileSync as readFileSync112, writeFileSync as writeFileSync69 } from "fs";
611773
611868
  import { join as join189 } from "path";
611774
611869
  var import_react85 = __toESM(require_react(), 1);
611775
611870
 
@@ -611785,10 +611880,10 @@ var jsx_dev_runtime50 = __toESM(require_jsx_dev_runtime(), 1);
611785
611880
  var DATA_DIR5 = join189(process.env.HOME || "~", ".8gent", "tabs");
611786
611881
  function loadBTW() {
611787
611882
  try {
611788
- if (!existsSync124(DATA_DIR5))
611883
+ if (!existsSync125(DATA_DIR5))
611789
611884
  mkdirSync89(DATA_DIR5, { recursive: true });
611790
611885
  const filepath = join189(DATA_DIR5, "btw.json");
611791
- if (existsSync124(filepath)) {
611886
+ if (existsSync125(filepath)) {
611792
611887
  const raw = JSON.parse(readFileSync112(filepath, "utf-8"));
611793
611888
  return raw.items || [];
611794
611889
  }
@@ -611797,7 +611892,7 @@ function loadBTW() {
611797
611892
  }
611798
611893
  function saveBTW(items) {
611799
611894
  try {
611800
- if (!existsSync124(DATA_DIR5))
611895
+ if (!existsSync125(DATA_DIR5))
611801
611896
  mkdirSync89(DATA_DIR5, { recursive: true });
611802
611897
  writeFileSync69(join189(DATA_DIR5, "btw.json"), JSON.stringify({ items }, null, 2));
611803
611898
  } catch {}
@@ -612132,17 +612227,17 @@ function HistoryScreen({ conversations, onSelect, onBack }) {
612132
612227
  }
612133
612228
 
612134
612229
  // apps/tui/src/screens/IdeasView.tsx
612135
- import { existsSync as existsSync125, mkdirSync as mkdirSync90, readFileSync as readFileSync113, writeFileSync as writeFileSync70 } from "fs";
612230
+ import { existsSync as existsSync126, mkdirSync as mkdirSync90, readFileSync as readFileSync113, writeFileSync as writeFileSync70 } from "fs";
612136
612231
  import { join as join190 } from "path";
612137
612232
  var import_react87 = __toESM(require_react(), 1);
612138
612233
  var jsx_dev_runtime52 = __toESM(require_jsx_dev_runtime(), 1);
612139
612234
  var DATA_DIR6 = join190(process.env.HOME || "~", ".8gent", "tabs");
612140
612235
  function loadIdeas() {
612141
612236
  try {
612142
- if (!existsSync125(DATA_DIR6))
612237
+ if (!existsSync126(DATA_DIR6))
612143
612238
  mkdirSync90(DATA_DIR6, { recursive: true });
612144
612239
  const filepath = join190(DATA_DIR6, "ideas.json");
612145
- if (existsSync125(filepath)) {
612240
+ if (existsSync126(filepath)) {
612146
612241
  const raw = JSON.parse(readFileSync113(filepath, "utf-8"));
612147
612242
  return raw.ideas || [];
612148
612243
  }
@@ -612151,7 +612246,7 @@ function loadIdeas() {
612151
612246
  }
612152
612247
  function saveIdeas(ideas) {
612153
612248
  try {
612154
- if (!existsSync125(DATA_DIR6))
612249
+ if (!existsSync126(DATA_DIR6))
612155
612250
  mkdirSync90(DATA_DIR6, { recursive: true });
612156
612251
  writeFileSync70(join190(DATA_DIR6, "ideas.json"), JSON.stringify({ ideas }, null, 2));
612157
612252
  } catch {}
@@ -612614,7 +612709,7 @@ function KeyVaultScreen({
612614
612709
  }
612615
612710
 
612616
612711
  // apps/tui/src/screens/MusicPlayerView.tsx
612617
- import { existsSync as existsSync126, readdirSync as readdirSync28, statSync as statSync38 } from "fs";
612712
+ import { existsSync as existsSync127, readdirSync as readdirSync28, statSync as statSync38 } from "fs";
612618
612713
  import { basename as basename53, join as join191 } from "path";
612619
612714
  var import_react89 = __toESM(require_react(), 1);
612620
612715
  var jsx_dev_runtime54 = __toESM(require_jsx_dev_runtime(), 1);
@@ -612636,7 +612731,7 @@ function formatDate(date) {
612636
612731
  }
612637
612732
  function loadTracks(currentTrack) {
612638
612733
  const tracks = [];
612639
- if (!existsSync126(CACHE_DIR4))
612734
+ if (!existsSync127(CACHE_DIR4))
612640
612735
  return tracks;
612641
612736
  try {
612642
612737
  const files = readdirSync28(CACHE_DIR4).filter((f) => f.endsWith(".mp3") || f.endsWith(".wav"));
@@ -613145,7 +613240,7 @@ function NarratorView({ tasks, narratorText, maxHeight }) {
613145
613240
  }
613146
613241
 
613147
613242
  // apps/tui/src/screens/NotesView.tsx
613148
- import { existsSync as existsSync127, mkdirSync as mkdirSync91, readFileSync as readFileSync114, writeFileSync as writeFileSync71 } from "fs";
613243
+ import { existsSync as existsSync128, mkdirSync as mkdirSync91, readFileSync as readFileSync114, writeFileSync as writeFileSync71 } from "fs";
613149
613244
  import { join as join192 } from "path";
613150
613245
  var import_react91 = __toESM(require_react(), 1);
613151
613246
  var jsx_dev_runtime59 = __toESM(require_jsx_dev_runtime(), 1);
@@ -613153,10 +613248,10 @@ var DATA_DIR7 = join192(process.env.HOME || "~", ".8gent", "tabs");
613153
613248
  var EMPTY_TAB_NAMES = [];
613154
613249
  function loadNotes() {
613155
613250
  try {
613156
- if (!existsSync127(DATA_DIR7))
613251
+ if (!existsSync128(DATA_DIR7))
613157
613252
  mkdirSync91(DATA_DIR7, { recursive: true });
613158
613253
  const filepath = join192(DATA_DIR7, "notes.json");
613159
- if (existsSync127(filepath)) {
613254
+ if (existsSync128(filepath)) {
613160
613255
  const raw = JSON.parse(readFileSync114(filepath, "utf-8"));
613161
613256
  return raw.notes || [];
613162
613257
  }
@@ -613165,7 +613260,7 @@ function loadNotes() {
613165
613260
  }
613166
613261
  function saveNotes(notes) {
613167
613262
  try {
613168
- if (!existsSync127(DATA_DIR7))
613263
+ if (!existsSync128(DATA_DIR7))
613169
613264
  mkdirSync91(DATA_DIR7, { recursive: true });
613170
613265
  writeFileSync71(join192(DATA_DIR7, "notes.json"), JSON.stringify({ notes }, null, 2));
613171
613266
  } catch {}
@@ -614360,7 +614455,7 @@ function OnboardingScreen({
614360
614455
 
614361
614456
  // apps/tui/src/screens/ProjectsView.tsx
614362
614457
  import { execSync as execSync9 } from "child_process";
614363
- import { existsSync as existsSync128, mkdirSync as mkdirSync92, readFileSync as readFileSync115, readdirSync as readdirSync29, statSync as statSync39, writeFileSync as writeFileSync72 } from "fs";
614458
+ import { existsSync as existsSync129, mkdirSync as mkdirSync92, readFileSync as readFileSync115, readdirSync as readdirSync29, statSync as statSync39, writeFileSync as writeFileSync72 } from "fs";
614364
614459
  import { homedir as homedir91 } from "os";
614365
614460
  import { basename as basename54, join as join193 } from "path";
614366
614461
  var import_react96 = __toESM(require_react(), 1);
@@ -614375,7 +614470,7 @@ var SCAN_DIRS = [
614375
614470
  var PROJECT_MARKERS = ["package.json", ".git", "CLAUDE.md", ".8gent"];
614376
614471
  function isProjectDir(dirPath) {
614377
614472
  try {
614378
- return PROJECT_MARKERS.some((marker) => existsSync128(join193(dirPath, marker)));
614473
+ return PROJECT_MARKERS.some((marker) => existsSync129(join193(dirPath, marker)));
614379
614474
  } catch {
614380
614475
  return false;
614381
614476
  }
@@ -614383,7 +614478,7 @@ function isProjectDir(dirPath) {
614383
614478
  function getProjectName(dirPath) {
614384
614479
  try {
614385
614480
  const pkgPath = join193(dirPath, "package.json");
614386
- if (existsSync128(pkgPath)) {
614481
+ if (existsSync129(pkgPath)) {
614387
614482
  const pkg = JSON.parse(readFileSync115(pkgPath, "utf8"));
614388
614483
  if (pkg.name)
614389
614484
  return pkg.name;
@@ -614393,7 +614488,7 @@ function getProjectName(dirPath) {
614393
614488
  }
614394
614489
  function getGitBranch2(dirPath) {
614395
614490
  try {
614396
- if (!existsSync128(join193(dirPath, ".git")))
614491
+ if (!existsSync129(join193(dirPath, ".git")))
614397
614492
  return;
614398
614493
  const result = execSync9(`git -C "${dirPath}" branch --show-current 2>/dev/null`, {
614399
614494
  encoding: "utf8",
@@ -614413,7 +614508,7 @@ function getLastModified(dirPath) {
614413
614508
  }
614414
614509
  }
614415
614510
  function has8gent(dirPath) {
614416
- return existsSync128(join193(dirPath, ".8gent"));
614511
+ return existsSync129(join193(dirPath, ".8gent"));
614417
614512
  }
614418
614513
  function buildProjectEntry(dirPath) {
614419
614514
  return {
@@ -614430,7 +614525,7 @@ function scanForProjects(currentPath) {
614430
614525
  found.add(currentPath);
614431
614526
  results.push(buildProjectEntry(currentPath));
614432
614527
  for (const scanDir of SCAN_DIRS) {
614433
- if (!existsSync128(scanDir))
614528
+ if (!existsSync129(scanDir))
614434
614529
  continue;
614435
614530
  try {
614436
614531
  const entries = readdirSync29(scanDir, { withFileTypes: true });
@@ -614462,7 +614557,7 @@ function scanForProjects(currentPath) {
614462
614557
  }
614463
614558
  function loadSavedProjects() {
614464
614559
  try {
614465
- if (!existsSync128(SAVED_PROJECTS_PATH))
614560
+ if (!existsSync129(SAVED_PROJECTS_PATH))
614466
614561
  return [];
614467
614562
  const raw = readFileSync115(SAVED_PROJECTS_PATH, "utf8");
614468
614563
  const data = JSON.parse(raw);
@@ -614485,7 +614580,7 @@ function mergeProjects(scanned, saved) {
614485
614580
  for (const s of saved) {
614486
614581
  if (paths.has(s.path))
614487
614582
  continue;
614488
- if (!existsSync128(s.path))
614583
+ if (!existsSync129(s.path))
614489
614584
  continue;
614490
614585
  paths.add(s.path);
614491
614586
  extra.push(buildProjectEntry(s.path));
@@ -614553,7 +614648,7 @@ function ProjectsView({
614553
614648
  if (addMode) {
614554
614649
  if (key.return) {
614555
614650
  const trimmed = addInput.trim();
614556
- if (trimmed && existsSync128(trimmed)) {
614651
+ if (trimmed && existsSync129(trimmed)) {
614557
614652
  const newEntry = {
614558
614653
  ...buildProjectEntry(trimmed),
614559
614654
  addedAt: new Date().toISOString()
@@ -614740,17 +614835,17 @@ function ProjectsView({
614740
614835
  }
614741
614836
 
614742
614837
  // apps/tui/src/screens/QuestionsView.tsx
614743
- import { existsSync as existsSync129, mkdirSync as mkdirSync93, readFileSync as readFileSync116, writeFileSync as writeFileSync73 } from "fs";
614838
+ import { existsSync as existsSync130, mkdirSync as mkdirSync93, readFileSync as readFileSync116, writeFileSync as writeFileSync73 } from "fs";
614744
614839
  import { join as join194 } from "path";
614745
614840
  var import_react97 = __toESM(require_react(), 1);
614746
614841
  var jsx_dev_runtime62 = __toESM(require_jsx_dev_runtime(), 1);
614747
614842
  var DATA_DIR8 = join194(process.env.HOME || "~", ".8gent", "tabs");
614748
614843
  function loadQuestions() {
614749
614844
  try {
614750
- if (!existsSync129(DATA_DIR8))
614845
+ if (!existsSync130(DATA_DIR8))
614751
614846
  mkdirSync93(DATA_DIR8, { recursive: true });
614752
614847
  const filepath = join194(DATA_DIR8, "questions.json");
614753
- if (existsSync129(filepath)) {
614848
+ if (existsSync130(filepath)) {
614754
614849
  const raw = JSON.parse(readFileSync116(filepath, "utf-8"));
614755
614850
  return raw.questions || [];
614756
614851
  }
@@ -614759,7 +614854,7 @@ function loadQuestions() {
614759
614854
  }
614760
614855
  function saveQuestions(questions) {
614761
614856
  try {
614762
- if (!existsSync129(DATA_DIR8))
614857
+ if (!existsSync130(DATA_DIR8))
614763
614858
  mkdirSync93(DATA_DIR8, { recursive: true });
614764
614859
  writeFileSync73(join194(DATA_DIR8, "questions.json"), JSON.stringify({ questions }, null, 2));
614765
614860
  } catch {}
@@ -616047,13 +616142,13 @@ import * as fs104 from "fs";
616047
616142
  import * as pathMod from "path";
616048
616143
 
616049
616144
  // apps/tui/src/lib/package-scripts.ts
616050
- import { existsSync as existsSync130 } from "fs";
616051
- import { join as join195, resolve as resolve73 } from "path";
616145
+ import { existsSync as existsSync131 } from "fs";
616146
+ import { join as join195, resolve as resolve74 } from "path";
616052
616147
  var __dirname = (import.meta.dir + "/../apps/tui/src/lib");
616053
- var PACKAGE_ROOT = resolve73(__dirname, "../../../..");
616148
+ var PACKAGE_ROOT = resolve74(__dirname, "../../../..");
616054
616149
  function packageBinScript(name, root = PACKAGE_ROOT) {
616055
616150
  const candidate = join195(root, "bin", name);
616056
- return existsSync130(candidate) ? candidate : null;
616151
+ return existsSync131(candidate) ? candidate : null;
616057
616152
  }
616058
616153
 
616059
616154
  // apps/tui/src/app.tsx
@@ -620977,6 +621072,26 @@ ${found.slice(0, 40).map((k) => ` ${k.name} ${k.description}`).join(`
620977
621072
  }
620978
621073
  return;
620979
621074
  }
621075
+ if (input && !attached) {
621076
+ const termArgs = detectTermPresetIntent(input, listPresetIds());
621077
+ if (termArgs) {
621078
+ appendToTab(tabId, {
621079
+ id: `user-${Date.now()}`,
621080
+ role: "user",
621081
+ content: bubbleContent,
621082
+ timestamp: new Date
621083
+ });
621084
+ setRecentCommands((prev) => [bubbleContent, ...prev].slice(0, 20));
621085
+ const refused = planModeRefusal2(permView(permHolderFor(tabId)).mode);
621086
+ if (refused) {
621087
+ addSystemMessage(refused);
621088
+ return;
621089
+ }
621090
+ addSystemMessage(`Opening ${termArgs[0]} (same as /term ${termArgs.join(" ")}).`);
621091
+ handleSlashCommand("term", termArgs);
621092
+ return;
621093
+ }
621094
+ }
620980
621095
  if (input && !attached) {
620981
621096
  const buildTask = detectBuildIntent(input);
620982
621097
  if (buildTask) {