@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/node/tui.js CHANGED
@@ -21831,8 +21831,6 @@ var init_allowlist = __esm(() => {
21831
21831
  "node",
21832
21832
  "npm",
21833
21833
  "npx",
21834
- "pnpm",
21835
- "yarn",
21836
21834
  "deno",
21837
21835
  "python",
21838
21836
  "python3",
@@ -21840,9 +21838,6 @@ var init_allowlist = __esm(() => {
21840
21838
  "pip3",
21841
21839
  "uv",
21842
21840
  "git",
21843
- "go",
21844
- "cargo",
21845
- "rustc",
21846
21841
  "tsc",
21847
21842
  "gh",
21848
21843
  "docker",
@@ -21867,20 +21862,18 @@ var init_allowlist = __esm(() => {
21867
21862
  "biome",
21868
21863
  "eslint",
21869
21864
  "prettier",
21870
- "vite"
21865
+ "vite",
21866
+ "8gent"
21871
21867
  ]);
21872
21868
  SHORT_VERSION = {
21873
21869
  node: "-v",
21874
21870
  bun: "-v",
21875
21871
  npm: "-v",
21876
- pnpm: "-v",
21877
- yarn: "-v",
21878
21872
  deno: "-V",
21879
21873
  python: "-V",
21880
21874
  python3: "-V",
21881
21875
  ffmpeg: "-version",
21882
- ffprobe: "-version",
21883
- go: "version"
21876
+ ffprobe: "-version"
21884
21877
  };
21885
21878
  SECRET_ARG = /(^|\/)\.env(\.[\w.-]+)?$|(^|\/)\.env\b|id_rsa|id_ed25519|id_ecdsa|(^|\/)\.ssh(\/|$)|\.aws\/|credentials|\.netrc|\.npmrc|\.git-credentials|keychain|\.pem$|\.key$|\.p12$/;
21886
21879
  REDIR_RE2 = /(?<![<>&\d])(\d?|&)(>>?|<)(\|?)\s*(&\d+|[^\s;|&<>()]+)/g;
@@ -33286,7 +33279,7 @@ var init_permissions = __esm(() => {
33286
33279
  var require_package = __commonJS(function(exports2, module2) {
33287
33280
  module2.exports = {
33288
33281
  name: "@8gi-foundation/8gent-code",
33289
- version: "0.20.15",
33282
+ version: "0.20.16",
33290
33283
  private: false,
33291
33284
  engines: {
33292
33285
  node: ">=22.5"
@@ -77734,8 +77727,31 @@ function emptyReplyStall(toolCalls) {
77734
77727
  function isEmptyReplyStall(line) {
77735
77728
  return line.startsWith(EMPTY_REPLY_STALL_PREFIX);
77736
77729
  }
77737
- function emptyReplyNote(toolCalls) {
77738
- return `[harness] No reply: ${emptyReplyStall(toolCalls)}.`;
77730
+ function refusedCalls(log, max = 3) {
77731
+ const out = [];
77732
+ let total = 0;
77733
+ for (const e of log) {
77734
+ const m = /^\[[^\]]*\b(?:BLOCKED|DENIED)\b[^\]]*\]\s*([^\n]*)/.exec((e.result ?? "").trimStart());
77735
+ if (!m)
77736
+ continue;
77737
+ total++;
77738
+ if (out.length >= max)
77739
+ continue;
77740
+ const a = e.args;
77741
+ const first = typeof a?.command === "string" ? a.command.trim().split(/\s+/)[0] ?? "" : "";
77742
+ const bin = /^[A-Za-z][\w.+-]{0,40}$/.test(first) ? ` ${first}` : "";
77743
+ const why = /declin/i.test(m[1]) ? "declined" : "blocked by policy";
77744
+ out.push(`${e.name}${bin}: ${why}`);
77745
+ }
77746
+ return { total, shown: out };
77747
+ }
77748
+ function emptyReplyNote(toolCalls, log = []) {
77749
+ const base = `[harness] No reply: ${emptyReplyStall(toolCalls)}.`;
77750
+ const { total, shown } = refusedCalls(log);
77751
+ if (total === 0)
77752
+ return base;
77753
+ const more = total > shown.length ? ` (and ${total - shown.length} more)` : "";
77754
+ return `${base} ${total} of the calls were refused, so they did not run: ${shown.join("; ")}${more}.`;
77739
77755
  }
77740
77756
  function isEmptyReplyNote(reply) {
77741
77757
  return reply.startsWith("[harness] No reply:");
@@ -77828,7 +77844,7 @@ ${summary}`;
77828
77844
  if (unverified.length === 0)
77829
77845
  return { content, rounds, toolLog, unverified: [] };
77830
77846
  const notes = [
77831
- empty ? emptyReplyNote(toolLog.length) : content.trimEnd(),
77847
+ empty ? emptyReplyNote(toolLog.length, toolLog) : content.trimEnd(),
77832
77848
  unfulfilled.length > 0 ? formatHarnessNote(unfulfilled) : ""
77833
77849
  ].filter((n) => n.trim() !== "");
77834
77850
  return { content: notes.join(`
@@ -580415,6 +580431,44 @@ var init_registry = __esm(() => {
580415
580431
  ];
580416
580432
  });
580417
580433
 
580434
+ // packages/eight/small-talk.ts
580435
+ function isSmallTalk(text) {
580436
+ const flat = text.toLowerCase().replace(/^\[mode: [^\]]*\]\s*/, "").replace(/['’]/g, "").replace(/[^a-z0-9 ]+/g, " ").replace(/\s+/g, " ").trim();
580437
+ return flat.length > 0 && flat.length <= 40 && GREETING.test(flat);
580438
+ }
580439
+ 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.";
580440
+ var init_small_talk = __esm(() => {
580441
+ CORE = [
580442
+ "h+i+",
580443
+ "h+e+y+",
580444
+ "h+e+l+o+",
580445
+ "hiya",
580446
+ "howdy",
580447
+ "y+o+",
580448
+ "sup",
580449
+ "wa+s+s?u+p+",
580450
+ "w?h?a+t?s? ?u+p+",
580451
+ "good (?:morning|afternoon|evening|night)",
580452
+ "gm",
580453
+ "gn",
580454
+ "morning",
580455
+ "evening",
580456
+ "how are you(?: doing| today)?",
580457
+ "how are things",
580458
+ "hows it going",
580459
+ "how you doing",
580460
+ "you there",
580461
+ "anyone there",
580462
+ "ping",
580463
+ "thanks(?: a lot| so much)?",
580464
+ "thank you(?: so much)?",
580465
+ "thx",
580466
+ "ty",
580467
+ "cheers"
580468
+ ].join("|");
580469
+ GREETING = new RegExp(`^(?:(?:hi+|hey+|hello+|yo+|hiya|howdy) )?(?:${CORE})(?: (?:there|again|everyone|all|team|mate|man|bro|friend|8gent|eight))?$`);
580470
+ });
580471
+
580418
580472
  // packages/eight/verify-gate.ts
580419
580473
  function isVerifyGateEnabled(env = process.env) {
580420
580474
  const v = (env.EIGHT_VERIFY_GATE ?? "").trim().toLowerCase();
@@ -581345,8 +581399,8 @@ __export(exports_system_prompt, {
581345
581399
  styleHasGuide: () => styleHasGuide,
581346
581400
  workingProjectNote: () => workingProjectNote
581347
581401
  });
581348
- import { readFileSync as readFileSync97 } from "node:fs";
581349
- import { basename as basename51, join as join155 } from "node:path";
581402
+ import { existsSync as existsSync106, readFileSync as readFileSync97 } from "node:fs";
581403
+ import { basename as basename51, join as join155, resolve as resolve73 } from "node:path";
581350
581404
  function buildBoardContextSegment(path = BOARD_CONTEXT_PATH) {
581351
581405
  let text;
581352
581406
  try {
@@ -581377,8 +581431,20 @@ function communicationStyleLine(style) {
581377
581431
  function styleHasGuide(style) {
581378
581432
  return isCommunicationStyle(style) && STYLE_GUIDE[style] !== "";
581379
581433
  }
581434
+ function isPlainHome(dir, home = resolveHome()) {
581435
+ try {
581436
+ return home !== "" && resolve73(dir) === resolve73(home) && !existsSync106(join155(dir, ".git"));
581437
+ } catch {
581438
+ return false;
581439
+ }
581440
+ }
581380
581441
  function workingProjectNote(dir) {
581381
581442
  const name = basename51(dir) || dir;
581443
+ if (isPlainHome(dir)) {
581444
+ return `
581445
+
581446
+ **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.**`;
581447
+ }
581382
581448
  if (name === "8gent-code") {
581383
581449
  return `
581384
581450
 
@@ -592540,14 +592606,15 @@ ${LOCAL_LOGIN_NOTE}`;
592540
592606
  instructions,
592541
592607
  localCoreTools,
592542
592608
  chatStartTime,
592543
- textForAgent
592609
+ textForAgent,
592610
+ smallTalk = false
592544
592611
  } = opts;
592545
592612
  const signal = this.armController().signal;
592546
592613
  const allow = new Set(localCoreTools);
592547
592614
  const specs = toolDefsToSpecs(this.executor.getToolDefinitions(), allow);
592548
592615
  let stepNumber = 0;
592549
592616
  let breakerStop = null;
592550
- if (this.extensionToolsAllowed()) {
592617
+ if (this.extensionToolsAllowed() && !smallTalk) {
592551
592618
  for (const t of this.extensionHost.listTools()) {
592552
592619
  if (this.modeAllows(t.name)) {
592553
592620
  specs.push({ name: t.name, description: t.description, parameters: t.parameters });
@@ -593125,6 +593192,10 @@ ${LOCAL_LOGIN_NOTE}`;
593125
593192
  this.turnGitBaseline = asksForCommit(userMessage) ? gitSnapshot(this.config.workingDirectory || process.cwd()) : null;
593126
593193
  this.executor.beginTurn(userMessage);
593127
593194
  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);
593195
+ const smallTalkTurn = isSmallTalk(textForAgent) && !imageBase64 && this.config.agentScope !== "__table__";
593196
+ if (smallTalkTurn) {
593197
+ this.messageHistory.push({ role: "user", content: harnessNote(SMALL_TALK_NOTE) });
593198
+ }
593128
593199
  let visionId = null;
593129
593200
  let visionFailed = false;
593130
593201
  if (imageBase64 && await this.promptImageSeen()) {
@@ -593195,7 +593266,7 @@ ${result.description.slice(0, 200)}${result.description.length > 200 ? "..." : "
593195
593266
  }
593196
593267
  }
593197
593268
  const quickPrompt = quickLaneEnabled() && classifyPrompt(textForAgent) === "quick";
593198
- if (needsClarification(textForAgent) && !imageBase64 && !quickPrompt && !isNoToolScope(this.config.agentScope)) {
593269
+ if (needsClarification(textForAgent) && !imageBase64 && !quickPrompt && !smallTalkTurn && !isNoToolScope(this.config.agentScope)) {
593199
593270
  this.proactiveGatherer = createGatherer(textForAgent);
593200
593271
  const question = this.proactiveGatherer.getCurrentQuestion();
593201
593272
  if (question) {
@@ -593217,7 +593288,7 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
593217
593288
  this.kanban.moveTask(this.currentBmadTask.id, "ready");
593218
593289
  this.kanban.moveTask(this.currentBmadTask.id, "in_progress");
593219
593290
  }
593220
- if (!this.proactiveGatherer && !quickPrompt) {
593291
+ if (!this.proactiveGatherer && !quickPrompt && !smallTalkTurn) {
593221
593292
  await this.tryRunPreToolRouter(textForAgent);
593222
593293
  }
593223
593294
  const needsPlanningGate2 = !isVerbatimScope(this.config.agentScope) && needsPlanningGate(textForAgent);
@@ -593362,9 +593433,9 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
593362
593433
  "search_symbols",
593363
593434
  "recall"
593364
593435
  ]);
593365
- const scopedTools = isNoToolScope(this.config.agentScope) ? {} : this.config.agentScope === "__table__" ? Object.fromEntries(Object.entries(providerTools).filter(([k]) => TABLE_SESSION_TOOLS.has(k))) : providerTools;
593436
+ const scopedTools = isNoToolScope(this.config.agentScope) || smallTalkTurn ? {} : this.config.agentScope === "__table__" ? Object.fromEntries(Object.entries(providerTools).filter(([k]) => TABLE_SESSION_TOOLS.has(k))) : providerTools;
593366
593437
  const withExtensions = { ...scopedTools };
593367
- if (this.extensionToolsAllowed()) {
593438
+ if (this.extensionToolsAllowed() && !smallTalkTurn) {
593368
593439
  for (const t of this.extensionHost.listTools()) {
593369
593440
  if (t.name in withExtensions)
593370
593441
  continue;
@@ -593376,7 +593447,7 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
593376
593447
  }
593377
593448
  }
593378
593449
  const effectiveTools = this.modeFilter(withExtensions);
593379
- const textToolAllowlist = (isNoToolScope(this.config.agentScope) ? [] : this.config.agentScope === "__table__" ? [...TABLE_SESSION_TOOLS] : localCoreTools).filter((name) => getModeManager().isToolAllowed(name));
593450
+ const textToolAllowlist = (isNoToolScope(this.config.agentScope) || smallTalkTurn ? [] : this.config.agentScope === "__table__" ? [...TABLE_SESSION_TOOLS] : localCoreTools).filter((name) => getModeManager().isToolAllowed(name));
593380
593451
  const harness = this.harnessProfileFor(providerConfig.model, isLocalProvider);
593381
593452
  Object.assign(this.runtimeParams, {
593382
593453
  voiceChatActive: getRuntimeParams().voiceChatActive,
@@ -593401,7 +593472,8 @@ Ask the user naturally; don't mention this instruction. After they answer, proce
593401
593472
  `),
593402
593473
  localCoreTools: textToolAllowlist,
593403
593474
  chatStartTime,
593404
- textForAgent
593475
+ textForAgent,
593476
+ smallTalk: smallTalkTurn
593405
593477
  });
593406
593478
  await this.readServerContextWindow();
593407
593479
  await this.compactIfNeeded(providerConfig);
@@ -594086,7 +594158,7 @@ ${summary || " (no provider errors recorded)"}`);
594086
594158
  };
594087
594159
  }
594088
594160
  const blankNative = gatedNative.content.trim() === "";
594089
- const content = blankNative ? emptyReplyNote(this.turnToolLedger.length) : gatedNative.content;
594161
+ const content = blankNative ? emptyReplyNote(this.turnToolLedger.length, this.turnToolLedger) : gatedNative.content;
594090
594162
  const flavor = voice.getFlavor("complete");
594091
594163
  const flavoredContent = fixCompletedLine(gatedNative.violated || isVerbatimScope(this.config.agentScope) ? content : flavorResponse(content, flavor)).text;
594092
594164
  this.messageHistory.push({ role: "assistant", content: flavoredContent });
@@ -594801,6 +594873,7 @@ var init_agent2 = __esm(() => {
594801
594873
  init_modes();
594802
594874
  init_planning_gate();
594803
594875
  init_registry();
594876
+ init_small_talk();
594804
594877
  init_verify_gate();
594805
594878
  init_local_tool_scope();
594806
594879
  init_local_turn_error();
@@ -595296,7 +595369,7 @@ var init_install = __esm(() => {
595296
595369
 
595297
595370
  // packages/eight/source-update.ts
595298
595371
  import { execFile as execFile7 } from "node:child_process";
595299
- import { existsSync as existsSync110, realpathSync as realpathSync36, statSync as statSync37 } from "node:fs";
595372
+ import { existsSync as existsSync111, realpathSync as realpathSync36, statSync as statSync37 } from "node:fs";
595300
595373
  import { homedir as homedir84 } from "node:os";
595301
595374
  import * as nodePath from "node:path";
595302
595375
  function scrubbedEnv(base) {
@@ -595395,7 +595468,7 @@ function modulePathToFs(modulePath) {
595395
595468
  }
595396
595469
  return s;
595397
595470
  }
595398
- function findCheckoutRoot(modulePath, platform, hasGit = (d) => existsSync110(lib(platform).join(d, ".git"))) {
595471
+ function findCheckoutRoot(modulePath, platform, hasGit = (d) => existsSync111(lib(platform).join(d, ".git"))) {
595399
595472
  const p = lib(platform);
595400
595473
  const fsPath = modulePathToFs(modulePath);
595401
595474
  let dir = p.dirname(platform === "win32" ? fsPath.replace(/\//g, "\\") : fsPath);
@@ -595411,7 +595484,7 @@ function findCheckoutRoot(modulePath, platform, hasGit = (d) => existsSync110(li
595411
595484
  }
595412
595485
  function findBrowserCheckout(o) {
595413
595486
  const p = lib(o.platform);
595414
- const hasGit = o.hasGit ?? ((d) => existsSync110(p.join(d, ".git")));
595487
+ const hasGit = o.hasGit ?? ((d) => existsSync111(p.join(d, ".git")));
595415
595488
  const looked = [];
595416
595489
  let note;
595417
595490
  const cands = [];
@@ -595737,7 +595810,7 @@ async function runSourceUpdate(deps, opts, report) {
595737
595810
  git,
595738
595811
  bun: bun ?? "bun",
595739
595812
  platform,
595740
- fileExists: deps.fileExists ?? ((f) => existsSync110(f)),
595813
+ fileExists: deps.fileExists ?? ((f) => existsSync111(f)),
595741
595814
  now: deps.now ?? (() => new Date),
595742
595815
  baseEnv: env,
595743
595816
  forgeHost: env.EIGHT_FORGE_HOST,
@@ -596183,7 +596256,7 @@ var init_update_notice = __esm(() => {
596183
596256
  });
596184
596257
 
596185
596258
  // packages/voice/cloud-transcriber.ts
596186
- import { existsSync as existsSync111 } from "node:fs";
596259
+ import { existsSync as existsSync112 } from "node:fs";
596187
596260
  async function transcribeCloud(wavPath, options) {
596188
596261
  const {
596189
596262
  apiKey,
@@ -596195,7 +596268,7 @@ async function transcribeCloud(wavPath, options) {
596195
596268
  if (!apiKey) {
596196
596269
  throw new Error("OpenAI API key is required for cloud transcription. Set OPENAI_API_KEY environment variable.");
596197
596270
  }
596198
- if (!existsSync111(wavPath)) {
596271
+ if (!existsSync112(wavPath)) {
596199
596272
  throw new Error(`WAV file not found: ${wavPath}`);
596200
596273
  }
596201
596274
  const startTime = Date.now();
@@ -596299,11 +596372,11 @@ var init_types4 = __esm(() => {
596299
596372
 
596300
596373
  // packages/voice/model-manager.ts
596301
596374
  import { EventEmitter as EventEmitter15 } from "node:events";
596302
- import { existsSync as existsSync112, mkdirSync as mkdirSync80, realpathSync as realpathSync37, unlinkSync as unlinkSync3 } from "node:fs";
596375
+ import { existsSync as existsSync113, mkdirSync as mkdirSync80, realpathSync as realpathSync37, unlinkSync as unlinkSync3 } from "node:fs";
596303
596376
  import { homedir as homedir85 } from "node:os";
596304
596377
  import { join as join169, sep as sep30 } from "node:path";
596305
596378
  function resolveWhisperModel(opts = {}) {
596306
- const exists = opts.exists ?? existsSync112;
596379
+ const exists = opts.exists ?? existsSync113;
596307
596380
  const dirs = [
596308
596381
  resolveModelsPath(opts.modelsDir ?? "~/.8gent/models/whisper"),
596309
596382
  ...opts.extraDirs ?? []
@@ -596364,7 +596437,7 @@ var init_model_manager = __esm(() => {
596364
596437
  this.modelsDir = resolveModelsPath(modelsPath);
596365
596438
  }
596366
596439
  ensureModelsDir() {
596367
- if (!existsSync112(this.modelsDir)) {
596440
+ if (!existsSync113(this.modelsDir)) {
596368
596441
  mkdirSync80(this.modelsDir, { recursive: true });
596369
596442
  }
596370
596443
  }
@@ -596378,7 +596451,7 @@ var init_model_manager = __esm(() => {
596378
596451
  return join169(this.modelsDir, WHISPER_MODELS[name].filename);
596379
596452
  }
596380
596453
  isModelDownloaded(name) {
596381
- return existsSync112(this.getModelPath(name));
596454
+ return existsSync113(this.getModelPath(name));
596382
596455
  }
596383
596456
  listModels() {
596384
596457
  return Object.keys(WHISPER_MODELS).map((name) => ({
@@ -596393,7 +596466,7 @@ var init_model_manager = __esm(() => {
596393
596466
  async downloadModel(name) {
596394
596467
  const info = WHISPER_MODELS[name];
596395
596468
  const destPath = this.getModelPath(name);
596396
- if (existsSync112(destPath)) {
596469
+ if (existsSync113(destPath)) {
596397
596470
  return destPath;
596398
596471
  }
596399
596472
  this.ensureModelsDir();
@@ -596444,7 +596517,7 @@ var init_model_manager = __esm(() => {
596444
596517
  this.emit("download-complete", { model: name, path: destPath });
596445
596518
  return destPath;
596446
596519
  } catch (err) {
596447
- if (existsSync112(destPath)) {
596520
+ if (existsSync113(destPath)) {
596448
596521
  try {
596449
596522
  unlinkSync3(destPath);
596450
596523
  } catch {}
@@ -596456,7 +596529,7 @@ var init_model_manager = __esm(() => {
596456
596529
  }
596457
596530
  deleteModel(name) {
596458
596531
  const path = this.getModelPath(name);
596459
- if (existsSync112(path)) {
596532
+ if (existsSync113(path)) {
596460
596533
  unlinkSync3(path);
596461
596534
  return true;
596462
596535
  }
@@ -596467,7 +596540,7 @@ var init_model_manager = __esm(() => {
596467
596540
  let totalBytes = 0;
596468
596541
  for (const name of Object.keys(WHISPER_MODELS)) {
596469
596542
  const path = this.getModelPath(name);
596470
- if (existsSync112(path)) {
596543
+ if (existsSync113(path)) {
596471
596544
  try {
596472
596545
  const stat = Bun.file(path);
596473
596546
  models[name] = stat.size;
@@ -596532,7 +596605,7 @@ var init_termux = __esm(() => {
596532
596605
 
596533
596606
  // packages/voice/recorder.ts
596534
596607
  import { EventEmitter as EventEmitter16 } from "node:events";
596535
- import { closeSync as closeSync21, existsSync as existsSync113, openSync as openSync21, readSync as readSync13, statSync as statSync38, unlinkSync as unlinkSync4 } from "node:fs";
596608
+ import { closeSync as closeSync21, existsSync as existsSync114, openSync as openSync21, readSync as readSync13, statSync as statSync38, unlinkSync as unlinkSync4 } from "node:fs";
596536
596609
  import { tmpdir as tmpdir20 } from "node:os";
596537
596610
  import { join as join171 } from "node:path";
596538
596611
  async function findSoxPath() {
@@ -596734,7 +596807,7 @@ var init_recorder = __esm(() => {
596734
596807
  }
596735
596808
  static cleanupFile(path) {
596736
596809
  try {
596737
- if (existsSync113(path)) {
596810
+ if (existsSync114(path)) {
596738
596811
  unlinkSync4(path);
596739
596812
  }
596740
596813
  } catch {}
@@ -596754,7 +596827,7 @@ var init_recorder = __esm(() => {
596754
596827
  });
596755
596828
 
596756
596829
  // packages/voice/transcriber.ts
596757
- import { existsSync as existsSync114 } from "node:fs";
596830
+ import { existsSync as existsSync115 } from "node:fs";
596758
596831
  import { arch as arch2, homedir as homedir86, platform as platform9 } from "node:os";
596759
596832
  import { join as join172 } from "node:path";
596760
596833
  function getWhisperBinaryPath() {
@@ -596762,7 +596835,7 @@ function getWhisperBinaryPath() {
596762
596835
  }
596763
596836
  async function findWhisperBinary() {
596764
596837
  const managedPath = getWhisperBinaryPath();
596765
- if (existsSync114(managedPath)) {
596838
+ if (existsSync115(managedPath)) {
596766
596839
  return managedPath;
596767
596840
  }
596768
596841
  const names = ["whisper-cpp", "whisper-cli", "whisper", "main"];
@@ -596777,7 +596850,7 @@ async function findWhisperBinary() {
596777
596850
  } catch {}
596778
596851
  }
596779
596852
  for (const brewName of ["/opt/homebrew/bin/whisper-cpp", "/opt/homebrew/bin/whisper-cli"]) {
596780
- if (existsSync114(brewName)) {
596853
+ if (existsSync115(brewName)) {
596781
596854
  return brewName;
596782
596855
  }
596783
596856
  }
@@ -596785,13 +596858,13 @@ async function findWhisperBinary() {
596785
596858
  }
596786
596859
  async function transcribeLocal(wavPath, options) {
596787
596860
  const { binaryPath, modelPath, language = "en", timeoutMs = 30000, threads } = options;
596788
- if (!existsSync114(wavPath)) {
596861
+ if (!existsSync115(wavPath)) {
596789
596862
  throw new Error(`WAV file not found: ${wavPath}`);
596790
596863
  }
596791
- if (!existsSync114(binaryPath)) {
596864
+ if (!existsSync115(binaryPath)) {
596792
596865
  throw new Error(`Whisper binary not found: ${binaryPath}`);
596793
596866
  }
596794
- if (!existsSync114(modelPath)) {
596867
+ if (!existsSync115(modelPath)) {
596795
596868
  throw new Error(`Model file not found: ${modelPath}`);
596796
596869
  }
596797
596870
  const startTime = Date.now();
@@ -597068,7 +597141,7 @@ var init_neudecide = __esm(() => {
597068
597141
 
597069
597142
  // packages/runtime/node-runtime.ts
597070
597143
  import { execFile as execFile8 } from "node:child_process";
597071
- import { existsSync as existsSync119 } from "node:fs";
597144
+ import { existsSync as existsSync120 } from "node:fs";
597072
597145
  import { homedir as homedir88 } from "node:os";
597073
597146
  import { join as join179 } from "node:path";
597074
597147
  import { promisify as promisify6 } from "node:util";
@@ -597131,7 +597204,7 @@ async function isRuntimeReady(opts) {
597131
597204
  version: opts.version,
597132
597205
  platform
597133
597206
  });
597134
- if (!existsSync119(bin))
597207
+ if (!existsSync120(bin))
597135
597208
  return false;
597136
597209
  const min = opts.minVersion ?? opts.version;
597137
597210
  try {
@@ -597169,7 +597242,7 @@ var init_node_runtime = __esm(() => {
597169
597242
  import { spawn as spawn23 } from "node:child_process";
597170
597243
  import {
597171
597244
  createWriteStream,
597172
- existsSync as existsSync120,
597245
+ existsSync as existsSync121,
597173
597246
  mkdirSync as mkdirSync83,
597174
597247
  mkdtempSync as mkdtempSync12,
597175
597248
  readdirSync as readdirSync27,
@@ -597230,7 +597303,7 @@ async function installRuntime(input) {
597230
597303
  throw new Error(`could not find node-* dir inside ${extractRoot}`);
597231
597304
  }
597232
597305
  const extractedPath = join180(extractRoot, nodeDir);
597233
- if (existsSync120(plan.runtimeDir)) {
597306
+ if (existsSync121(plan.runtimeDir)) {
597234
597307
  rmSync17(plan.runtimeDir, { recursive: true, force: true });
597235
597308
  }
597236
597309
  renameSync17(extractedPath, plan.runtimeDir);
@@ -599212,7 +599285,7 @@ Nothing was changed. Type /update again to apply.`);
599212
599285
  // apps/tui/src/lib/telegram-setup.ts
599213
599286
  import {
599214
599287
  chmodSync as chmodSync6,
599215
- existsSync as existsSync135,
599288
+ existsSync as existsSync136,
599216
599289
  lstatSync as lstatSync31,
599217
599290
  mkdirSync as mkdirSync96,
599218
599291
  readFileSync as readFileSync121,
@@ -599334,7 +599407,7 @@ async function getSeenChats(key) {
599334
599407
  function mergeSettings(chatIds, envFile, tokenVar) {
599335
599408
  const path = join201(homeDir4(), ".8gent", "settings.json");
599336
599409
  let settings = {};
599337
- if (existsSync135(path)) {
599410
+ if (existsSync136(path)) {
599338
599411
  try {
599339
599412
  const parsed = JSON.parse(readFileSync121(path, "utf8"));
599340
599413
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
@@ -599701,7 +599774,7 @@ Personal 8gent mini-app. Iteration is driven by \`SKILL.md\`. Run tests with \`b
599701
599774
  }
599702
599775
 
599703
599776
  // packages/app-creator/creator.ts
599704
- import { existsSync as existsSync136, mkdirSync as mkdirSync98, writeFileSync as writeFileSync78 } from "node:fs";
599777
+ import { existsSync as existsSync137, mkdirSync as mkdirSync98, writeFileSync as writeFileSync78 } from "node:fs";
599705
599778
  import { homedir as homedir96 } from "node:os";
599706
599779
  import { dirname as dirname84, join as join203 } from "node:path";
599707
599780
  function defaultAppsRoot() {
@@ -599759,7 +599832,7 @@ function createApp(input) {
599759
599832
  return { ...draft, persisted: false, written: [] };
599760
599833
  }
599761
599834
  const dir = draft.dir;
599762
- const targetExists = existsSync136(dir);
599835
+ const targetExists = existsSync137(dir);
599763
599836
  if (targetExists && !input.allowOverwrite) {
599764
599837
  return {
599765
599838
  ...draft,
@@ -600606,7 +600679,7 @@ var init_producer = __esm(() => {
600606
600679
  });
600607
600680
 
600608
600681
  // packages/pet/companion.ts
600609
- import { existsSync as existsSync140, mkdirSync as mkdirSync104, readFileSync as readFileSync123, writeFileSync as writeFileSync80 } from "node:fs";
600682
+ import { existsSync as existsSync141, mkdirSync as mkdirSync104, readFileSync as readFileSync123, writeFileSync as writeFileSync80 } from "node:fs";
600610
600683
  import { join as join205 } from "node:path";
600611
600684
  function seedRng(seed) {
600612
600685
  let h = 0;
@@ -600750,7 +600823,7 @@ function getDeckPath() {
600750
600823
  function loadDeck() {
600751
600824
  const deckPath = getDeckPath();
600752
600825
  try {
600753
- if (existsSync140(deckPath)) {
600826
+ if (existsSync141(deckPath)) {
600754
600827
  return JSON.parse(readFileSync123(deckPath, "utf-8"));
600755
600828
  }
600756
600829
  } catch {}
@@ -622006,234 +622079,6 @@ function useImageInput(options = {}) {
622006
622079
  // apps/tui/src/components/message-list.tsx
622007
622080
  var import_react63 = __toESM(require_react(), 1);
622008
622081
 
622009
- // apps/tui/src/lib/chat-layout.ts
622010
- var SHELL_CHROME = 4;
622011
- var COLUMN_GAP = 1;
622012
- var ACTIVITY_RAIL_WIDTH = 34;
622013
- function chatColumnWidth(cols, shell, min = 24) {
622014
- let width = cols - SHELL_CHROME;
622015
- if (shell.planWidth && shell.planWidth > 0)
622016
- width -= shell.planWidth + COLUMN_GAP;
622017
- if (shell.activity)
622018
- width -= ACTIVITY_RAIL_WIDTH + COLUMN_GAP;
622019
- return Math.max(min, width);
622020
- }
622021
- var USER_BUBBLE_SHARE = 0.78;
622022
- function bubbleWidths(contentWidth, role) {
622023
- const inner = Math.max(16, contentWidth - 2);
622024
- const bubble = role === "user" ? Math.max(16, Math.floor(inner * USER_BUBBLE_SHARE)) : inner;
622025
- return { bubble, wrap: Math.max(8, bubble - 2) };
622026
- }
622027
-
622028
- // apps/tui/src/lib/tool-trail.ts
622029
- var COLLAPSE_THRESHOLD = 8;
622030
- var MAX_TRAIL_ROWS = 12;
622031
- var PATH_KEYS = ["path", "file_path", "filePath", "file", "directory", "dir"];
622032
- var COMMAND_KEYS = ["command", "cmd"];
622033
- var QUERY_KEYS = ["query", "pattern", "q", "search", "url"];
622034
- function oneLine(s) {
622035
- return s.replace(/\s+/g, " ").trim().slice(0, 200);
622036
- }
622037
- function pick3(args, keys) {
622038
- for (const k of keys) {
622039
- const v = args[k];
622040
- if (typeof v === "string" && v.trim())
622041
- return oneLine(v);
622042
- }
622043
- return null;
622044
- }
622045
- function summarizeToolArgs(tool, args) {
622046
- if (!args || typeof args !== "object")
622047
- return "";
622048
- const isCommand = /command|shell|exec|bash/.test(tool);
622049
- 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];
622050
- for (const keys of order) {
622051
- const hit = pick3(args, keys);
622052
- if (hit)
622053
- return hit;
622054
- }
622055
- for (const v of Object.values(args)) {
622056
- if (typeof v === "string" && v.trim())
622057
- return oneLine(v);
622058
- }
622059
- return "";
622060
- }
622061
- var GATE_PREFIX = /^\s*\[([A-Z0-9_ -]*\b(?:BLOCKED|DENIED)\b[A-Z0-9_ -]*)\]/;
622062
- var OTHER_BLOCK = /^\s*(?:Path blocked by path-guard|Path traversal blocked)|Hook blocked tool/;
622063
- var RULE_NAME = /\[([a-z0-9]+(?:-[a-z0-9]+)+)\]/;
622064
- function classifyToolResult(success, preview) {
622065
- const text = preview ?? "";
622066
- const gate = text.match(GATE_PREFIX);
622067
- if (gate || OTHER_BLOCK.test(text)) {
622068
- if (gate && /DENIED/.test(gate[1]))
622069
- return { status: "blocked", reason: "denied" };
622070
- const rule = text.match(RULE_NAME);
622071
- return { status: "blocked", reason: rule ? `blocked: [${rule[1]}]` : "blocked" };
622072
- }
622073
- const exit = text.match(/^\s*Exit code (-?\d+)/);
622074
- if (exit && exit[1] !== "0")
622075
- return { status: "fail", reason: `exit ${exit[1]}` };
622076
- if (!success || /^\s*(?:Error\b|Unknown tool:)/.test(text)) {
622077
- const first = (text.split(`
622078
- `).find((l) => l.trim()) ?? "").replace(/^\s*Error(?: running tool "[^"]*")?:?\s*/, "").trim();
622079
- if (!first)
622080
- return { status: "fail", reason: "error" };
622081
- return { status: "fail", reason: first.length > 40 ? `${first.slice(0, 39)}…` : first };
622082
- }
622083
- return { status: "ok" };
622084
- }
622085
- function toTrailEntry(event) {
622086
- const { status, reason } = classifyToolResult(event.success !== false, event.resultPreview);
622087
- const entry = {
622088
- tool: event.toolName,
622089
- summary: summarizeToolArgs(event.toolName, event.args),
622090
- status
622091
- };
622092
- if (reason)
622093
- entry.reason = reason;
622094
- if (event.toolName === "update_plan") {
622095
- const items = event.args?.plan;
622096
- if (Array.isArray(items)) {
622097
- 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" }));
622098
- if (plan.length > 0)
622099
- entry.plan = plan;
622100
- }
622101
- }
622102
- return entry;
622103
- }
622104
- function isReadTool(tool) {
622105
- return /^(?:read_|list_|get_|search|grep|glob|find|view|web_search|web_fetch)/.test(tool);
622106
- }
622107
- function commonDir(paths) {
622108
- const split = paths.map((p) => p.split("/").slice(0, -1));
622109
- const first = split[0] ?? [];
622110
- let n = 0;
622111
- while (n < first.length && split.every((s) => s[n] === first[n]))
622112
- n++;
622113
- return n > 0 ? `${first.slice(0, n).join("/")}/` : "";
622114
- }
622115
- function collapseTrail(entries, maxRows = MAX_TRAIL_ROWS) {
622116
- let rows = entries.map((e) => ({ ...e, count: 1 }));
622117
- if (entries.length > COLLAPSE_THRESHOLD) {
622118
- const out = [];
622119
- for (let i = 0;i < entries.length; ) {
622120
- const e = entries[i];
622121
- let j = i + 1;
622122
- if (e.status === "ok" && isReadTool(e.tool)) {
622123
- while (j < entries.length && entries[j].tool === e.tool && entries[j].status === "ok")
622124
- j++;
622125
- }
622126
- if (j - i > 1) {
622127
- const run = entries.slice(i, j);
622128
- out.push({
622129
- tool: e.tool,
622130
- status: "ok",
622131
- count: run.length,
622132
- summary: commonDir(run.map((r) => r.summary))
622133
- });
622134
- } else {
622135
- out.push({ ...e, count: 1 });
622136
- }
622137
- i = j;
622138
- }
622139
- rows = out;
622140
- }
622141
- const cap = Math.max(1, Math.floor(maxRows));
622142
- if (rows.length > cap) {
622143
- let toFold = rows.length - cap + 1;
622144
- const fold = new Set;
622145
- for (const wantOk of [true, false]) {
622146
- for (let i = 0;i < rows.length && toFold > 0; i++) {
622147
- if (fold.has(i) || wantOk && rows[i].status !== "ok")
622148
- continue;
622149
- fold.add(i);
622150
- toFold--;
622151
- }
622152
- }
622153
- const folded = rows.filter((_, i) => fold.has(i));
622154
- const count = (s) => folded.filter((r) => r.status === s).reduce((n, r) => n + r.count, 0);
622155
- const failed = count("fail");
622156
- const blocked = count("blocked");
622157
- const summary = {
622158
- tool: "",
622159
- summary: "",
622160
- status: failed > 0 ? "fail" : blocked > 0 ? "blocked" : "ok",
622161
- count: folded.reduce((n, r) => n + r.count, 0),
622162
- folded: true,
622163
- all: fold.size === rows.length
622164
- };
622165
- if (failed > 0)
622166
- summary.failed = failed;
622167
- if (blocked > 0)
622168
- summary.blocked = blocked;
622169
- rows = [summary, ...rows.filter((_, i) => !fold.has(i))];
622170
- }
622171
- return rows;
622172
- }
622173
- var ICONS = { ok: "✓", fail: "✗", blocked: "⊘" };
622174
- function formatTrailRow(row, width) {
622175
- const icon = ICONS[row.status];
622176
- const budget = Math.max(1, width - icon.length - 1);
622177
- if (row.folded) {
622178
- const parts = [`${row.count} ${row.all ? "calls" : "earlier calls"}`];
622179
- if (row.failed)
622180
- parts.push(`${row.failed} failed`);
622181
- if (row.blocked)
622182
- parts.push(`${row.blocked} blocked`);
622183
- return { icon, text: clip(parts.join(", "), budget) };
622184
- }
622185
- const head = row.count > 1 ? `${row.tool} ×${row.count}` : row.tool;
622186
- const tail = row.reason ? ` (${row.reason})` : "";
622187
- let summary = row.count > 1 ? row.summary ? `(${row.summary}…)` : "" : row.summary;
622188
- if (!summary)
622189
- return { icon, text: clip(`${head}${tail}`, budget) };
622190
- const room = budget - head.length - 1 - tail.length;
622191
- if (room < 4)
622192
- return { icon, text: clip(`${head}${tail}`, budget) };
622193
- if (summary.length > room)
622194
- summary = `${summary.slice(0, room - 1)}…`;
622195
- return { icon, text: `${head} ${summary}${tail}` };
622196
- }
622197
- function clip(s, width) {
622198
- return s.length <= width ? s : `${s.slice(0, Math.max(0, width - 1))}…`;
622199
- }
622200
- function buildChatItems(messages) {
622201
- const items = [];
622202
- let pending = [];
622203
- let pendingFirst = null;
622204
- const flush = () => {
622205
- if (pending.length > 0 && pendingFirst) {
622206
- items.push({
622207
- message: { ...pendingFirst, id: `trail-${pendingFirst.id}`, role: "tool", content: "" },
622208
- trail: pending
622209
- });
622210
- }
622211
- pending = [];
622212
- pendingFirst = null;
622213
- };
622214
- for (const m of messages) {
622215
- if (m.role === "tool") {
622216
- if (m.toolTrail) {
622217
- if (!pendingFirst)
622218
- pendingFirst = m;
622219
- pending.push(m.toolTrail);
622220
- }
622221
- continue;
622222
- }
622223
- if (m.role === "assistant") {
622224
- items.push({ message: m, trail: pending });
622225
- pending = [];
622226
- pendingFirst = null;
622227
- continue;
622228
- }
622229
- if (m.role === "user")
622230
- flush();
622231
- items.push({ message: m, trail: [] });
622232
- }
622233
- flush();
622234
- return items;
622235
- }
622236
-
622237
622082
  // apps/tui/src/hooks/useMouseScroll.ts
622238
622083
  var import_react57 = __toESM(require_react(), 1);
622239
622084
  function useMouseScroll({
@@ -622259,111 +622104,25 @@ function useMouseScroll({
622259
622104
  }, [enabled, step]);
622260
622105
  }
622261
622106
 
622262
- // apps/tui/src/components/fade-transition.tsx
622263
- var import_react58 = __toESM(require_react(), 1);
622264
- var jsx_dev_runtime30 = __toESM(require_jsx_dev_runtime(), 1);
622265
- function FadeIn({ children, duration = 300, delay = 0, onComplete }) {
622266
- const [started, setStarted] = import_react58.useState(false);
622267
- import_react58.useEffect(() => {
622268
- const timeout = setTimeout(() => {
622269
- setStarted(true);
622270
- }, delay);
622271
- return () => clearTimeout(timeout);
622272
- }, [delay]);
622273
- import_react58.useEffect(() => {
622274
- if (!started)
622275
- return;
622276
- const timeout = setTimeout(() => {
622277
- onComplete?.();
622278
- }, duration);
622279
- return () => clearTimeout(timeout);
622280
- }, [started, duration, onComplete]);
622281
- if (!started)
622282
- return null;
622283
- return /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Box_default, {
622284
- children
622285
- }, undefined, false, undefined, this);
622286
- }
622287
-
622288
- // apps/tui/src/components/ToolTrail.tsx
622289
- var import_react59 = __toESM(require_react(), 1);
622290
- var jsx_dev_runtime31 = __toESM(require_jsx_dev_runtime(), 1);
622291
- function iconColor(status) {
622292
- return status === "ok" ? t.green : status === "fail" ? t.red : t.orange;
622293
- }
622294
- function useLandingRows(total, animate) {
622295
- const [shown, setShown] = import_react59.useState(total);
622296
- const shownRef = import_react59.useRef(total);
622297
- shownRef.current = shown;
622298
- const landingRef = import_react59.useRef(null);
622299
- import_react59.useEffect(() => {
622300
- const from = shownRef.current;
622301
- if (total <= from + 1 || !motionEnabled(animate)) {
622302
- landingRef.current = null;
622303
- setShown(total);
622304
- return;
622305
- }
622306
- landingRef.current = total;
622307
- const step = staggerFor(total - from);
622308
- let n = from + 1;
622309
- setShown(n);
622310
- const id = setInterval(() => {
622311
- n += 1;
622312
- setShown(Math.min(total, n));
622313
- if (n >= total) {
622314
- landingRef.current = null;
622315
- clearInterval(id);
622316
- }
622317
- }, step);
622318
- return () => clearInterval(id);
622319
- }, [total, animate]);
622320
- if (landingRef.current === total)
622321
- return Math.min(shown, total);
622322
- if (total <= shown + 1)
622323
- return total;
622324
- return shown;
622325
- }
622326
- function ToolTrail({
622327
- entries,
622328
- width,
622329
- maxRows,
622330
- animate = true
622331
- }) {
622332
- const rows = collapseTrail(entries, maxRows);
622333
- const visible = useLandingRows(rows.length, animate);
622334
- if (rows.length === 0)
622335
- return null;
622336
- return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
622337
- flexDirection: "column",
622338
- flexShrink: 0,
622339
- children: rows.slice(0, visible).map((row, i) => {
622340
- const { text } = formatTrailRow(row, width);
622341
- const g = glyphs();
622342
- const icon = row.status === "ok" ? g.ok : row.status === "fail" ? g.fail : g.blocked;
622343
- return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
622344
- width,
622345
- children: [
622346
- /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
622347
- color: iconColor(row.status),
622348
- children: icon
622349
- }, undefined, false, undefined, this),
622350
- /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
622351
- color: row.status === "ok" ? t.muted : t.textPrimary,
622352
- wrap: "truncate-end",
622353
- children: ` ${text}`
622354
- }, undefined, false, undefined, this)
622355
- ]
622356
- }, i, true, undefined, this);
622357
- })
622358
- }, undefined, false, undefined, this);
622107
+ // apps/tui/src/lib/chat-layout.ts
622108
+ var SHELL_CHROME = 4;
622109
+ var COLUMN_GAP = 1;
622110
+ var ACTIVITY_RAIL_WIDTH = 34;
622111
+ function chatColumnWidth(cols, shell, min = 24) {
622112
+ let width = cols - SHELL_CHROME;
622113
+ if (shell.planWidth && shell.planWidth > 0)
622114
+ width -= shell.planWidth + COLUMN_GAP;
622115
+ if (shell.activity)
622116
+ width -= ACTIVITY_RAIL_WIDTH + COLUMN_GAP;
622117
+ return Math.max(min, width);
622359
622118
  }
622360
- function toolTrailRows(entries, maxRows) {
622361
- return entries.length === 0 ? 0 : collapseTrail(entries, maxRows).length;
622119
+ var USER_BUBBLE_SHARE = 0.78;
622120
+ function bubbleWidths(contentWidth, role) {
622121
+ const inner = Math.max(16, contentWidth - 2);
622122
+ const bubble = role === "user" ? Math.max(16, Math.floor(inner * USER_BUBBLE_SHARE)) : inner;
622123
+ return { bubble, wrap: Math.max(8, bubble - 2) };
622362
622124
  }
622363
622125
 
622364
- // apps/tui/src/components/TurnResults.tsx
622365
- var import_react60 = __toESM(require_react(), 1);
622366
-
622367
622126
  // apps/tui/src/lib/inline-markdown.ts
622368
622127
  var MAX_DEPTH3 = 3;
622369
622128
  var INDENT_STEP = 2;
@@ -622678,6 +622437,215 @@ function blockRows(block, width) {
622678
622437
  }
622679
622438
  }
622680
622439
 
622440
+ // apps/tui/src/lib/tool-trail.ts
622441
+ var COLLAPSE_THRESHOLD = 8;
622442
+ var MAX_TRAIL_ROWS = 12;
622443
+ var PATH_KEYS = ["path", "file_path", "filePath", "file", "directory", "dir"];
622444
+ var COMMAND_KEYS = ["command", "cmd"];
622445
+ var QUERY_KEYS = ["query", "pattern", "q", "search", "url"];
622446
+ function oneLine(s) {
622447
+ return s.replace(/\s+/g, " ").trim().slice(0, 200);
622448
+ }
622449
+ function pick3(args, keys) {
622450
+ for (const k of keys) {
622451
+ const v = args[k];
622452
+ if (typeof v === "string" && v.trim())
622453
+ return oneLine(v);
622454
+ }
622455
+ return null;
622456
+ }
622457
+ function summarizeToolArgs(tool, args) {
622458
+ if (!args || typeof args !== "object")
622459
+ return "";
622460
+ const isCommand = /command|shell|exec|bash/.test(tool);
622461
+ 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];
622462
+ for (const keys of order) {
622463
+ const hit = pick3(args, keys);
622464
+ if (hit)
622465
+ return hit;
622466
+ }
622467
+ for (const v of Object.values(args)) {
622468
+ if (typeof v === "string" && v.trim())
622469
+ return oneLine(v);
622470
+ }
622471
+ return "";
622472
+ }
622473
+ var GATE_PREFIX = /^\s*\[([A-Z0-9_ -]*\b(?:BLOCKED|DENIED)\b[A-Z0-9_ -]*)\]/;
622474
+ var OTHER_BLOCK = /^\s*(?:Path blocked by path-guard|Path traversal blocked)|Hook blocked tool/;
622475
+ var RULE_NAME = /\[([a-z0-9]+(?:-[a-z0-9]+)+)\]/;
622476
+ function classifyToolResult(success, preview) {
622477
+ const text = preview ?? "";
622478
+ const gate = text.match(GATE_PREFIX);
622479
+ if (gate || OTHER_BLOCK.test(text)) {
622480
+ if (gate && /DENIED/.test(gate[1]))
622481
+ return { status: "blocked", reason: "denied" };
622482
+ const rule = text.match(RULE_NAME);
622483
+ return { status: "blocked", reason: rule ? `blocked: [${rule[1]}]` : "blocked" };
622484
+ }
622485
+ const exit = text.match(/^\s*Exit code (-?\d+)/);
622486
+ if (exit && exit[1] !== "0")
622487
+ return { status: "fail", reason: `exit ${exit[1]}` };
622488
+ if (!success || /^\s*(?:Error\b|Unknown tool:)/.test(text)) {
622489
+ const first = (text.split(`
622490
+ `).find((l) => l.trim()) ?? "").replace(/^\s*Error(?: running tool "[^"]*")?:?\s*/, "").trim();
622491
+ if (!first)
622492
+ return { status: "fail", reason: "error" };
622493
+ return { status: "fail", reason: first.length > 40 ? `${first.slice(0, 39)}…` : first };
622494
+ }
622495
+ return { status: "ok" };
622496
+ }
622497
+ function toTrailEntry(event) {
622498
+ const { status, reason } = classifyToolResult(event.success !== false, event.resultPreview);
622499
+ const entry = {
622500
+ tool: event.toolName,
622501
+ summary: summarizeToolArgs(event.toolName, event.args),
622502
+ status
622503
+ };
622504
+ if (reason)
622505
+ entry.reason = reason;
622506
+ if (event.toolName === "update_plan") {
622507
+ const items = event.args?.plan;
622508
+ if (Array.isArray(items)) {
622509
+ 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" }));
622510
+ if (plan.length > 0)
622511
+ entry.plan = plan;
622512
+ }
622513
+ }
622514
+ return entry;
622515
+ }
622516
+ function isReadTool(tool) {
622517
+ return /^(?:read_|list_|get_|search|grep|glob|find|view|web_search|web_fetch)/.test(tool);
622518
+ }
622519
+ function commonDir(paths) {
622520
+ const split = paths.map((p) => p.split("/").slice(0, -1));
622521
+ const first = split[0] ?? [];
622522
+ let n = 0;
622523
+ while (n < first.length && split.every((s) => s[n] === first[n]))
622524
+ n++;
622525
+ return n > 0 ? `${first.slice(0, n).join("/")}/` : "";
622526
+ }
622527
+ function collapseTrail(entries, maxRows = MAX_TRAIL_ROWS) {
622528
+ let rows = entries.map((e) => ({ ...e, count: 1 }));
622529
+ if (entries.length > COLLAPSE_THRESHOLD) {
622530
+ const out = [];
622531
+ for (let i = 0;i < entries.length; ) {
622532
+ const e = entries[i];
622533
+ let j = i + 1;
622534
+ if (e.status === "ok" && isReadTool(e.tool)) {
622535
+ while (j < entries.length && entries[j].tool === e.tool && entries[j].status === "ok")
622536
+ j++;
622537
+ }
622538
+ if (j - i > 1) {
622539
+ const run = entries.slice(i, j);
622540
+ out.push({
622541
+ tool: e.tool,
622542
+ status: "ok",
622543
+ count: run.length,
622544
+ summary: commonDir(run.map((r) => r.summary))
622545
+ });
622546
+ } else {
622547
+ out.push({ ...e, count: 1 });
622548
+ }
622549
+ i = j;
622550
+ }
622551
+ rows = out;
622552
+ }
622553
+ const cap = Math.max(1, Math.floor(maxRows));
622554
+ if (rows.length > cap) {
622555
+ let toFold = rows.length - cap + 1;
622556
+ const fold = new Set;
622557
+ for (const wantOk of [true, false]) {
622558
+ for (let i = 0;i < rows.length && toFold > 0; i++) {
622559
+ if (fold.has(i) || wantOk && rows[i].status !== "ok")
622560
+ continue;
622561
+ fold.add(i);
622562
+ toFold--;
622563
+ }
622564
+ }
622565
+ const folded = rows.filter((_, i) => fold.has(i));
622566
+ const count = (s) => folded.filter((r) => r.status === s).reduce((n, r) => n + r.count, 0);
622567
+ const failed = count("fail");
622568
+ const blocked = count("blocked");
622569
+ const summary = {
622570
+ tool: "",
622571
+ summary: "",
622572
+ status: failed > 0 ? "fail" : blocked > 0 ? "blocked" : "ok",
622573
+ count: folded.reduce((n, r) => n + r.count, 0),
622574
+ folded: true,
622575
+ all: fold.size === rows.length
622576
+ };
622577
+ if (failed > 0)
622578
+ summary.failed = failed;
622579
+ if (blocked > 0)
622580
+ summary.blocked = blocked;
622581
+ rows = [summary, ...rows.filter((_, i) => !fold.has(i))];
622582
+ }
622583
+ return rows;
622584
+ }
622585
+ var ICONS = { ok: "✓", fail: "✗", blocked: "⊘" };
622586
+ function formatTrailRow(row, width) {
622587
+ const icon = ICONS[row.status];
622588
+ const budget = Math.max(1, width - icon.length - 1);
622589
+ if (row.folded) {
622590
+ const parts = [`${row.count} ${row.all ? "calls" : "earlier calls"}`];
622591
+ if (row.failed)
622592
+ parts.push(`${row.failed} failed`);
622593
+ if (row.blocked)
622594
+ parts.push(`${row.blocked} blocked`);
622595
+ return { icon, text: clip(parts.join(", "), budget) };
622596
+ }
622597
+ const head = row.count > 1 ? `${row.tool} ×${row.count}` : row.tool;
622598
+ const tail = row.reason ? ` (${row.reason})` : "";
622599
+ let summary = row.count > 1 ? row.summary ? `(${row.summary}…)` : "" : row.summary;
622600
+ if (!summary)
622601
+ return { icon, text: clip(`${head}${tail}`, budget) };
622602
+ const room = budget - head.length - 1 - tail.length;
622603
+ if (room < 4)
622604
+ return { icon, text: clip(`${head}${tail}`, budget) };
622605
+ if (summary.length > room)
622606
+ summary = `${summary.slice(0, room - 1)}…`;
622607
+ return { icon, text: `${head} ${summary}${tail}` };
622608
+ }
622609
+ function clip(s, width) {
622610
+ return s.length <= width ? s : `${s.slice(0, Math.max(0, width - 1))}…`;
622611
+ }
622612
+ function buildChatItems(messages) {
622613
+ const items = [];
622614
+ let pending = [];
622615
+ let pendingFirst = null;
622616
+ const flush = () => {
622617
+ if (pending.length > 0 && pendingFirst) {
622618
+ items.push({
622619
+ message: { ...pendingFirst, id: `trail-${pendingFirst.id}`, role: "tool", content: "" },
622620
+ trail: pending
622621
+ });
622622
+ }
622623
+ pending = [];
622624
+ pendingFirst = null;
622625
+ };
622626
+ for (const m of messages) {
622627
+ if (m.role === "tool") {
622628
+ if (m.toolTrail) {
622629
+ if (!pendingFirst)
622630
+ pendingFirst = m;
622631
+ pending.push(m.toolTrail);
622632
+ }
622633
+ continue;
622634
+ }
622635
+ if (m.role === "assistant") {
622636
+ items.push({ message: m, trail: pending });
622637
+ pending = [];
622638
+ pendingFirst = null;
622639
+ continue;
622640
+ }
622641
+ if (m.role === "user")
622642
+ flush();
622643
+ items.push({ message: m, trail: [] });
622644
+ }
622645
+ flush();
622646
+ return items;
622647
+ }
622648
+
622681
622649
  // apps/tui/src/lib/activity-rail-derivation.ts
622682
622650
  function deriveTools(messages, isProcessing, limit = 5) {
622683
622651
  const ended = new Map;
@@ -623149,8 +623117,85 @@ function turnResultRows(trail, maxRows, plan) {
623149
623117
  return buildTurnResults(trail, maxRows, plan).length;
623150
623118
  }
623151
623119
 
623120
+ // apps/tui/src/components/ToolTrail.tsx
623121
+ var import_react58 = __toESM(require_react(), 1);
623122
+ var jsx_dev_runtime30 = __toESM(require_jsx_dev_runtime(), 1);
623123
+ function iconColor(status) {
623124
+ return status === "ok" ? t.green : status === "fail" ? t.red : t.orange;
623125
+ }
623126
+ function useLandingRows(total, animate) {
623127
+ const [shown, setShown] = import_react58.useState(total);
623128
+ const shownRef = import_react58.useRef(total);
623129
+ shownRef.current = shown;
623130
+ const landingRef = import_react58.useRef(null);
623131
+ import_react58.useEffect(() => {
623132
+ const from = shownRef.current;
623133
+ if (total <= from + 1 || !motionEnabled(animate)) {
623134
+ landingRef.current = null;
623135
+ setShown(total);
623136
+ return;
623137
+ }
623138
+ landingRef.current = total;
623139
+ const step = staggerFor(total - from);
623140
+ let n = from + 1;
623141
+ setShown(n);
623142
+ const id = setInterval(() => {
623143
+ n += 1;
623144
+ setShown(Math.min(total, n));
623145
+ if (n >= total) {
623146
+ landingRef.current = null;
623147
+ clearInterval(id);
623148
+ }
623149
+ }, step);
623150
+ return () => clearInterval(id);
623151
+ }, [total, animate]);
623152
+ if (landingRef.current === total)
623153
+ return Math.min(shown, total);
623154
+ if (total <= shown + 1)
623155
+ return total;
623156
+ return shown;
623157
+ }
623158
+ function ToolTrail({
623159
+ entries,
623160
+ width,
623161
+ maxRows,
623162
+ animate = true
623163
+ }) {
623164
+ const rows = collapseTrail(entries, maxRows);
623165
+ const visible = useLandingRows(rows.length, animate);
623166
+ if (rows.length === 0)
623167
+ return null;
623168
+ return /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Box_default, {
623169
+ flexDirection: "column",
623170
+ flexShrink: 0,
623171
+ children: rows.slice(0, visible).map((row, i) => {
623172
+ const { text } = formatTrailRow(row, width);
623173
+ const g = glyphs();
623174
+ const icon = row.status === "ok" ? g.ok : row.status === "fail" ? g.fail : g.blocked;
623175
+ return /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Box_default, {
623176
+ width,
623177
+ children: [
623178
+ /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Text, {
623179
+ color: iconColor(row.status),
623180
+ children: icon
623181
+ }, undefined, false, undefined, this),
623182
+ /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Text, {
623183
+ color: row.status === "ok" ? t.muted : t.textPrimary,
623184
+ wrap: "truncate-end",
623185
+ children: ` ${text}`
623186
+ }, undefined, false, undefined, this)
623187
+ ]
623188
+ }, i, true, undefined, this);
623189
+ })
623190
+ }, undefined, false, undefined, this);
623191
+ }
623192
+ function toolTrailRows(entries, maxRows) {
623193
+ return entries.length === 0 ? 0 : collapseTrail(entries, maxRows).length;
623194
+ }
623195
+
623152
623196
  // apps/tui/src/components/TurnResults.tsx
623153
- var jsx_dev_runtime32 = __toESM(require_jsx_dev_runtime(), 1);
623197
+ var import_react59 = __toESM(require_react(), 1);
623198
+ var jsx_dev_runtime31 = __toESM(require_jsx_dev_runtime(), 1);
623154
623199
  function iconFor(status) {
623155
623200
  const g = glyphs();
623156
623201
  if (status === "ok")
@@ -623162,8 +623207,8 @@ function iconFor(status) {
623162
623207
  return { icon: g.pending, color: t.muted };
623163
623208
  }
623164
623209
  function useResultLanding(total, land) {
623165
- const [shown, setShown] = import_react60.useState(land ? 0 : total);
623166
- import_react60.useEffect(() => {
623210
+ const [shown, setShown] = import_react59.useState(land ? 0 : total);
623211
+ import_react59.useEffect(() => {
623167
623212
  if (!land) {
623168
623213
  setShown(total);
623169
623214
  return;
@@ -623203,12 +623248,12 @@ function TurnResults({
623203
623248
  const visible = useResultLanding(rows.length, land && motionEnabled(animate));
623204
623249
  if (rows.length === 0)
623205
623250
  return null;
623206
- return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
623251
+ return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
623207
623252
  flexDirection: "column",
623208
623253
  flexShrink: 0,
623209
623254
  children: rows.map((row, i) => {
623210
623255
  if (i >= visible) {
623211
- return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
623256
+ return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
623212
623257
  height: 1,
623213
623258
  width
623214
623259
  }, i, false, undefined, this);
@@ -623216,55 +623261,55 @@ function TurnResults({
623216
623261
  const { icon, color } = iconFor(row.status);
623217
623262
  const fit = fitResultRow(row, width);
623218
623263
  const quiet = row.status === "pending" || row.kind === "fold";
623219
- return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
623264
+ return /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Box_default, {
623220
623265
  width,
623221
623266
  height: 1,
623222
- children: /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623267
+ children: /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623223
623268
  wrap: "truncate-end",
623224
623269
  children: [
623225
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623270
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623226
623271
  color,
623227
623272
  children: icon
623228
623273
  }, undefined, false, undefined, this),
623229
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623274
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623230
623275
  children: " "
623231
623276
  }, undefined, false, undefined, this),
623232
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623277
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623233
623278
  bold: !quiet,
623234
623279
  color: quiet ? t.muted : t.textPrimary,
623235
623280
  children: fit.verb
623236
623281
  }, undefined, false, undefined, this),
623237
- fit.spans ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(jsx_dev_runtime32.Fragment, {
623282
+ fit.spans ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(jsx_dev_runtime31.Fragment, {
623238
623283
  children: [
623239
- fit.verb ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623284
+ fit.verb ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623240
623285
  children: " "
623241
623286
  }, undefined, false, undefined, this) : null,
623242
- fit.spans.map((s, j) => s.code ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623287
+ fit.spans.map((s, j) => s.code ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623243
623288
  color: t.textPrimary,
623244
623289
  backgroundColor: t.border,
623245
623290
  children: s.text
623246
- }, j, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623291
+ }, j, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623247
623292
  color: quiet ? t.muted : t.textSecondary,
623248
623293
  children: s.text
623249
623294
  }, j, false, undefined, this))
623250
623295
  ]
623251
- }, undefined, true, undefined, this) : fit.text ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623296
+ }, undefined, true, undefined, this) : fit.text ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623252
623297
  color: quiet ? t.muted : t.textSecondary,
623253
623298
  children: ` ${fit.text}`
623254
623299
  }, undefined, false, undefined, this) : null,
623255
- fit.chip ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(jsx_dev_runtime32.Fragment, {
623300
+ fit.chip ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(jsx_dev_runtime31.Fragment, {
623256
623301
  children: [
623257
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623302
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623258
623303
  children: " "
623259
623304
  }, undefined, false, undefined, this),
623260
- /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623305
+ /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623261
623306
  color: t.textPrimary,
623262
623307
  backgroundColor: t.border,
623263
623308
  children: ` ${fit.chip} `
623264
623309
  }, undefined, false, undefined, this)
623265
623310
  ]
623266
623311
  }, undefined, true, undefined, this) : null,
623267
- fit.note ? /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Text, {
623312
+ fit.note ? /* @__PURE__ */ jsx_dev_runtime31.jsxDEV(Text, {
623268
623313
  color: row.status === "fail" ? t.red : t.muted,
623269
623314
  children: `${" ".repeat(fit.noteGap ?? 2)}${fit.note}`
623270
623315
  }, undefined, false, undefined, this) : null
@@ -623275,6 +623320,32 @@ function TurnResults({
623275
623320
  }, undefined, false, undefined, this);
623276
623321
  }
623277
623322
 
623323
+ // apps/tui/src/components/fade-transition.tsx
623324
+ var import_react60 = __toESM(require_react(), 1);
623325
+ var jsx_dev_runtime32 = __toESM(require_jsx_dev_runtime(), 1);
623326
+ function FadeIn({ children, duration = 300, delay = 0, onComplete }) {
623327
+ const [started, setStarted] = import_react60.useState(false);
623328
+ import_react60.useEffect(() => {
623329
+ const timeout = setTimeout(() => {
623330
+ setStarted(true);
623331
+ }, delay);
623332
+ return () => clearTimeout(timeout);
623333
+ }, [delay]);
623334
+ import_react60.useEffect(() => {
623335
+ if (!started)
623336
+ return;
623337
+ const timeout = setTimeout(() => {
623338
+ onComplete?.();
623339
+ }, duration);
623340
+ return () => clearTimeout(timeout);
623341
+ }, [started, duration, onComplete]);
623342
+ if (!started)
623343
+ return null;
623344
+ return /* @__PURE__ */ jsx_dev_runtime32.jsxDEV(Box_default, {
623345
+ children
623346
+ }, undefined, false, undefined, this);
623347
+ }
623348
+
623278
623349
  // apps/tui/src/components/sound-effects.tsx
623279
623350
  var import_react61 = __toESM(require_react(), 1);
623280
623351
  var BELL = "\x07";
@@ -623687,6 +623758,7 @@ function MessageList({
623687
623758
  showAnimations,
623688
623759
  isLatestAssistant: message.id === lastAssistantId,
623689
623760
  trail: trailById.get(message.id) ?? [],
623761
+ turnRunning,
623690
623762
  trailMaxRows: trailCaps.get(message.id),
623691
623763
  onAnimationStart: (id) => {
623692
623764
  animatedIdsRef.current.add(id);
@@ -623718,6 +623790,7 @@ function MessageItem({
623718
623790
  showAnimations,
623719
623791
  isLatestAssistant = false,
623720
623792
  trail = [],
623793
+ turnRunning = false,
623721
623794
  trailMaxRows,
623722
623795
  onAnimationStart
623723
623796
  }) {
@@ -623903,7 +623976,7 @@ function MessageItem({
623903
623976
  animate: showAnimations
623904
623977
  }, undefined, false, undefined, this)
623905
623978
  }, undefined, false, undefined, this),
623906
- !isUser && !message.content.trim() && trail.length > 0 ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(MutedText, {
623979
+ !isUser && !turnRunning && !message.content.trim() && trail.length > 0 ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(MutedText, {
623907
623980
  children: "No reply."
623908
623981
  }, undefined, false, undefined, this) : null,
623909
623982
  message.content.trim() || trail.length === 0 ? /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
@@ -624065,7 +624138,11 @@ function MarkdownBody({
624065
624138
  })
624066
624139
  }, undefined, false, undefined, this);
624067
624140
  }
624068
- function InlineSpans({ spans, color, adhdMode }) {
624141
+ function InlineSpans({
624142
+ spans,
624143
+ color,
624144
+ adhdMode
624145
+ }) {
624069
624146
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text, {
624070
624147
  color,
624071
624148
  wrap: "wrap",
@@ -626476,7 +626553,7 @@ init_cloud_transcriber();
626476
626553
  init_types4();
626477
626554
 
626478
626555
  // packages/voice/voice-chat.ts
626479
- import { existsSync as existsSync115, unlinkSync as unlinkSync5 } from "node:fs";
626556
+ import { existsSync as existsSync116, unlinkSync as unlinkSync5 } from "node:fs";
626480
626557
  import { tmpdir as tmpdir21 } from "node:os";
626481
626558
  import { join as join173 } from "node:path";
626482
626559
 
@@ -626935,7 +627012,7 @@ class VoiceChatLoop {
626935
627012
  cleanupFile(wavPath);
626936
627013
  throw new VoiceChatFatalError(describeRecFailure(exitCode, why));
626937
627014
  }
626938
- if (!existsSync115(wavPath)) {
627015
+ if (!existsSync116(wavPath)) {
626939
627016
  this.reportError("No audio was recorded. Check that your microphone is the system input.");
626940
627017
  return null;
626941
627018
  }
@@ -627059,7 +627136,7 @@ function sleep2(ms) {
627059
627136
  }
627060
627137
  function cleanupFile(path) {
627061
627138
  try {
627062
- if (existsSync115(path))
627139
+ if (existsSync116(path))
627063
627140
  unlinkSync5(path);
627064
627141
  } catch {}
627065
627142
  }
@@ -627119,7 +627196,7 @@ function splitIntoSentences(text) {
627119
627196
  }
627120
627197
  // packages/voice/full-duplex-provider.ts
627121
627198
  import { execSync as execSync6 } from "node:child_process";
627122
- import { existsSync as existsSync116 } from "node:fs";
627199
+ import { existsSync as existsSync117 } from "node:fs";
627123
627200
  import { homedir as homedir87 } from "node:os";
627124
627201
  import { join as join174 } from "node:path";
627125
627202
  var MOSHI_HF_CACHE_PATH = join174(homedir87(), ".cache", "huggingface", "hub", "models--kyutai--moshiko-pytorch-bf16", "snapshots");
@@ -627137,7 +627214,7 @@ async function detectCapabilities() {
627137
627214
  const hasAppleSilicon = platform === "darwin" && arch === "arm64";
627138
627215
  const hasMLX = hasAppleSilicon && tryExec('python3 -c "import mlx"');
627139
627216
  const hasMoshiInstalled = tryExec('python3 -c "import moshi"');
627140
- const hasMoshiModel = existsSync116(MOSHI_HF_CACHE_PATH);
627217
+ const hasMoshiModel = existsSync117(MOSHI_HF_CACHE_PATH);
627141
627218
  const hasCUDA = tryExec("nvidia-smi");
627142
627219
  const hasNIMKey = !!process.env.NVIDIA_NIM_KEY;
627143
627220
  return {
@@ -628169,7 +628246,7 @@ function useWorkspaceTabs() {
628169
628246
 
628170
628247
  // apps/tui/src/lib/adhd-audio.ts
628171
628248
  init_bun_shim();
628172
- import { existsSync as existsSync118, mkdirSync as mkdirSync82 } from "node:fs";
628249
+ import { existsSync as existsSync119, mkdirSync as mkdirSync82 } from "node:fs";
628173
628250
  import { join as join177 } from "node:path";
628174
628251
  var DEFAULT_CONFIG4 = {
628175
628252
  duration: 180,
@@ -628213,7 +628290,7 @@ var CACHE_DIR3 = join177(process.env.HOME || "~", ".8gent", "adhd-audio");
628213
628290
  var CONFIG_PATH = join177(CACHE_DIR3, "config.json");
628214
628291
  function loadConfig2() {
628215
628292
  try {
628216
- if (existsSync118(CONFIG_PATH)) {
628293
+ if (existsSync119(CONFIG_PATH)) {
628217
628294
  const raw = JSON.parse(__require("node:fs").readFileSync(CONFIG_PATH, "utf-8"));
628218
628295
  return { ...DEFAULT_CONFIG4, ...raw };
628219
628296
  }
@@ -628222,7 +628299,7 @@ function loadConfig2() {
628222
628299
  }
628223
628300
  function saveConfig2(config) {
628224
628301
  try {
628225
- if (!existsSync118(CACHE_DIR3))
628302
+ if (!existsSync119(CACHE_DIR3))
628226
628303
  mkdirSync82(CACHE_DIR3, { recursive: true });
628227
628304
  __require("node:fs").writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
628228
628305
  } catch {}
@@ -628234,7 +628311,7 @@ class ADHDAudio {
628234
628311
  generating = false;
628235
628312
  _config;
628236
628313
  constructor() {
628237
- if (!existsSync118(CACHE_DIR3)) {
628314
+ if (!existsSync119(CACHE_DIR3)) {
628238
628315
  mkdirSync82(CACHE_DIR3, { recursive: true });
628239
628316
  }
628240
628317
  this._config = loadConfig2();
@@ -628261,7 +628338,7 @@ class ADHDAudio {
628261
628338
  for (const soundscape of Object.keys(SOUNDSCAPES)) {
628262
628339
  const path = this.getCachePath(soundscape);
628263
628340
  try {
628264
- if (existsSync118(path))
628341
+ if (existsSync119(path))
628265
628342
  __require("node:fs").unlinkSync(path);
628266
628343
  } catch {}
628267
628344
  }
@@ -628280,7 +628357,7 @@ class ADHDAudio {
628280
628357
  return join177(CACHE_DIR3, `${soundscape}.mp3`);
628281
628358
  }
628282
628359
  hasCached(soundscape) {
628283
- return existsSync118(this.getCachePath(soundscape));
628360
+ return existsSync119(this.getCachePath(soundscape));
628284
628361
  }
628285
628362
  async generate(soundscape) {
628286
628363
  const preset = SOUNDSCAPES[soundscape];
@@ -633185,7 +633262,7 @@ function snapshot(manager) {
633185
633262
 
633186
633263
  // apps/tui/src/hooks/useBodyParts.ts
633187
633264
  var import_react83 = __toESM(require_react(), 1);
633188
- import { existsSync as existsSync123 } from "node:fs";
633265
+ import { existsSync as existsSync124 } from "node:fs";
633189
633266
  import { execSync as execSync8 } from "node:child_process";
633190
633267
  import { homedir as homedir90 } from "node:os";
633191
633268
  import { join as join188 } from "node:path";
@@ -633199,7 +633276,7 @@ function hasCliclick() {
633199
633276
  }
633200
633277
  }
633201
633278
  function hasAxBridge() {
633202
- return existsSync123(join188(homedir90(), ".8gent", "bin", "8gent-ax-bridge"));
633279
+ return existsSync124(join188(homedir90(), ".8gent", "bin", "8gent-ax-bridge"));
633203
633280
  }
633204
633281
  function detectDefaultBodyPartsState() {
633205
633282
  const handsOk = hasCliclick();
@@ -634787,6 +634864,24 @@ ${r.message}`;
634787
634864
  // apps/tui/src/app.tsx
634788
634865
  init_slash_commands();
634789
634866
 
634867
+ // apps/tui/src/lib/term-intent.ts
634868
+ var VERBS2 = "run|open|start|launch|fire up|spin up|boot";
634869
+ var POLITE = "(?:please\\s+|can you\\s+|could you\\s+)?";
634870
+ var WHERE = "(?:\\s+in\\s+(?:a\\s+)?(?:new\\s+)?(?:tab|terminal(?:\\s+tab)?))?";
634871
+ var PRESET_INTENT = new RegExp(`^${POLITE}(?:${VERBS2})\\s+(?:up\\s+)?([a-z0-9][a-z0-9_-]*)(${WHERE})(?:\\s+please)?$`);
634872
+ function detectTermPresetIntent(input, presetIds) {
634873
+ const text = input.trim().toLowerCase().replace(/[.!?]+$/, "").replace(/\s+/g, " ");
634874
+ if (!text || text.length > 60)
634875
+ return null;
634876
+ const m = PRESET_INTENT.exec(text);
634877
+ if (!m)
634878
+ return null;
634879
+ const id = presetIds.find((p) => p.toLowerCase() === m[1]);
634880
+ if (!id)
634881
+ return null;
634882
+ return m[2] ? [id, "--in-tab"] : [id];
634883
+ }
634884
+
634790
634885
  // apps/tui/src/lib/turn-outcome.ts
634791
634886
  init_local_turn_error();
634792
634887
  function turnEndedInError(messages) {
@@ -634804,7 +634899,7 @@ function turnEndedInError(messages) {
634804
634899
  }
634805
634900
 
634806
634901
  // apps/tui/src/screens/BTWView.tsx
634807
- import { existsSync as existsSync125, mkdirSync as mkdirSync89, readFileSync as readFileSync113, writeFileSync as writeFileSync69 } from "node:fs";
634902
+ import { existsSync as existsSync126, mkdirSync as mkdirSync89, readFileSync as readFileSync113, writeFileSync as writeFileSync69 } from "node:fs";
634808
634903
  import { join as join190 } from "node:path";
634809
634904
  var import_react85 = __toESM(require_react(), 1);
634810
634905
 
@@ -634820,10 +634915,10 @@ var jsx_dev_runtime50 = __toESM(require_jsx_dev_runtime(), 1);
634820
634915
  var DATA_DIR5 = join190(process.env.HOME || "~", ".8gent", "tabs");
634821
634916
  function loadBTW() {
634822
634917
  try {
634823
- if (!existsSync125(DATA_DIR5))
634918
+ if (!existsSync126(DATA_DIR5))
634824
634919
  mkdirSync89(DATA_DIR5, { recursive: true });
634825
634920
  const filepath = join190(DATA_DIR5, "btw.json");
634826
- if (existsSync125(filepath)) {
634921
+ if (existsSync126(filepath)) {
634827
634922
  const raw = JSON.parse(readFileSync113(filepath, "utf-8"));
634828
634923
  return raw.items || [];
634829
634924
  }
@@ -634832,7 +634927,7 @@ function loadBTW() {
634832
634927
  }
634833
634928
  function saveBTW(items) {
634834
634929
  try {
634835
- if (!existsSync125(DATA_DIR5))
634930
+ if (!existsSync126(DATA_DIR5))
634836
634931
  mkdirSync89(DATA_DIR5, { recursive: true });
634837
634932
  writeFileSync69(join190(DATA_DIR5, "btw.json"), JSON.stringify({ items }, null, 2));
634838
634933
  } catch {}
@@ -635167,17 +635262,17 @@ function HistoryScreen({ conversations, onSelect, onBack }) {
635167
635262
  }
635168
635263
 
635169
635264
  // apps/tui/src/screens/IdeasView.tsx
635170
- import { existsSync as existsSync126, mkdirSync as mkdirSync90, readFileSync as readFileSync114, writeFileSync as writeFileSync70 } from "node:fs";
635265
+ import { existsSync as existsSync127, mkdirSync as mkdirSync90, readFileSync as readFileSync114, writeFileSync as writeFileSync70 } from "node:fs";
635171
635266
  import { join as join191 } from "node:path";
635172
635267
  var import_react87 = __toESM(require_react(), 1);
635173
635268
  var jsx_dev_runtime52 = __toESM(require_jsx_dev_runtime(), 1);
635174
635269
  var DATA_DIR6 = join191(process.env.HOME || "~", ".8gent", "tabs");
635175
635270
  function loadIdeas() {
635176
635271
  try {
635177
- if (!existsSync126(DATA_DIR6))
635272
+ if (!existsSync127(DATA_DIR6))
635178
635273
  mkdirSync90(DATA_DIR6, { recursive: true });
635179
635274
  const filepath = join191(DATA_DIR6, "ideas.json");
635180
- if (existsSync126(filepath)) {
635275
+ if (existsSync127(filepath)) {
635181
635276
  const raw = JSON.parse(readFileSync114(filepath, "utf-8"));
635182
635277
  return raw.ideas || [];
635183
635278
  }
@@ -635186,7 +635281,7 @@ function loadIdeas() {
635186
635281
  }
635187
635282
  function saveIdeas(ideas) {
635188
635283
  try {
635189
- if (!existsSync126(DATA_DIR6))
635284
+ if (!existsSync127(DATA_DIR6))
635190
635285
  mkdirSync90(DATA_DIR6, { recursive: true });
635191
635286
  writeFileSync70(join191(DATA_DIR6, "ideas.json"), JSON.stringify({ ideas }, null, 2));
635192
635287
  } catch {}
@@ -635649,7 +635744,7 @@ function KeyVaultScreen({
635649
635744
  }
635650
635745
 
635651
635746
  // apps/tui/src/screens/MusicPlayerView.tsx
635652
- import { existsSync as existsSync127, readdirSync as readdirSync28, statSync as statSync39 } from "node:fs";
635747
+ import { existsSync as existsSync128, readdirSync as readdirSync28, statSync as statSync39 } from "node:fs";
635653
635748
  import { basename as basename53, join as join192 } from "node:path";
635654
635749
  var import_react89 = __toESM(require_react(), 1);
635655
635750
  var jsx_dev_runtime54 = __toESM(require_jsx_dev_runtime(), 1);
@@ -635671,7 +635766,7 @@ function formatDate(date) {
635671
635766
  }
635672
635767
  function loadTracks(currentTrack) {
635673
635768
  const tracks = [];
635674
- if (!existsSync127(CACHE_DIR4))
635769
+ if (!existsSync128(CACHE_DIR4))
635675
635770
  return tracks;
635676
635771
  try {
635677
635772
  const files = readdirSync28(CACHE_DIR4).filter((f) => f.endsWith(".mp3") || f.endsWith(".wav"));
@@ -636180,7 +636275,7 @@ function NarratorView({ tasks, narratorText, maxHeight }) {
636180
636275
  }
636181
636276
 
636182
636277
  // apps/tui/src/screens/NotesView.tsx
636183
- import { existsSync as existsSync128, mkdirSync as mkdirSync91, readFileSync as readFileSync115, writeFileSync as writeFileSync71 } from "node:fs";
636278
+ import { existsSync as existsSync129, mkdirSync as mkdirSync91, readFileSync as readFileSync115, writeFileSync as writeFileSync71 } from "node:fs";
636184
636279
  import { join as join193 } from "node:path";
636185
636280
  var import_react91 = __toESM(require_react(), 1);
636186
636281
  var jsx_dev_runtime59 = __toESM(require_jsx_dev_runtime(), 1);
@@ -636188,10 +636283,10 @@ var DATA_DIR7 = join193(process.env.HOME || "~", ".8gent", "tabs");
636188
636283
  var EMPTY_TAB_NAMES = [];
636189
636284
  function loadNotes() {
636190
636285
  try {
636191
- if (!existsSync128(DATA_DIR7))
636286
+ if (!existsSync129(DATA_DIR7))
636192
636287
  mkdirSync91(DATA_DIR7, { recursive: true });
636193
636288
  const filepath = join193(DATA_DIR7, "notes.json");
636194
- if (existsSync128(filepath)) {
636289
+ if (existsSync129(filepath)) {
636195
636290
  const raw = JSON.parse(readFileSync115(filepath, "utf-8"));
636196
636291
  return raw.notes || [];
636197
636292
  }
@@ -636200,7 +636295,7 @@ function loadNotes() {
636200
636295
  }
636201
636296
  function saveNotes(notes) {
636202
636297
  try {
636203
- if (!existsSync128(DATA_DIR7))
636298
+ if (!existsSync129(DATA_DIR7))
636204
636299
  mkdirSync91(DATA_DIR7, { recursive: true });
636205
636300
  writeFileSync71(join193(DATA_DIR7, "notes.json"), JSON.stringify({ notes }, null, 2));
636206
636301
  } catch {}
@@ -637395,7 +637490,7 @@ function OnboardingScreen({
637395
637490
 
637396
637491
  // apps/tui/src/screens/ProjectsView.tsx
637397
637492
  import { execSync as execSync9 } from "node:child_process";
637398
- import { existsSync as existsSync129, mkdirSync as mkdirSync92, readFileSync as readFileSync116, readdirSync as readdirSync29, statSync as statSync40, writeFileSync as writeFileSync72 } from "node:fs";
637493
+ import { existsSync as existsSync130, mkdirSync as mkdirSync92, readFileSync as readFileSync116, readdirSync as readdirSync29, statSync as statSync40, writeFileSync as writeFileSync72 } from "node:fs";
637399
637494
  import { homedir as homedir91 } from "node:os";
637400
637495
  import { basename as basename54, join as join194 } from "node:path";
637401
637496
  var import_react96 = __toESM(require_react(), 1);
@@ -637410,7 +637505,7 @@ var SCAN_DIRS = [
637410
637505
  var PROJECT_MARKERS = ["package.json", ".git", "CLAUDE.md", ".8gent"];
637411
637506
  function isProjectDir(dirPath) {
637412
637507
  try {
637413
- return PROJECT_MARKERS.some((marker) => existsSync129(join194(dirPath, marker)));
637508
+ return PROJECT_MARKERS.some((marker) => existsSync130(join194(dirPath, marker)));
637414
637509
  } catch {
637415
637510
  return false;
637416
637511
  }
@@ -637418,7 +637513,7 @@ function isProjectDir(dirPath) {
637418
637513
  function getProjectName(dirPath) {
637419
637514
  try {
637420
637515
  const pkgPath = join194(dirPath, "package.json");
637421
- if (existsSync129(pkgPath)) {
637516
+ if (existsSync130(pkgPath)) {
637422
637517
  const pkg = JSON.parse(readFileSync116(pkgPath, "utf8"));
637423
637518
  if (pkg.name)
637424
637519
  return pkg.name;
@@ -637428,7 +637523,7 @@ function getProjectName(dirPath) {
637428
637523
  }
637429
637524
  function getGitBranch2(dirPath) {
637430
637525
  try {
637431
- if (!existsSync129(join194(dirPath, ".git")))
637526
+ if (!existsSync130(join194(dirPath, ".git")))
637432
637527
  return;
637433
637528
  const result = execSync9(`git -C "${dirPath}" branch --show-current 2>/dev/null`, {
637434
637529
  encoding: "utf8",
@@ -637448,7 +637543,7 @@ function getLastModified(dirPath) {
637448
637543
  }
637449
637544
  }
637450
637545
  function has8gent(dirPath) {
637451
- return existsSync129(join194(dirPath, ".8gent"));
637546
+ return existsSync130(join194(dirPath, ".8gent"));
637452
637547
  }
637453
637548
  function buildProjectEntry(dirPath) {
637454
637549
  return {
@@ -637465,7 +637560,7 @@ function scanForProjects(currentPath) {
637465
637560
  found.add(currentPath);
637466
637561
  results.push(buildProjectEntry(currentPath));
637467
637562
  for (const scanDir of SCAN_DIRS) {
637468
- if (!existsSync129(scanDir))
637563
+ if (!existsSync130(scanDir))
637469
637564
  continue;
637470
637565
  try {
637471
637566
  const entries = readdirSync29(scanDir, { withFileTypes: true });
@@ -637497,7 +637592,7 @@ function scanForProjects(currentPath) {
637497
637592
  }
637498
637593
  function loadSavedProjects() {
637499
637594
  try {
637500
- if (!existsSync129(SAVED_PROJECTS_PATH))
637595
+ if (!existsSync130(SAVED_PROJECTS_PATH))
637501
637596
  return [];
637502
637597
  const raw = readFileSync116(SAVED_PROJECTS_PATH, "utf8");
637503
637598
  const data = JSON.parse(raw);
@@ -637520,7 +637615,7 @@ function mergeProjects(scanned, saved) {
637520
637615
  for (const s of saved) {
637521
637616
  if (paths.has(s.path))
637522
637617
  continue;
637523
- if (!existsSync129(s.path))
637618
+ if (!existsSync130(s.path))
637524
637619
  continue;
637525
637620
  paths.add(s.path);
637526
637621
  extra.push(buildProjectEntry(s.path));
@@ -637588,7 +637683,7 @@ function ProjectsView({
637588
637683
  if (addMode) {
637589
637684
  if (key.return) {
637590
637685
  const trimmed = addInput.trim();
637591
- if (trimmed && existsSync129(trimmed)) {
637686
+ if (trimmed && existsSync130(trimmed)) {
637592
637687
  const newEntry = {
637593
637688
  ...buildProjectEntry(trimmed),
637594
637689
  addedAt: new Date().toISOString()
@@ -637775,17 +637870,17 @@ function ProjectsView({
637775
637870
  }
637776
637871
 
637777
637872
  // apps/tui/src/screens/QuestionsView.tsx
637778
- import { existsSync as existsSync130, mkdirSync as mkdirSync93, readFileSync as readFileSync117, writeFileSync as writeFileSync73 } from "node:fs";
637873
+ import { existsSync as existsSync131, mkdirSync as mkdirSync93, readFileSync as readFileSync117, writeFileSync as writeFileSync73 } from "node:fs";
637779
637874
  import { join as join195 } from "node:path";
637780
637875
  var import_react97 = __toESM(require_react(), 1);
637781
637876
  var jsx_dev_runtime62 = __toESM(require_jsx_dev_runtime(), 1);
637782
637877
  var DATA_DIR8 = join195(process.env.HOME || "~", ".8gent", "tabs");
637783
637878
  function loadQuestions() {
637784
637879
  try {
637785
- if (!existsSync130(DATA_DIR8))
637880
+ if (!existsSync131(DATA_DIR8))
637786
637881
  mkdirSync93(DATA_DIR8, { recursive: true });
637787
637882
  const filepath = join195(DATA_DIR8, "questions.json");
637788
- if (existsSync130(filepath)) {
637883
+ if (existsSync131(filepath)) {
637789
637884
  const raw = JSON.parse(readFileSync117(filepath, "utf-8"));
637790
637885
  return raw.questions || [];
637791
637886
  }
@@ -637794,7 +637889,7 @@ function loadQuestions() {
637794
637889
  }
637795
637890
  function saveQuestions(questions) {
637796
637891
  try {
637797
- if (!existsSync130(DATA_DIR8))
637892
+ if (!existsSync131(DATA_DIR8))
637798
637893
  mkdirSync93(DATA_DIR8, { recursive: true });
637799
637894
  writeFileSync73(join195(DATA_DIR8, "questions.json"), JSON.stringify({ questions }, null, 2));
637800
637895
  } catch {}
@@ -639082,13 +639177,13 @@ import * as fs104 from "node:fs";
639082
639177
  import * as pathMod from "node:path";
639083
639178
 
639084
639179
  // apps/tui/src/lib/package-scripts.ts
639085
- import { existsSync as existsSync131 } from "node:fs";
639086
- import { join as join196, resolve as resolve73 } from "node:path";
639180
+ import { existsSync as existsSync132 } from "node:fs";
639181
+ import { join as join196, resolve as resolve74 } from "node:path";
639087
639182
  var __dirname = (import.meta.dirname + "/../../apps/tui/src/lib");
639088
- var PACKAGE_ROOT = resolve73(__dirname, "../../../..");
639183
+ var PACKAGE_ROOT = resolve74(__dirname, "../../../..");
639089
639184
  function packageBinScript(name, root = PACKAGE_ROOT) {
639090
639185
  const candidate = join196(root, "bin", name);
639091
- return existsSync131(candidate) ? candidate : null;
639186
+ return existsSync132(candidate) ? candidate : null;
639092
639187
  }
639093
639188
 
639094
639189
  // apps/tui/src/app.tsx
@@ -644012,6 +644107,26 @@ ${found.slice(0, 40).map((k) => ` ${k.name} ${k.description}`).join(`
644012
644107
  }
644013
644108
  return;
644014
644109
  }
644110
+ if (input && !attached) {
644111
+ const termArgs = detectTermPresetIntent(input, listPresetIds());
644112
+ if (termArgs) {
644113
+ appendToTab(tabId, {
644114
+ id: `user-${Date.now()}`,
644115
+ role: "user",
644116
+ content: bubbleContent,
644117
+ timestamp: new Date
644118
+ });
644119
+ setRecentCommands((prev) => [bubbleContent, ...prev].slice(0, 20));
644120
+ const refused = planModeRefusal2(permView(permHolderFor(tabId)).mode);
644121
+ if (refused) {
644122
+ addSystemMessage(refused);
644123
+ return;
644124
+ }
644125
+ addSystemMessage(`Opening ${termArgs[0]} (same as /term ${termArgs.join(" ")}).`);
644126
+ handleSlashCommand("term", termArgs);
644127
+ return;
644128
+ }
644129
+ }
644015
644130
  if (input && !attached) {
644016
644131
  const buildTask = detectBuildIntent(input);
644017
644132
  if (buildTask) {