omnius 1.0.536 → 1.0.537

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6981,7 +6981,7 @@ var init_file_write = __esm({
6981
6981
  init_text_encoding();
6982
6982
  FileWriteTool = class {
6983
6983
  name = "file_write";
6984
- description = "Create new files or deliberately replace small, empty, or placeholder files; use file_edit/file_patch for local changes. For content with heavy quoting, raw JSON, control characters, or large full-file payloads, pass content_base64 instead of shell heredocs/cat/tee.";
6984
+ description = "Create new files or deliberately replace small, empty, or placeholder files; use file_edit/file_patch for local changes. Existing-file replacement requires overwrite=true plus expected_hash from a fresh file_read; never use this as a fallback after a failed targeted edit. For content with heavy quoting, raw JSON, control characters, or large full-file payloads, pass content_base64 instead of shell heredocs/cat/tee.";
6985
6985
  parameters = {
6986
6986
  type: "object",
6987
6987
  properties: {
@@ -296557,8 +296557,8 @@ var init_cron_agent = __esm({
296557
296557
  }
296558
296558
  const lines = [];
296559
296559
  const formatJob = (job, scopeLabel, isThisProject) => {
296560
- const statusIcon = job.status === "active" ? "●" : job.status === "completed" ? "✔" : job.status === "paused" ? "⏸" : "✖";
296561
- lines.push(` ${statusIcon} [${job.id}] ${job.status.toUpperCase()} [${scopeLabel}]`);
296560
+ const statusIcon2 = job.status === "active" ? "●" : job.status === "completed" ? "✔" : job.status === "paused" ? "⏸" : "✖";
296561
+ lines.push(` ${statusIcon2} [${job.id}] ${job.status.toUpperCase()} [${scopeLabel}]`);
296562
296562
  lines.push(` Goal: ${job.goal.slice(0, 80)}${job.goal.length > 80 ? "..." : ""}`);
296563
296563
  lines.push(` Schedule: ${job.scheduleDescription}`);
296564
296564
  lines.push(` Runs: ${job.runCount}${job.maxRuns > 0 ? `/${job.maxRuns}` : ""}`);
@@ -553281,6 +553281,7 @@ function spawnFullSubAgent(task, opts, onOutput, onComplete) {
553281
553281
  child.stderr?.on("data", (chunk) => {
553282
553282
  const text2 = chunk.toString();
553283
553283
  outputBuffer.push(...text2.split("\n").filter((l2) => l2.length > 0));
553284
+ onOutput?.(text2);
553284
553285
  });
553285
553286
  child.on("exit", (code8) => {
553286
553287
  clearInterval(heartbeatTimer);
@@ -586548,6 +586549,8 @@ var init_agenticRunner = __esm({
586548
586549
  _completionCaveat = null;
586549
586550
  _completionLedger = null;
586550
586551
  _staleEditFamilies = /* @__PURE__ */ new Map();
586552
+ /** Semantic retry counter for blocked full-file writes (payload-independent). */
586553
+ _blockedFullWriteAttempts = /* @__PURE__ */ new Map();
586551
586554
  _lastReadTurnByPathKey = /* @__PURE__ */ new Map();
586552
586555
  _recentSuccessfulReplacements = [];
586553
586556
  // ── WO-AM-01/04/10: Associative memory stores ──
@@ -594775,6 +594778,7 @@ Respond with your assessment, then take action.`;
594775
594778
  this._lastDeclaredVerifierOutput = null;
594776
594779
  this._lastDeclaredVerifierDiagnostics = [];
594777
594780
  this._staleEditFamilies.clear();
594781
+ this._blockedFullWriteAttempts.clear();
594778
594782
  this._lastReadTurnByPathKey.clear();
594779
594783
  this._recentSuccessfulReplacements = [];
594780
594784
  this._lastWorldStateTurn = -1;
@@ -596155,7 +596159,7 @@ Respond with EXACTLY this structure before your next tool call:
596155
596159
  PICK: <number 1-3>
596156
596160
  WHY: <one sentence>
596157
596161
  FALSIFICATION: <observable signal that would refute the pick>
596158
- NEXT ACTION: <a single creative edit — file_write / file_edit / batch_edit>`;
596162
+ NEXT ACTION: <a single targeted edit — file_edit / file_patch / batch_edit; file_write only for a verified-new file>`;
596159
596163
  messages2.push({ role: "system", content: _replan60 });
596160
596164
  stagnationCooldownUntilTurn = turn + REG60_COOLDOWN_TURNS;
596161
596165
  const _tel60 = this._describeTopClusterTelemetry();
@@ -596416,7 +596420,7 @@ If this matches your current shape, try it before continuing.`
596416
596420
  ] : [
596417
596421
  `Pick ONE of these for your next response:`,
596418
596422
  ``,
596419
- ` (a) PRODUCE: emit a file_write / file_edit / file_patch that creates or changes project content. Even a partial draft of the next planned file is progress. Use shell only for commands/tests/system operations, not as a workaround for failed edit-tool encoding.`,
596423
+ ` (a) PRODUCE: emit a targeted file_edit / file_patch / batch_edit that creates or changes project content. Use file_write only after verifying the target is new/placeholder and never as recovery for a blocked existing-file write. Use shell only for commands/tests/system operations, not as a workaround for failed edit-tool encoding.`,
596420
596424
  ``,
596421
596425
  ` (b) ERROR-INFORMED LOCAL TRIAGE: anchor on the freshest local failure, identify the implicated file/path/symbol/command, then make one targeted local move instead of broad exploration.`,
596422
596426
  ``,
@@ -598132,7 +598136,7 @@ Corrective action: try a different approach first: read relevant files, adjust a
598132
598136
  `A REG-61 FIRST-EDIT NUDGE was issued earlier and has not yet been satisfied. The directive asks you to prioritize a creative edit. You issued '${tc.name}' instead, which is a read/explore/shell call.`,
598133
598137
  ``,
598134
598138
  `This call was ALLOWED through (needed context should never be blocked), but try to make a creative edit next:`,
598135
- ` • file_write — create a new file`,
598139
+ ` • file_write — create a verified-new file only (never use it to repair an existing target)`,
598136
598140
  ` • file_edit — modify an existing file (find/replace)`,
598137
598141
  ` • batch_edit — multiple find/replace edits in one call`,
598138
598142
  ` • file_patch — apply a version-checked line-range patch`,
@@ -598297,7 +598301,7 @@ Read the current file if needed, make a different concrete edit, or move to veri
598297
598301
  systemGuidance: staleBlockOutput
598298
598302
  };
598299
598303
  }
598300
- const staleRewriteBlock = this.staleEditOverwritePreflightBlock(tc.name, tc.arguments ?? {});
598304
+ const staleRewriteBlock = this.staleEditOverwritePreflightBlock(tc.name, tc.arguments ?? {}, turn);
598301
598305
  if (staleRewriteBlock) {
598302
598306
  const focusDecision2 = this._focusSupervisor?.evaluateProposedCall({
598303
598307
  turn,
@@ -603306,6 +603310,29 @@ ${marker}` : marker);
603306
603310
  const path16 = this.extractPrimaryToolPath(args);
603307
603311
  if (!path16)
603308
603312
  return null;
603313
+ const pathKey = this.staleEditPathKey(path16);
603314
+ const active = [...this._staleEditFamilies.values()].filter((entry) => entry.pathKey === pathKey).sort((a2, b) => b.lastFailureTurn - a2.lastFailureTurn).find((entry) => {
603315
+ const hasFreshRead = entry.lastReadTurn > entry.lastFailureTurn;
603316
+ const hasFreshMutation = entry.lastMutationTurn > entry.lastFailureTurn;
603317
+ return !hasFreshMutation && (entry.count >= 2 || !hasFreshRead);
603318
+ });
603319
+ if (active) {
603320
+ const tier = this.options.modelTier ?? "large";
603321
+ if (tier !== "small" && tier !== "medium")
603322
+ return null;
603323
+ return [
603324
+ `[EDIT REPAIR LOCK] A recent narrow edit to ${active.path} failed because the model-visible target diverged from disk (${active.errorKind}; latest_file_hash=${active.latestFileHash || "unknown"}).`,
603325
+ `This full-file overwrite was NOT executed. A stale narrow edit must not be repaired by broad file regeneration; preserve a constrained edit path until fresh target evidence is used.`,
603326
+ ``,
603327
+ `Allowed next actions:`,
603328
+ `1. file_read ${active.path} once and construct file_edit from exact current text.`,
603329
+ `2. file_patch/file_edit against the latest file hash or a different target.`,
603330
+ `3. file_write with dry_run=true only to validate a full rewrite proposal without changing disk.`,
603331
+ `4. run verification if the desired change is already present, or report the explicit blocker instead of completing.`,
603332
+ ``,
603333
+ `Stale target preview: ${active.preview}`
603334
+ ].join("\n");
603335
+ }
603309
603336
  let exists2 = false;
603310
603337
  try {
603311
603338
  const st = _fsStatSync(this._absoluteToolPath(path16), {
@@ -603317,6 +603344,15 @@ ${marker}` : marker);
603317
603344
  }
603318
603345
  if (!exists2)
603319
603346
  return null;
603347
+ const overwrite = args["overwrite"] === true || args["overwriteExisting"] === true;
603348
+ if (!overwrite) {
603349
+ return [
603350
+ "[FULL FILE REWRITE CONTRACT]",
603351
+ `file_write would overwrite existing file ${path16}, but overwrite=true is missing.`,
603352
+ "This call was not executed. Prefer file_edit/file_patch for a local change.",
603353
+ "If a deliberate whole-file replacement is truly required, first file_read the current file and retry with overwrite=true and expected_hash equal to that read's sha256."
603354
+ ].join("\n");
603355
+ }
603320
603356
  if (!this._toolCallUsesFreshFileReadEvidence(toolName, args)) {
603321
603357
  const fresh = this._freshReadHashForEditPath(path16);
603322
603358
  return [
@@ -603430,16 +603466,13 @@ ${marker}` : marker);
603430
603466
  }
603431
603467
  return null;
603432
603468
  }
603433
- staleEditOverwritePreflightBlock(toolName, args) {
603469
+ staleEditOverwritePreflightBlock(toolName, args, turn = 0) {
603434
603470
  if (toolName !== "file_write")
603435
603471
  return null;
603436
603472
  if (process.env["OMNIUS_ALLOW_STALE_REPAIR_OVERWRITE"] === "1")
603437
603473
  return null;
603438
603474
  if (args?.["dry_run"] === true || args?.["dryRun"] === true)
603439
603475
  return null;
603440
- const tier = this.options.modelTier ?? "large";
603441
- if (tier !== "small" && tier !== "medium")
603442
- return null;
603443
603476
  const path16 = this.extractPrimaryToolPath(args);
603444
603477
  if (!path16)
603445
603478
  return null;
@@ -603449,19 +603482,49 @@ ${marker}` : marker);
603449
603482
  const hasFreshMutation = entry.lastMutationTurn > entry.lastFailureTurn;
603450
603483
  return !hasFreshMutation && (entry.count >= 2 || !hasFreshRead);
603451
603484
  });
603452
- if (!active)
603485
+ if (active) {
603486
+ const tier = this.options.modelTier ?? "large";
603487
+ if (tier !== "small" && tier !== "medium")
603488
+ return null;
603489
+ return [
603490
+ `[EDIT REPAIR LOCK] A recent narrow edit to ${active.path} failed because the model-visible target diverged from disk (${active.errorKind}; latest_file_hash=${active.latestFileHash || "unknown"}).`,
603491
+ `This full-file overwrite was NOT executed. A stale narrow edit must not be repaired by broad file regeneration; preserve a constrained edit path until fresh target evidence is used.`,
603492
+ ``,
603493
+ `Allowed next actions:`,
603494
+ `1. file_read ${active.path} once and construct file_edit from exact current text.`,
603495
+ `2. file_patch/file_edit against the latest file hash or a different target.`,
603496
+ `3. file_write with dry_run=true only to validate a full rewrite proposal without changing disk.`,
603497
+ `4. run verification if the desired change is already present, or report the explicit blocker instead of completing.`,
603498
+ ``,
603499
+ `Stale target preview: ${active.preview}`
603500
+ ].join("\n");
603501
+ }
603502
+ let exists2 = false;
603503
+ try {
603504
+ const st = _fsStatSync(this._absoluteToolPath(path16), {
603505
+ throwIfNoEntry: false
603506
+ });
603507
+ exists2 = !!st && st.isFile();
603508
+ } catch {
603509
+ exists2 = false;
603510
+ }
603511
+ if (!exists2)
603512
+ return null;
603513
+ const overwrite = args?.["overwrite"] === true || args?.["overwriteExisting"] === true;
603514
+ const hasFreshContract = overwrite && this._toolCallUsesFreshFileReadEvidence(toolName, args ?? {});
603515
+ if (hasFreshContract) {
603516
+ this._blockedFullWriteAttempts.delete(pathKey);
603517
+ return null;
603518
+ }
603519
+ const previous = this._blockedFullWriteAttempts.get(pathKey);
603520
+ const count = (previous?.count ?? 0) + 1;
603521
+ this._blockedFullWriteAttempts.set(pathKey, { count, lastTurn: turn });
603522
+ if (count < 2)
603453
603523
  return null;
603454
603524
  return [
603455
- `[EDIT REPAIR LOCK] A recent narrow edit to ${active.path} failed because the model-visible target diverged from disk (${active.errorKind}; latest_file_hash=${active.latestFileHash || "unknown"}).`,
603456
- `This full-file overwrite was NOT executed. A stale narrow edit must not be repaired by broad file regeneration; preserve a constrained edit path until fresh target evidence is used.`,
603457
- ``,
603458
- `Allowed next actions:`,
603459
- `1. file_read ${active.path} once and construct file_edit from exact current text.`,
603460
- `2. file_patch/file_edit against the latest file hash or a different target.`,
603461
- `3. file_write with dry_run=true only to validate a full rewrite proposal without changing disk.`,
603462
- `4. run verification if the desired change is already present, or report the explicit blocker instead of completing.`,
603463
- ``,
603464
- `Stale target preview: ${active.preview}`
603525
+ `[FULL FILE WRITE LOOP BLOCKED] ${path16} has already received ${count} blocked full-file write attempts.`,
603526
+ `This call was NOT executed, even if the replacement payload changed. Do not call file_write again for this path until a fresh file_read is completed.`,
603527
+ `Next action: use file_read ${path16}, then prefer file_patch/file_edit for the local change. Only an intentional whole-file replacement may use file_write with overwrite=true and expected_hash from that fresh read.`
603465
603528
  ].join("\n");
603466
603529
  }
603467
603530
  editReversalPreflightBlock(toolName, args, turn) {
@@ -603555,7 +603618,9 @@ ${marker}` : marker);
603555
603618
  noteStaleEditGuardOutcome(toolName, args, result, turn) {
603556
603619
  const path16 = this.extractPrimaryToolPath(args);
603557
603620
  const pathKey = path16 ? this.staleEditPathKey(path16) : "";
603558
- if (toolName === "file_read" && path16 && result.success) {
603621
+ const isFreshRead = toolName === "file_read" && path16 && result.success && result.runtimeAuthored !== true;
603622
+ if (isFreshRead) {
603623
+ this._blockedFullWriteAttempts.delete(pathKey);
603559
603624
  for (const entry of this._staleEditFamilies.values()) {
603560
603625
  if (entry.pathKey === pathKey)
603561
603626
  entry.lastReadTurn = turn;
@@ -603563,6 +603628,7 @@ ${marker}` : marker);
603563
603628
  return;
603564
603629
  }
603565
603630
  if (toolName === "file_write" && path16 && this._isRealProjectMutation(toolName, result)) {
603631
+ this._blockedFullWriteAttempts.delete(pathKey);
603566
603632
  for (const [key2, entry] of this._staleEditFamilies) {
603567
603633
  if (entry.pathKey === pathKey)
603568
603634
  this._staleEditFamilies.delete(key2);
@@ -603576,6 +603642,7 @@ ${marker}` : marker);
603576
603642
  return;
603577
603643
  const targetPathKey = this.staleEditPathKey(target.path);
603578
603644
  if (this._isRealProjectMutation(toolName, result) || result.alreadyApplied === true) {
603645
+ this._blockedFullWriteAttempts.delete(targetPathKey);
603579
603646
  for (const [key2, entry] of this._staleEditFamilies) {
603580
603647
  if (entry.pathKey === targetPathKey)
603581
603648
  this._staleEditFamilies.delete(key2);
@@ -640891,6 +640958,50 @@ var init_dist9 = __esm({
640891
640958
  });
640892
640959
 
640893
640960
  // packages/cli/src/tui/stageIndicator.ts
640961
+ function stageForToolName(toolName) {
640962
+ const name10 = String(toolName ?? "").trim().toLowerCase();
640963
+ if (name10 === "file_read" || name10 === "structured_read") return "reading";
640964
+ if (name10 === "grep_search" || name10 === "glob_find" || name10 === "find_files" || name10 === "web_search" || name10 === "web_fetch" || name10 === "web_crawl")
640965
+ return "searching";
640966
+ if (name10 === "file_write" || name10 === "file_edit" || name10 === "file_patch" || name10 === "batch_edit" || name10 === "structured_file")
640967
+ return "writing";
640968
+ if (name10 === "shell" || name10 === "shell_async" || name10 === "run_tests" || name10 === "repl_exec")
640969
+ return "building";
640970
+ return "tool_call";
640971
+ }
640972
+ function stageForAgentEvent(event) {
640973
+ switch (event.type) {
640974
+ case "tool_call":
640975
+ return { stage: stageForToolName(event.toolName), detail: event.toolName };
640976
+ case "tool_result":
640977
+ return event.success === false ? {
640978
+ stage: "failed",
640979
+ detail: event.toolName ? `${event.toolName} failed` : void 0
640980
+ } : {
640981
+ stage: stageForToolName(event.toolName),
640982
+ detail: event.toolName
640983
+ };
640984
+ case "stream_start":
640985
+ case "assistant_text":
640986
+ case "model_response":
640987
+ return { stage: "planning" };
640988
+ case "compaction":
640989
+ return { stage: "compacting" };
640990
+ case "debug_adversary":
640991
+ return { stage: "adversary_audit" };
640992
+ case "complete":
640993
+ return { stage: "completed", detail: "done" };
640994
+ case "error":
640995
+ return {
640996
+ stage: "failed",
640997
+ detail: event.content?.trim().slice(0, 40) || void 0
640998
+ };
640999
+ case "user_interrupt":
641000
+ return { stage: "blocked", detail: "interrupted" };
641001
+ default:
641002
+ return null;
641003
+ }
641004
+ }
640894
641005
  function tri360(x) {
640895
641006
  const t2 = (x % 360 + 360) % 360;
640896
641007
  return t2 < 180 ? t2 / 90 - 1 : 3 - t2 / 90;
@@ -640915,6 +641026,9 @@ function supportsTruecolor() {
640915
641026
  function isSweepingStage(stage2) {
640916
641027
  return SWEEPING_STAGES.has(stage2);
640917
641028
  }
641029
+ function stageLabel2(stage2) {
641030
+ return STAGE_META[stage2].label;
641031
+ }
640918
641032
  function stageBlockNeededWidth(stage2, detail) {
640919
641033
  const meta = STAGE_META[stage2];
640920
641034
  let body = meta.label;
@@ -641064,6 +641178,125 @@ var init_stageIndicator = __esm({
641064
641178
  }
641065
641179
  });
641066
641180
 
641181
+ // packages/cli/src/tui/sub-agent-live-block.ts
641182
+ function stageFallbackColor(stage2) {
641183
+ return STAGE_FALLBACK_COLOR[stage2];
641184
+ }
641185
+ function sanitizeSubAgentActivity(value2) {
641186
+ return value2.replace(ANSI_RE2, "").replace(/[\x00-\x1F\x7F]/g, " ").replace(/\s+/g, " ").trim().slice(0, MAX_ACTIVITY_CHARS);
641187
+ }
641188
+ function appendSubAgentActivity(entry, value2, maxLines = 3) {
641189
+ const line = sanitizeSubAgentActivity(value2);
641190
+ if (!line) return;
641191
+ entry.activity.push(line);
641192
+ if (entry.activity.length > maxLines) {
641193
+ entry.activity.splice(0, entry.activity.length - maxLines);
641194
+ }
641195
+ entry.updatedAt = Date.now();
641196
+ }
641197
+ function buildSubAgentLiveBlockLines(sourceEntries, width, phase, truecolor = supportsTruecolor()) {
641198
+ const entries = Array.from(sourceEntries).slice(-MAX_PREVIEW_AGENTS);
641199
+ if (entries.length === 0) return [];
641200
+ const w = Math.max(48, Math.floor(width));
641201
+ const inner = Math.max(1, w - 4);
641202
+ const primary = entries.find((entry) => entry.status === "running") ?? entries[0];
641203
+ const title = " Sub-agent activity ";
641204
+ const top = `╭─┤${title}├${"─".repeat(Math.max(0, w - 5 - title.length))}╮`;
641205
+ const bottom = `╰${"─".repeat(Math.max(0, w - 2))}╯`;
641206
+ const lines = [paint(top, primary.stage, phase, 0, truecolor)];
641207
+ for (const entry of entries) {
641208
+ const icon = statusIcon(entry.status);
641209
+ const label = entry.label || entry.id;
641210
+ const state = entry.status === "running" ? `${stageLabel2(entry.stage)}${entry.detail ? ` ${entry.detail}` : ""}` : entry.status;
641211
+ const activity = entry.activity.at(-1) ?? "waiting for activity";
641212
+ lines.push(
641213
+ contentRow(
641214
+ `${icon} ${label} · ${state}`,
641215
+ inner,
641216
+ entry.stage,
641217
+ phase,
641218
+ truecolor
641219
+ )
641220
+ );
641221
+ lines.push(
641222
+ contentRow(
641223
+ ` └─ ${activity}`,
641224
+ inner,
641225
+ entry.stage,
641226
+ phase,
641227
+ truecolor
641228
+ )
641229
+ );
641230
+ }
641231
+ lines.push(paint(bottom, primary.stage, phase, 0, truecolor));
641232
+ return lines;
641233
+ }
641234
+ function contentRow(value2, width, stage2, phase, truecolor) {
641235
+ return `│ ${paint(fit2(value2, width), stage2, phase, 1, truecolor)} │`;
641236
+ }
641237
+ function paint(value2, stage2, phase, offset, truecolor) {
641238
+ let out = "";
641239
+ const chars = Array.from(value2);
641240
+ for (let i2 = 0; i2 < chars.length; i2++) {
641241
+ const char = chars[i2];
641242
+ if (truecolor) {
641243
+ const [r2, g, b] = stageGradientRgb(stage2, i2 + offset, phase);
641244
+ out += `\x1B[38;2;${r2};${g};${b}m${char}`;
641245
+ } else {
641246
+ out += `\x1B[38;5;${STAGE_FALLBACK_COLOR[stage2]}m${char}`;
641247
+ }
641248
+ }
641249
+ return `${out}\x1B[0m`;
641250
+ }
641251
+ function fit2(value2, width) {
641252
+ const plain = value2.replace(ANSI_RE2, "").replace(/\s+$/g, "");
641253
+ const chars = Array.from(plain);
641254
+ if (chars.length > width) {
641255
+ return `${chars.slice(0, Math.max(0, width - 1)).join("")}…`;
641256
+ }
641257
+ return plain + " ".repeat(Math.max(0, width - chars.length));
641258
+ }
641259
+ function statusIcon(status) {
641260
+ switch (status) {
641261
+ case "completed":
641262
+ return "✓";
641263
+ case "failed":
641264
+ return "✗";
641265
+ case "paused":
641266
+ case "stopped":
641267
+ return "○";
641268
+ default:
641269
+ return "●";
641270
+ }
641271
+ }
641272
+ var ANSI_RE2, MAX_PREVIEW_AGENTS, MAX_ACTIVITY_CHARS, STAGE_FALLBACK_COLOR;
641273
+ var init_sub_agent_live_block = __esm({
641274
+ "packages/cli/src/tui/sub-agent-live-block.ts"() {
641275
+ init_stageIndicator();
641276
+ ANSI_RE2 = /\x1B\[[0-?]*[ -/]*[@-~]|\x1B\].*?(?:\x07|\x1B\\)/g;
641277
+ MAX_PREVIEW_AGENTS = 4;
641278
+ MAX_ACTIVITY_CHARS = 220;
641279
+ STAGE_FALLBACK_COLOR = {
641280
+ idle: 39,
641281
+ planning: 141,
641282
+ tool_call: 45,
641283
+ reading: 39,
641284
+ searching: 129,
641285
+ editing: 42,
641286
+ writing: 43,
641287
+ testing: 82,
641288
+ building: 220,
641289
+ verifying: 214,
641290
+ reflecting: 177,
641291
+ compacting: 51,
641292
+ adversary_audit: 207,
641293
+ blocked: 208,
641294
+ completed: 46,
641295
+ failed: 196
641296
+ };
641297
+ }
641298
+ });
641299
+
641067
641300
  // packages/cli/src/tui/tui-tasks-renderer.ts
641068
641301
  var tui_tasks_renderer_exports = {};
641069
641302
  __export(tui_tasks_renderer_exports, {
@@ -643701,6 +643934,7 @@ __export(status_bar_exports, {
643701
643934
  refreshThemeVars: () => refreshThemeVars,
643702
643935
  setTermTitleWriter: () => setTermTitleWriter,
643703
643936
  setTerminalTitle: () => setTerminalTitle,
643937
+ stripSubAgentPrefix: () => stripSubAgentPrefix,
643704
643938
  unlockFooterRedraws: () => unlockFooterRedraws
643705
643939
  });
643706
643940
  import { basename as basename30, dirname as dirname44 } from "node:path";
@@ -643725,7 +643959,8 @@ function headerTelegramFg() {
643725
643959
  return themeMode() === "system" ? "\x1B[39m" : "\x1B[38;5;45m";
643726
643960
  }
643727
643961
  function stripSubAgentPrefix(label) {
643728
- const stripped = label.replace(/^\s*sub[-\s]?agents?\b[\s:_–—-]*/i, "").trim();
643962
+ let stripped = label.replace(/^\s*sub[-\s]?agents?\b[\s:_–—-]*/i, "").trim();
643963
+ stripped = stripped.replace(/^omnius-/i, "").trim();
643729
643964
  return stripped.length > 0 ? stripped : label.trim();
643730
643965
  }
643731
643966
  function xterm256ToRgb2(i2) {
@@ -643823,6 +644058,7 @@ var init_status_bar = __esm({
643823
644058
  "packages/cli/src/tui/status-bar.ts"() {
643824
644059
  init_render();
643825
644060
  init_stageIndicator();
644061
+ init_sub_agent_live_block();
643826
644062
  init_tui_tasks_renderer();
643827
644063
  init_braille_spinner();
643828
644064
  init_project_context();
@@ -644063,6 +644299,8 @@ var init_status_bar = __esm({
644063
644299
  _activeViewId = "main";
644064
644300
  _headerExpanded = false;
644065
644301
  // toggles sub-agent / scroll info below header
644302
+ _subAgentLiveBlockId = "sub-agent-live-preview";
644303
+ _subAgentLiveBlockAppended = false;
644066
644304
  // Virtual scrollback buffer — stores content lines for Page Up/Down scrolling.
644067
644305
  // Since we're in alternate screen buffer, there's no native scrollback.
644068
644306
  // NOTE: _contentLines is now a reference to the ACTIVE view's buffer.
@@ -644675,6 +644913,46 @@ var init_status_bar = __esm({
644675
644913
  * - Category A: Version + menu buttons (help/voice/model/endpoint/sponsor)
644676
644914
  * - Category B: Systems (agents/voice status/nexus status)
644677
644915
  * Each category paginates independently across N pages. */
644916
+ /** Filled header button whose hue band follows the child agent's current action. */
644917
+ paintAgentButton(content, stage2, active) {
644918
+ const useTruecolor = supportsTruecolor();
644919
+ const chars = Array.from(content);
644920
+ let body = "";
644921
+ for (let i2 = 0; i2 < chars.length; i2++) {
644922
+ const char = chars[i2];
644923
+ if (useTruecolor) {
644924
+ const [r2, g, b] = stageGradientRgb(stage2, i2, this._stagePhase);
644925
+ const luma = 0.299 * r2 + 0.587 * g + 0.114 * b;
644926
+ const fg2 = luma >= 140 ? "\x1B[38;2;16;16;16m" : "\x1B[38;2;255;255;255m";
644927
+ body += `${active ? "\x1B[1m" : ""}${fg2}\x1B[48;2;${r2};${g};${b}m${char}\x1B[0m`;
644928
+ } else {
644929
+ const fallback = stageFallbackColor(stage2);
644930
+ body += `${active ? "\x1B[1m" : ""}\x1B[38;5;${contrastTextColor(fallback)}m\x1B[48;5;${fallback}m${char}\x1B[0m`;
644931
+ }
644932
+ }
644933
+ const [lr, lg, lb] = stageGradientRgb(stage2, 0, this._stagePhase);
644934
+ const leftBorder = useTruecolor ? `\x1B[38;2;${lr};${lg};${lb}m` : `\x1B[38;5;${stageFallbackColor(stage2)}m`;
644935
+ const [rr, rg, rb] = stageGradientRgb(
644936
+ stage2,
644937
+ Math.max(0, chars.length - 1),
644938
+ this._stagePhase
644939
+ );
644940
+ const rightBorder = useTruecolor ? `\x1B[38;2;${rr};${rg};${rb}m` : `\x1B[38;5;${stageFallbackColor(stage2)}m`;
644941
+ return `${leftBorder}${HEADER_BUTTON_LEFT}${RESET4}` + body + `${RESET4}${PANEL_BG_SEQ}${rightBorder}${HEADER_BUTTON_RIGHT}${RESET4}${PANEL_BG_SEQ}`;
644942
+ }
644943
+ paintAgentText(value2, stage2, offset = 0) {
644944
+ const chars = Array.from(value2);
644945
+ let out = "";
644946
+ for (let i2 = 0; i2 < chars.length; i2++) {
644947
+ if (supportsTruecolor()) {
644948
+ const [r2, g, b] = stageGradientRgb(stage2, i2 + offset, this._stagePhase);
644949
+ out += `\x1B[38;2;${r2};${g};${b}m${chars[i2]}`;
644950
+ } else {
644951
+ out += `\x1B[38;5;${stageFallbackColor(stage2)}m${chars[i2]}`;
644952
+ }
644953
+ }
644954
+ return `${out}${RESET4}`;
644955
+ }
644678
644956
  _rebuildHeaderPanels() {
644679
644957
  this._headerPanels = [];
644680
644958
  const savedIdx = this._headerPanelIndex;
@@ -644686,12 +644964,6 @@ var init_status_bar = __esm({
644686
644964
  const decorateMenuButton = (cmd, label) => {
644687
644965
  return `${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_LEFT}\x1B[0m${HEADER_BUTTON_BG}${HEADER_BUTTON_FG}${label}\x1B[0m${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`;
644688
644966
  };
644689
- const decorateAgentButton = (content, color, active) => {
644690
- const bg = `\x1B[48;5;${color}m`;
644691
- const fg2 = `\x1B[38;5;${contrastTextColor(color)}m`;
644692
- const weight = active ? "\x1B[1m" : "";
644693
- return `\x1B[38;5;${color}m${HEADER_BUTTON_LEFT}\x1B[0m${weight}${fg2}${bg}${content}\x1B[0m${PANEL_BG_SEQ}\x1B[38;5;${color}m${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`;
644694
- };
644695
644967
  const renderBtn = (cmd, label) => {
644696
644968
  return linkify(cmd, decorateMenuButton(cmd, label));
644697
644969
  };
@@ -644779,9 +645051,8 @@ var init_status_bar = __esm({
644779
645051
  if (view.id === "main" && this._activeViewId === "main") continue;
644780
645052
  const icon = view.status === "running" ? "●" : view.status === "completed" ? "✓" : view.status === "failed" ? "✗" : "○";
644781
645053
  const content = ` ${trunc3(view.label)} ${icon} `;
644782
- const viewColor = view.colorCode ?? (view.type === "telegram" ? 45 : 213);
644783
645054
  const active = view.id === this._activeViewId;
644784
- const btn = decorateAgentButton(content, viewColor, active);
645055
+ const btn = this.paintAgentButton(content, view.stage, active);
644785
645056
  sysItems.push({
644786
645057
  render: () => linkify(`view:${view.id}`, btn) + " ",
644787
645058
  w: content.length + 2 + 1
@@ -645032,10 +645303,24 @@ var init_status_bar = __esm({
645032
645303
  buf += `\x1B[${subRow};1H${PANEL_BG_SEQ}\x1B[2K`;
645033
645304
  buf += `${BOX_FG}│${RESET4}${PANEL_BG_SEQ}`;
645034
645305
  buf += `\x1B[38;5;${TEXT_DIM}m${PANEL_BG_SEQ}`;
645035
- const subAgents = this._agentViews.size > 1 ? Array.from(this._agentViews.values()).filter((v) => v.id !== "main").map(
645036
- (v) => `${v.status === "running" ? "●" : v.status === "completed" ? "✓" : "○"} ${v.label}`
645037
- ).join(" ") : "no sub-agents";
645038
- buf += ` ${subAgents.substring(0, w - 4)}`;
645306
+ if (this._agentViews.size > 1) {
645307
+ let used = 1;
645308
+ for (const view of Array.from(this._agentViews.values()).filter(
645309
+ (candidate) => candidate.id !== "main"
645310
+ )) {
645311
+ const icon = view.status === "running" ? "●" : view.status === "completed" ? "✓" : view.status === "failed" ? "✗" : "○";
645312
+ const raw = `${icon} ${view.label}`;
645313
+ const gap = used > 1 ? " " : " ";
645314
+ const remaining = Math.max(0, w - 2 - used);
645315
+ if (remaining <= 0) break;
645316
+ const clipped = raw.length > remaining ? `${raw.slice(0, Math.max(0, remaining - 1))}…` : raw;
645317
+ buf += `${PANEL_BG_SEQ}${this.paintAgentText(`${gap}${clipped}`, view.stage, used)}`;
645318
+ used += gap.length + clipped.length;
645319
+ if (clipped !== raw) break;
645320
+ }
645321
+ } else {
645322
+ buf += ` ${NO_SUB_AGENTS_HEADER_LABEL.trim()}`;
645323
+ }
645039
645324
  buf += `\x1B[${subRow};${w}H${BOX_FG}│${RESET4}${PANEL_BG_SEQ}`;
645040
645325
  const scrollRow = subRow + 1;
645041
645326
  let expandedBottom = `${BOX_BL3}${BOX_H3.repeat(w - 2)}${BOX_BR3}`;
@@ -645176,6 +645461,9 @@ var init_status_bar = __esm({
645176
645461
  this.advanceStagePhase();
645177
645462
  this.renderFooterPreserveCursor();
645178
645463
  refreshTuiTasksAnimationFrame();
645464
+ if (this._agentViews.size > 1 && (String(this.currentHeaderPanel).startsWith("sys-") || this._headerExpanded)) {
645465
+ this.refreshHeaderContent();
645466
+ }
645179
645467
  }, _StatusBar.FOOTER_ANIMATION_INTERVAL_MS);
645180
645468
  this._footerAnimationTimer.unref?.();
645181
645469
  }
@@ -645752,9 +646040,13 @@ var init_status_bar = __esm({
645752
646040
  scrollOffset: 0,
645753
646041
  status: "running",
645754
646042
  taskSummary: "",
645755
- startedAt: Date.now()
646043
+ startedAt: Date.now(),
646044
+ stage: "idle",
646045
+ activity: [],
646046
+ updatedAt: Date.now()
645756
646047
  });
645757
646048
  }
646049
+ if (this._agentViews.size > 1) this.ensureSubAgentLiveBlock();
645758
646050
  setTermSize(process.stdout.rows ?? 24, process.stdout.columns ?? 80);
645759
646051
  setFooterMetricsVisible(this._footerMetricsVisible);
645760
646052
  this._prevTermRows = termRows();
@@ -646368,8 +646660,101 @@ var init_status_bar = __esm({
646368
646660
  }, 1500);
646369
646661
  }
646370
646662
  // ── Agent View Management (WO-NA1) ────────────────────────────
646663
+ /** Keep one compact, live child-agent preview in the MAIN view. */
646664
+ ensureSubAgentLiveBlock() {
646665
+ if (!this.active || !this._agentViews.has("main") || this._agentViews.size <= 1)
646666
+ return;
646667
+ if (this._subAgentLiveBlockAppended) return;
646668
+ this.registerDynamicBlock(
646669
+ this._subAgentLiveBlockId,
646670
+ (width) => buildSubAgentLiveBlockLines(
646671
+ Array.from(this._agentViews.values()).filter((view) => view.id !== "main"),
646672
+ width,
646673
+ this._stagePhase
646674
+ )
646675
+ );
646676
+ this.appendDynamicBlockToAgentView("main", this._subAgentLiveBlockId);
646677
+ this._subAgentLiveBlockAppended = true;
646678
+ }
646679
+ removeSubAgentLiveBlock() {
646680
+ if (!this._subAgentLiveBlockAppended) return;
646681
+ const main2 = this._agentViews.get("main");
646682
+ const marker = `${this.DYNAMIC_BLOCK_MARK_PREFIX}${this._subAgentLiveBlockId}${this.DYNAMIC_BLOCK_MARK_SUFFIX}`;
646683
+ if (main2) {
646684
+ for (let i2 = main2.contentLines.length - 1; i2 >= 0; i2--) {
646685
+ if (main2.contentLines[i2] === marker) main2.contentLines.splice(i2, 1);
646686
+ }
646687
+ }
646688
+ this.unregisterDynamicBlock(this._subAgentLiveBlockId);
646689
+ this._subAgentLiveBlockAppended = false;
646690
+ if (this.active && this._activeViewId === "main") {
646691
+ this.markContentMutated();
646692
+ this.repaintContent();
646693
+ }
646694
+ }
646695
+ /** Update a child-agent's action state and rolling live preview line. */
646696
+ updateAgentViewActivity(id2, stage2, detail, activity) {
646697
+ const view = this._agentViews.get(id2);
646698
+ if (!view || view.id === "main") return;
646699
+ view.stage = stage2;
646700
+ view.stageDetail = detail?.trim() || void 0;
646701
+ view.updatedAt = Date.now();
646702
+ if (activity) appendSubAgentActivity(view, activity);
646703
+ this.ensureSubAgentLiveBlock();
646704
+ if (this.active) {
646705
+ this.renderAgentTabs();
646706
+ if (this._headerExpanded) this.refreshHeaderContent();
646707
+ this.refreshDynamicBlocks();
646708
+ }
646709
+ }
646710
+ /** Structured-event adapter used by in-process child runners. */
646711
+ updateAgentViewEvent(id2, event, activity) {
646712
+ const view = this._agentViews.get(id2);
646713
+ if (!view) return;
646714
+ const transition = stageForAgentEvent(event);
646715
+ this.updateAgentViewActivity(
646716
+ id2,
646717
+ transition?.stage ?? view.stage,
646718
+ transition?.detail ?? view.stageDetail,
646719
+ activity
646720
+ );
646721
+ }
646722
+ /** Forward a raw full-sub-agent stdout chunk into the same state model. */
646723
+ recordAgentOutput(id2, text2) {
646724
+ const line = text2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").split(/\r?\n/).map((value2) => value2.trim()).filter(Boolean).at(-1);
646725
+ if (!line) {
646726
+ this.writeToAgentView(id2, text2);
646727
+ return;
646728
+ }
646729
+ const lower = line.toLowerCase();
646730
+ let stage2 = "tool_call";
646731
+ if (/\b(error|failed|failure|exception|blocked)\b/.test(lower)) stage2 = "failed";
646732
+ else if (/\b(complete|completed|done|success|succeeded)\b/.test(lower)) stage2 = "completed";
646733
+ else if (/\b(file_read|read|grep|search|find)\b/.test(lower)) stage2 = "reading";
646734
+ else if (/\b(file_write|file_edit|file_patch|batch_edit|write|edit|patch)\b/.test(lower)) stage2 = "writing";
646735
+ else if (/\b(shell|build|test|compile|npm|pnpm|pio)\b/.test(lower)) stage2 = "building";
646736
+ this.updateAgentViewActivity(id2, stage2, line.slice(0, 40), line);
646737
+ this.writeToAgentView(id2, text2);
646738
+ }
646371
646739
  /** Register a new sub-agent view with its own content buffer */
646372
646740
  registerAgentView(id2, label, type, taskSummary) {
646741
+ const existing = this._agentViews.get(id2);
646742
+ if (existing) {
646743
+ if (existing.id !== "main") {
646744
+ const cleanExistingLabel = stripSubAgentPrefix(existing.label);
646745
+ existing.label = cleanExistingLabel || stripSubAgentPrefix(label);
646746
+ }
646747
+ existing.taskSummary = taskSummary ?? existing.taskSummary;
646748
+ existing.status = "running";
646749
+ existing.updatedAt = Date.now();
646750
+ this.ensureSubAgentLiveBlock();
646751
+ if (this.active) {
646752
+ this._rebuildHeaderPanels?.();
646753
+ this.renderFooterAndPositionInput();
646754
+ this.refreshHeaderContent();
646755
+ }
646756
+ return;
646757
+ }
646373
646758
  this._agentViews.set(id2, {
646374
646759
  id: id2,
646375
646760
  label: stripSubAgentPrefix(label),
@@ -646379,9 +646764,14 @@ var init_status_bar = __esm({
646379
646764
  status: "running",
646380
646765
  taskSummary: taskSummary ?? "",
646381
646766
  startedAt: Date.now(),
646382
- colorCode: type === "telegram" ? 45 : this.nextAgentColor()
646767
+ colorCode: type === "telegram" ? 45 : this.nextAgentColor(),
646768
+ stage: "planning",
646769
+ stageDetail: "spawn",
646770
+ activity: ["spawned; waiting for first action"],
646771
+ updatedAt: Date.now()
646383
646772
  });
646384
646773
  if (this.active) {
646774
+ this.ensureSubAgentLiveBlock();
646385
646775
  this.applyScrollRegion();
646386
646776
  this._rebuildHeaderPanels?.();
646387
646777
  this.renderFooterAndPositionInput();
@@ -646391,6 +646781,7 @@ var init_status_bar = __esm({
646391
646781
  /** Remove a sub-agent view */
646392
646782
  unregisterAgentView(id2) {
646393
646783
  this._agentViews.delete(id2);
646784
+ if (this._agentViews.size <= 1) this.removeSubAgentLiveBlock();
646394
646785
  if (this._activeViewId === id2) this.switchToView("main");
646395
646786
  if (this.active) {
646396
646787
  this.applyScrollRegion();
@@ -646404,9 +646795,27 @@ var init_status_bar = __esm({
646404
646795
  const view = this._agentViews.get(id2);
646405
646796
  if (!view) return;
646406
646797
  view.status = status;
646407
- if (status === "completed" || status === "failed")
646798
+ if (status === "completed") {
646799
+ view.completedAt = Date.now();
646800
+ view.stage = "completed";
646801
+ view.stageDetail = "done";
646802
+ appendSubAgentActivity(view, "completed");
646803
+ } else if (status === "failed") {
646408
646804
  view.completedAt = Date.now();
646409
- if (this.active) this.renderAgentTabs();
646805
+ view.stage = "failed";
646806
+ view.stageDetail = "failed";
646807
+ appendSubAgentActivity(view, "failed");
646808
+ } else if (status === "paused" || status === "stopped") {
646809
+ view.stage = "blocked";
646810
+ view.stageDetail = status;
646811
+ appendSubAgentActivity(view, status);
646812
+ }
646813
+ view.updatedAt = Date.now();
646814
+ if (this.active) {
646815
+ this.renderAgentTabs();
646816
+ if (this._headerExpanded) this.refreshHeaderContent();
646817
+ this.refreshDynamicBlocks();
646818
+ }
646410
646819
  }
646411
646820
  /** Switch which agent's content buffer is displayed */
646412
646821
  switchToView(id2) {
@@ -653352,9 +653761,9 @@ function formatExpandedContextDiagnostic(specs, math) {
653352
653761
  memBits.push(`RAM ${fmtGB(specs.availableRamGB)}/${fmtGB(specs.totalRamGB)}${specs.unifiedMemory ? " unified" : ""}`);
653353
653762
  const mem = memBits.join(", ");
653354
653763
  const kv = `KV ${fmtKB(math.kvBytesPerToken)}/tok (${math.kvSource})`;
653355
- const fit3 = `fit ${fmtK(math.memoryFit)}, arch ${math.archCtx !== null ? fmtK(math.archCtx) : "n/a"}, floor ${fmtK(math.floor)}`;
653764
+ const fit4 = `fit ${fmtK(math.memoryFit)}, arch ${math.archCtx !== null ? fmtK(math.archCtx) : "n/a"}, floor ${fmtK(math.floor)}`;
653356
653765
  const limit = `→ ${fmtK(math.numCtx)} (${math.limitedBy === "floor" ? "min floor" : math.limitedBy === "arch" ? "arch-capped" : "memory-fit"})`;
653357
- return `[${mem} | model ${fmtGB(math.modelSizeGB)} | ${kv} | ${fit3} ${limit}]`;
653766
+ return `[${mem} | model ${fmtGB(math.modelSizeGB)} | ${kv} | ${fit4} ${limit}]`;
653358
653767
  }
653359
653768
  async function ensureExpandedContext(modelName, backendUrl2) {
653360
653769
  if (modelName.includes("cloud") || modelName.includes(":cloud")) {
@@ -653633,7 +654042,7 @@ export PATH="${binDir}:$PATH" # Added by omnius for nvim
653633
654042
  } catch {
653634
654043
  }
653635
654044
  }
653636
- var execAsync2, OMNIUS_FIRST_RUN_BANNER, ANSI_RE2, visibleLen2, SETUP_MODEL_VARIANTS, _toolSupportCache, EXPANDED_VARIANT_MIN_NUM_CTX, _cloudflaredInstallPromise;
654045
+ var execAsync2, OMNIUS_FIRST_RUN_BANNER, ANSI_RE3, visibleLen2, SETUP_MODEL_VARIANTS, _toolSupportCache, EXPANDED_VARIANT_MIN_NUM_CTX, _cloudflaredInstallPromise;
653637
654046
  var init_setup = __esm({
653638
654047
  "packages/cli/src/tui/setup.ts"() {
653639
654048
  init_model_picker();
@@ -653652,8 +654061,8 @@ var init_setup = __esm({
653652
654061
  "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ",
653653
654062
  " ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓██████▓▒░░▒▓███████▓▒░ "
653654
654063
  ].join("\n");
653655
- ANSI_RE2 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
653656
- visibleLen2 = (value2) => Array.from(value2.replace(ANSI_RE2, "")).length;
654064
+ ANSI_RE3 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
654065
+ visibleLen2 = (value2) => Array.from(value2.replace(ANSI_RE3, "")).length;
653657
654066
  SETUP_MODEL_VARIANTS = [
653658
654067
  { tag: "robit/ornith:9b", sizeGB: 6.6, label: "9B params (6.6 GB) - recommended minimum", cloud: false },
653659
654068
  { tag: "robit/ornith:35b", sizeGB: 24, label: "35B params (24 GB) - recommended on 32GB+ unified memory/VRAM", cloud: false }
@@ -674324,12 +674733,12 @@ function cad3dFitIcon(score) {
674324
674733
  if (score >= 40) return c3.yellow("△");
674325
674734
  return c3.red("✖");
674326
674735
  }
674327
- function cad3dModelDetail(spec, fit3) {
674736
+ function cad3dModelDetail(spec, fit4) {
674328
674737
  const download = spec.resources.approxDownloadGB ? `~${spec.resources.approxDownloadGB}GB` : "size unknown";
674329
674738
  const vram = spec.resources.recommendedVramGB ? `${spec.resources.recommendedVramGB}GB VRAM rec` : "VRAM varies";
674330
674739
  const license = spec.hf.license ? `license ${spec.hf.license}` : "license unknown";
674331
674740
  const token = spec.deployment.requiresToken ? "requires HF token" : "no token gate";
674332
- return `${fit3.label} · ${spec.backend} · ${download} · ${vram} · ${license} · ${token}`;
674741
+ return `${fit4.label} · ${spec.backend} · ${download} · ${vram} · ${license} · ${token}`;
674333
674742
  }
674334
674743
  async function startCad3dViewerFromCommand(rawPath) {
674335
674744
  const viewer = await startCadModelViewer(
@@ -674345,11 +674754,11 @@ async function showCad3dModelsMenu(ctx3, hasLocal, mode) {
674345
674754
  const active = mode === "cad" ? settings.cadModel : settings.model3dModel;
674346
674755
  const currentViewer2 = getCurrentCadModelViewer();
674347
674756
  const buildItem = (entry) => {
674348
- const fit3 = rateCad3dModelForHardware(entry.spec, specs);
674757
+ const fit4 = rateCad3dModelForHardware(entry.spec, specs);
674349
674758
  return {
674350
674759
  key: `model:${entry.spec.id}`,
674351
- label: `${cad3dFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${entry.spec.label}`,
674352
- detail: cad3dModelDetail(entry.spec, fit3)
674760
+ label: `${cad3dFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${entry.spec.label}`,
674761
+ detail: cad3dModelDetail(entry.spec, fit4)
674353
674762
  };
674354
674763
  };
674355
674764
  const items = [
@@ -674384,11 +674793,11 @@ async function showCad3dModelsMenu(ctx3, hasLocal, mode) {
674384
674793
  const patch = mode === "cad" ? { cadModel: id2, cadBackend: entry.spec.backend } : { model3dModel: id2, model3dBackend: entry.spec.backend };
674385
674794
  const save3 = hasLocal ? ctx3.saveLocalSettings : ctx3.saveSettings;
674386
674795
  save3(patch);
674387
- const fit3 = rateCad3dModelForHardware(entry.spec, specs);
674796
+ const fit4 = rateCad3dModelForHardware(entry.spec, specs);
674388
674797
  renderInfo(
674389
674798
  `${mode === "cad" ? "CAD" : "3D"} model: ${id2} (${entry.spec.backend})${hasLocal ? " (project-local)" : ""}`
674390
674799
  );
674391
- renderInfo(`Hardware fit: ${fit3.score}/100 ${fit3.label} - ${fit3.note}`);
674800
+ renderInfo(`Hardware fit: ${fit4.score}/100 ${fit4.label} - ${fit4.note}`);
674392
674801
  if (entry.spec.resources.approxDownloadGB) {
674393
674802
  renderInfo(
674394
674803
  `Download estimate: ~${entry.spec.resources.approxDownloadGB}GB in ${unifiedModelStoreDir()}`
@@ -674539,19 +674948,19 @@ async function renderImageModelList(ctx3) {
674539
674948
  renderInfo("");
674540
674949
  renderInfo(c3.bold(category));
674541
674950
  for (const preset of presets) {
674542
- const fit3 = rateImagePresetForHardware(preset, specs);
674951
+ const fit4 = rateImagePresetForHardware(preset, specs);
674543
674952
  const primary = category === "Primary hyper-realistic baseline" ? c3.cyan(" ★") : "";
674544
674953
  const disk = ctx3 ? imageModelDiskStats(ctx3, preset, ollamaSizes) : { downloaded: false, bytes: 0, paths: [] };
674545
674954
  const diskInfo = disk.downloaded ? ` ${c3.green("✓")} ${formatFileSize(disk.bytes)}` : "";
674546
674955
  renderInfo(
674547
- `${imageFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${c3.bold(preset.label)}${primary}${diskInfo}`
674956
+ `${imageFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${c3.bold(preset.label)}${primary}${diskInfo}`
674548
674957
  );
674549
674958
  renderInfo(` id: ${preset.id}`);
674550
674959
  renderInfo(
674551
- ` type: ${preset.backend} · ${preset.sizeClass ?? "unknown size"} · ${fit3.label}`
674960
+ ` type: ${preset.backend} · ${preset.sizeClass ?? "unknown size"} · ${fit4.label}`
674552
674961
  );
674553
674962
  renderImagePresetDetail(" quality: ", preset.quality ?? preset.note);
674554
- renderImagePresetDetail(" fit: ", fit3.note);
674963
+ renderImagePresetDetail(" fit: ", fit4.note);
674555
674964
  if (preset.deployment)
674556
674965
  renderImagePresetDetail(" deploy: ", preset.deployment);
674557
674966
  }
@@ -674694,13 +675103,13 @@ async function showImageModelsMenu(ctx3, hasLocal) {
674694
675103
  () => /* @__PURE__ */ new Map()
674695
675104
  );
674696
675105
  const buildModelItem = (preset) => {
674697
- const fit3 = rateImagePresetForHardware(preset, specs);
675106
+ const fit4 = rateImagePresetForHardware(preset, specs);
674698
675107
  const disk = imageModelDiskStats(ctx3, preset, ollamaSizes);
674699
675108
  const downloaded = disk.downloaded ? `${c3.green("✓")} ` : "";
674700
675109
  return {
674701
675110
  key: `model:${preset.id}`,
674702
- label: `${downloaded}${imageFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${preset.label}`,
674703
- detail: `${fit3.score}/100 ${fit3.label} · ${preset.category ?? preset.backend} · ${preset.sizeClass ?? preset.id}${downloadedModelSuffix(disk)}`
675111
+ label: `${downloaded}${imageFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${preset.label}`,
675112
+ detail: `${fit4.score}/100 ${fit4.label} · ${preset.category ?? preset.backend} · ${preset.sizeClass ?? preset.id}${downloadedModelSuffix(disk)}`
674704
675113
  };
674705
675114
  };
674706
675115
  const items = [
@@ -674785,9 +675194,9 @@ async function showImageModelsMenu(ctx3, hasLocal) {
674785
675194
  renderInfo(
674786
675195
  `Image model: ${model} (${backend})${hasLocal ? " (project-local)" : ""}`
674787
675196
  );
674788
- const fit3 = preset ? rateImagePresetForHardware(preset, specs) : void 0;
674789
- if (fit3)
674790
- renderInfo(`Hardware fit: ${fit3.score}/100 ${fit3.label} — ${fit3.note}`);
675197
+ const fit4 = preset ? rateImagePresetForHardware(preset, specs) : void 0;
675198
+ if (fit4)
675199
+ renderInfo(`Hardware fit: ${fit4.score}/100 ${fit4.label} — ${fit4.note}`);
674791
675200
  if (preset?.install) renderInfo(`Prewarm command: ${preset.install}`);
674792
675201
  await prewarmImageModel(ctx3, model, backend);
674793
675202
  }
@@ -675103,19 +675512,19 @@ async function renderVideoModelList(ctx3) {
675103
675512
  renderInfo("");
675104
675513
  renderInfo(c3.bold(category));
675105
675514
  for (const preset of presets) {
675106
- const fit3 = rateVideoPresetForHardware(preset, specs);
675515
+ const fit4 = rateVideoPresetForHardware(preset, specs);
675107
675516
  const disk = videoModelDiskStats(ctx3, preset);
675108
675517
  const diskInfo = disk.downloaded ? ` ${c3.green("✓")} ${formatFileSize(disk.bytes)}` : "";
675109
675518
  renderInfo(
675110
- `${imageFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${c3.bold(preset.label)}${diskInfo}`
675519
+ `${imageFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${c3.bold(preset.label)}${diskInfo}`
675111
675520
  );
675112
675521
  renderInfo(` id: ${preset.id}`);
675113
675522
  renderInfo(
675114
- ` type: ${preset.backend} · ${preset.sizeClass} · ${preset.kinds.join("/")} · ${fit3.label}`
675523
+ ` type: ${preset.backend} · ${preset.sizeClass} · ${preset.kinds.join("/")} · ${fit4.label}`
675115
675524
  );
675116
675525
  renderImagePresetDetail(" quality: ", preset.quality);
675117
675526
  renderImagePresetDetail(" output: ", preset.output);
675118
- renderImagePresetDetail(" fit: ", fit3.note);
675527
+ renderImagePresetDetail(" fit: ", fit4.note);
675119
675528
  renderImagePresetDetail(" deploy: ", preset.deployment);
675120
675529
  if (preset.licenseNote)
675121
675530
  renderImagePresetDetail(" license: ", preset.licenseNote);
@@ -675126,13 +675535,13 @@ async function showVideoModelsMenu(ctx3, hasLocal) {
675126
675535
  const settings = resolveSettings(ctx3.repoRoot);
675127
675536
  const specs = await detectSystemSpecsAsync();
675128
675537
  const buildModelItem = (preset) => {
675129
- const fit3 = rateVideoPresetForHardware(preset, specs);
675538
+ const fit4 = rateVideoPresetForHardware(preset, specs);
675130
675539
  const disk = videoModelDiskStats(ctx3, preset);
675131
675540
  const downloaded = disk.downloaded ? `${c3.green("✓")} ` : "";
675132
675541
  return {
675133
675542
  key: `model:${preset.id}`,
675134
- label: `${downloaded}${imageFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${preset.label}`,
675135
- detail: `${fit3.label} · ${preset.category} · ${preset.kinds.join("/")}${downloadedModelSuffix(disk)}`
675543
+ label: `${downloaded}${imageFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${preset.label}`,
675544
+ detail: `${fit4.label} · ${preset.category} · ${preset.kinds.join("/")}${downloadedModelSuffix(disk)}`
675136
675545
  };
675137
675546
  };
675138
675547
  const items = [
@@ -675211,8 +675620,8 @@ async function showVideoModelsMenu(ctx3, hasLocal) {
675211
675620
  `Video model: ${model} (${backend})${hasLocal ? " (project-local)" : ""}`
675212
675621
  );
675213
675622
  if (preset) {
675214
- const fit3 = rateVideoPresetForHardware(preset, specs);
675215
- renderInfo(`Hardware fit: ${fit3.score}/100 ${fit3.label} — ${fit3.note}`);
675623
+ const fit4 = rateVideoPresetForHardware(preset, specs);
675624
+ renderInfo(`Hardware fit: ${fit4.score}/100 ${fit4.label} — ${fit4.note}`);
675216
675625
  if (preset.licenseNote) renderInfo(`License: ${preset.licenseNote}`);
675217
675626
  if (preset.install) renderInfo(`Prewarm command: ${preset.install}`);
675218
675627
  }
@@ -675474,19 +675883,19 @@ async function renderAudioModelList(ctx3, kind) {
675474
675883
  renderInfo("");
675475
675884
  renderInfo(c3.bold(category));
675476
675885
  for (const preset of presets) {
675477
- const fit3 = rateAudioPresetForHardware(preset, specs);
675886
+ const fit4 = rateAudioPresetForHardware(preset, specs);
675478
675887
  const disk = audioModelDiskStats(ctx3, preset);
675479
675888
  const diskInfo = disk.downloaded ? ` ${c3.green("✓")} ${formatFileSize(disk.bytes)}` : "";
675480
675889
  renderInfo(
675481
- `${audioFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${c3.bold(preset.label)}${diskInfo}`
675890
+ `${audioFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${c3.bold(preset.label)}${diskInfo}`
675482
675891
  );
675483
675892
  renderInfo(` id: ${preset.id}`);
675484
675893
  renderInfo(
675485
- ` type: ${preset.backend} · ${preset.sizeClass} · ${fit3.label}`
675894
+ ` type: ${preset.backend} · ${preset.sizeClass} · ${fit4.label}`
675486
675895
  );
675487
675896
  renderImagePresetDetail(" quality: ", preset.quality);
675488
675897
  renderImagePresetDetail(" output: ", preset.output);
675489
- renderImagePresetDetail(" fit: ", fit3.note);
675898
+ renderImagePresetDetail(" fit: ", fit4.note);
675490
675899
  renderImagePresetDetail(" deploy: ", preset.deployment);
675491
675900
  }
675492
675901
  }
@@ -675511,13 +675920,13 @@ async function showAudioGenerationMenu(ctx3, hasLocal, kind) {
675511
675920
  const activeModel = activeAudioModel(settings, kind);
675512
675921
  const title = kind === "music" ? "Music Generation" : "Sound Generation";
675513
675922
  const buildModelItem = (preset) => {
675514
- const fit3 = rateAudioPresetForHardware(preset, specs);
675923
+ const fit4 = rateAudioPresetForHardware(preset, specs);
675515
675924
  const disk = audioModelDiskStats(ctx3, preset);
675516
675925
  const downloaded = disk.downloaded ? `${c3.green("✓")} ` : "";
675517
675926
  return {
675518
675927
  key: `model:${preset.id}`,
675519
- label: `${downloaded}${audioFitIcon(fit3.score)} ${String(fit3.score).padStart(3)}/100 ${preset.label}`,
675520
- detail: `${fit3.label} · ${preset.category} · ${preset.sizeClass}${downloadedModelSuffix(disk)}`
675928
+ label: `${downloaded}${audioFitIcon(fit4.score)} ${String(fit4.score).padStart(3)}/100 ${preset.label}`,
675929
+ detail: `${fit4.label} · ${preset.category} · ${preset.sizeClass}${downloadedModelSuffix(disk)}`
675521
675930
  };
675522
675931
  };
675523
675932
  const setupItems = kind === "music" ? [
@@ -675638,8 +676047,8 @@ async function showAudioGenerationMenu(ctx3, hasLocal, kind) {
675638
676047
  `${kind === "music" ? "Music" : "Sound"} model: ${model} (${backend})${hasLocal ? " (project-local)" : ""}`
675639
676048
  );
675640
676049
  if (preset) {
675641
- const fit3 = rateAudioPresetForHardware(preset, specs);
675642
- renderInfo(`Hardware fit: ${fit3.score}/100 ${fit3.label} - ${fit3.note}`);
676050
+ const fit4 = rateAudioPresetForHardware(preset, specs);
676051
+ renderInfo(`Hardware fit: ${fit4.score}/100 ${fit4.label} - ${fit4.note}`);
675643
676052
  renderInfo(`Prewarm command: ${preset.install}`);
675644
676053
  }
675645
676054
  await prewarmAudioModel(ctx3, kind, model, backend);
@@ -700975,10 +701384,10 @@ ${message2}`)
700975
701384
  telegramModelMenuItems(generation) {
700976
701385
  return this.telegramGenerationModelDescriptors(generation).map((model) => {
700977
701386
  const cached = model.cachedBytes > 0 ? `cached ${formatModelBytes(model.cachedBytes)}` : "not downloaded";
700978
- const fit3 = model.recommendedVramGB ? `${model.recommendedVramGB}GB VRAM rec` : model.minVramGB ? `${model.minVramGB}GB VRAM min` : "VRAM n/a";
701387
+ const fit4 = model.recommendedVramGB ? `${model.recommendedVramGB}GB VRAM rec` : model.minVramGB ? `${model.minVramGB}GB VRAM min` : "VRAM n/a";
700979
701388
  return {
700980
701389
  label: model.label,
700981
- description: `${model.backend} - ${model.category} - ${fit3} - ${cached}`,
701390
+ description: `${model.backend} - ${model.category} - ${fit4} - ${cached}`,
700982
701391
  action: {
700983
701392
  type: "model_detail",
700984
701393
  generation,
@@ -715088,17 +715497,17 @@ function buildShellLiveBlockLines(state, width) {
715088
715497
  const body = visibleLines.slice(-state.maxRows);
715089
715498
  const rows = [
715090
715499
  top,
715091
- contentRow(`$ ${state.command}`, inner),
715092
- contentRow("", inner)
715500
+ contentRow2(`$ ${state.command}`, inner),
715501
+ contentRow2("", inner)
715093
715502
  ];
715094
715503
  if (hidden > 0)
715095
715504
  rows.push(
715096
- contentRow(`... ${hidden} earlier line${hidden === 1 ? "" : "s"}`, inner)
715505
+ contentRow2(`... ${hidden} earlier line${hidden === 1 ? "" : "s"}`, inner)
715097
715506
  );
715098
715507
  if (body.length === 0) {
715099
- rows.push(contentRow("(waiting for output)", inner));
715508
+ rows.push(contentRow2("(waiting for output)", inner));
715100
715509
  } else {
715101
- for (const line of body) rows.push(contentRow(line, inner));
715510
+ for (const line of body) rows.push(contentRow2(line, inner));
715102
715511
  }
715103
715512
  rows.push(bottom);
715104
715513
  return rows;
@@ -715110,10 +715519,10 @@ function pushShellLiveLine(state, line) {
715110
715519
  state.lines.splice(0, state.lines.length - maxStored);
715111
715520
  }
715112
715521
  }
715113
- function contentRow(value2, inner) {
715114
- return `│ ${fit2(value2, inner)} │`;
715522
+ function contentRow2(value2, inner) {
715523
+ return `│ ${fit3(value2, inner)} │`;
715115
715524
  }
715116
- function fit2(value2, width) {
715525
+ function fit3(value2, width) {
715117
715526
  const plain = value2.replace(ANSI_OR_OSC_RE, "").replace(/\s+$/g, "");
715118
715527
  const chars = Array.from(plain);
715119
715528
  if (chars.length > width) {
@@ -748942,6 +749351,8 @@ function appendSubAgentNoteBox(statusBar, agentId, event, title, metrics2, conte
748942
749351
  );
748943
749352
  }
748944
749353
  function writeSubAgentEventForView(statusBar, agentId, event, verbose = false) {
749354
+ const previewLine = formatSubAgentEventForView(event);
749355
+ statusBar.updateAgentViewEvent(agentId, event, previewLine ?? event.type);
748945
749356
  switch (event.type) {
748946
749357
  case "tool_call": {
748947
749358
  subAgentPendingToolCalls.set(agentId, {
@@ -749025,7 +749436,7 @@ function writeSubAgentEventForView(statusBar, agentId, event, verbose = false) {
749025
749436
  return true;
749026
749437
  }
749027
749438
  default: {
749028
- const line = formatSubAgentEventForView(event);
749439
+ const line = previewLine;
749029
749440
  if (line) statusBar.writeToAgentView(agentId, line);
749030
749441
  return Boolean(line);
749031
749442
  }
@@ -750648,7 +751059,7 @@ ${content}
750648
751059
  });
750649
751060
  statusBar.ensureMonitorTimer();
750650
751061
  },
750651
- onViewWrite: (id2, text2) => statusBar.writeToAgentView(id2, text2),
751062
+ onViewWrite: (id2, text2) => statusBar.recordAgentOutput(id2, text2),
750652
751063
  onViewEvent: (id2, event) => writeSubAgentEventForView(statusBar, id2, event, config.verbose),
750653
751064
  onViewStatus: (id2, status) => {
750654
751065
  statusBar.updateAgentViewStatus(id2, status);
@@ -751228,40 +751639,9 @@ ${entry.fullContent}`
751228
751639
  };
751229
751640
  runner.onEvent((event) => {
751230
751641
  emotionEngine?.appraise(event);
751231
- switch (event.type) {
751232
- case "tool_call": {
751233
- const tn = event.toolName ?? "";
751234
- if (tn === "file_read" || tn === "structured_read")
751235
- setStage("reading", tn);
751236
- else if (tn === "grep_search" || tn === "glob_find" || tn === "web_search" || tn === "web_fetch" || tn === "web_crawl")
751237
- setStage("searching", tn);
751238
- else if (tn === "file_write" || tn === "file_edit" || tn === "file_patch" || tn === "batch_edit" || tn === "structured_file")
751239
- setStage("writing", tn);
751240
- else if (tn === "shell" || tn === "shell_async" || tn === "run_tests" || tn === "repl_exec")
751241
- setStage("building", tn);
751242
- else setStage("tool_call", tn);
751243
- break;
751244
- }
751245
- case "stream_start":
751246
- setStage("planning");
751247
- break;
751248
- case "compaction":
751249
- setStage("compacting");
751250
- break;
751251
- case "debug_adversary":
751252
- setStage("adversary_audit");
751253
- break;
751254
- case "complete":
751255
- setStage("completed");
751256
- break;
751257
- case "error":
751258
- setStage("failed", event.content?.slice(0, 40));
751259
- break;
751260
- case "user_interrupt":
751261
- setStage("blocked");
751262
- break;
751263
- default:
751264
- break;
751642
+ const stageTransition = stageForAgentEvent(event);
751643
+ if (stageTransition) {
751644
+ setStage(stageTransition.stage, stageTransition.detail);
751265
751645
  }
751266
751646
  switch (event.type) {
751267
751647
  case "tool_call":
@@ -752680,7 +753060,7 @@ async function startInteractive(config, repoPath2) {
752680
753060
  });
752681
753061
  statusBar.ensureMonitorTimer();
752682
753062
  },
752683
- onViewWrite: (id2, text2) => statusBar.writeToAgentView(id2, text2),
753063
+ onViewWrite: (id2, text2) => statusBar.recordAgentOutput(id2, text2),
752684
753064
  onViewStatus: (id2, status) => {
752685
753065
  statusBar.updateAgentViewStatus(id2, status);
752686
753066
  const agent = registry2.subAgents.get(id2);
@@ -752721,7 +753101,7 @@ async function startInteractive(config, repoPath2) {
752721
753101
  });
752722
753102
  statusBar.ensureMonitorTimer();
752723
753103
  },
752724
- onViewWrite: (id2, text2) => statusBar.writeToAgentView(id2, text2),
753104
+ onViewWrite: (id2, text2) => statusBar.recordAgentOutput(id2, text2),
752725
753105
  onViewEvent: (id2, event) => writeSubAgentEventForView(statusBar, id2, event, config.verbose),
752726
753106
  onViewStatus: (id2, status) => {
752727
753107
  statusBar.updateAgentViewStatus(id2, status);
@@ -752935,7 +753315,7 @@ ${result.summary}`
752935
753315
  },
752936
753316
  // Stream the full sub-agent's stdout into its toolbar view so the
752937
753317
  // clickable tab shows live output instead of staying empty.
752938
- (text2) => statusBar.writeToAgentView(opts.id, text2),
753318
+ (text2) => statusBar.recordAgentOutput(opts.id, text2),
752939
753319
  (id2, exitCode) => statusBar.updateAgentViewStatus(
752940
753320
  opts.id,
752941
753321
  exitCode === 0 ? "completed" : "failed"
@@ -755524,7 +755904,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
755524
755904
  );
755525
755905
  statusBar.ensureMonitorTimer();
755526
755906
  },
755527
- onWrite: (id2, text2) => statusBar.writeToAgentView(id2, text2),
755907
+ onWrite: (id2, text2) => statusBar.recordAgentOutput(id2, text2),
755528
755908
  onStatus: (id2, status) => {
755529
755909
  statusBar.updateAgentViewStatus(id2, status);
755530
755910
  const agent = registry2.subAgents.get(id2);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.536",
3
+ "version": "1.0.537",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.536",
9
+ "version": "1.0.537",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.536",
3
+ "version": "1.0.537",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -186,7 +186,7 @@ For ongoing interactions (phone calls, live chat, polling, monitoring, streaming
186
186
  ## Document Generation Strategy
187
187
 
188
188
  For long documents (reports, SOWs, proposals, contracts, plans):
189
- NEVER write the entire document in ONE file_write call. DECOMPOSE:
189
+ NEVER rewrite an existing document in ONE file_write call. For an existing target, read first and use file_patch/file_edit; a deliberate whole-file replacement requires overwrite=true and expected_hash from that read. Decompose new documents as follows:
190
190
 
191
191
  1. Read input data (requirements, specs, etc.)
192
192
  2. file_write a SKELETON with only section headers (## headings) and 1-line descriptions
@@ -112,9 +112,10 @@ When a test fails — TWO-STEP debug:
112
112
  2. PATCH: Based on what you SAW (not guessed), edit ONLY the failing line(s). Re-run test.
113
113
  Do NOT rewrite whole functions. Patch the specific fault.
114
114
 
115
- Creating new files — WRITE FIRST, refine later:
116
- - Your FIRST tool call MUST be file_write with a skeleton (class + method signatures + comments).
117
- - Do NOT plan or explain before writing. Write the skeleton immediately.
115
+ Creating genuinely new files — WRITE FIRST, refine later:
116
+ - Only when the target path is verified not to exist (or is an empty/placeholder file), your FIRST mutation may be file_write with a skeleton (class + method signatures + comments).
117
+ - Never use that rule for an existing file. For an existing target, file_read first, then use file_edit/file_patch/batch_edit; a deliberate full replacement requires overwrite=true and expected_hash from that fresh read.
118
+ - Do NOT plan or explain before writing a verified-new file. Write the skeleton immediately.
118
119
  - After writing: fill in each method, test after each one.
119
120
  - A bad first draft you can fix is better than no draft at all.
120
121
 
@@ -138,8 +139,8 @@ Build is FEEDBACK, not a task. A build/test command (make, cargo, npm test, tsc,
138
139
  Repeat policy: Do not repeat discovery calls merely to re-orient. Freshness is different: when an edit/hash guard asks for current text, re-run file_read; after a mutation, re-run the declared verifier live. Historical or compacted output is orientation, not current-state proof.
139
140
 
140
141
  Long document generation (reports, SOWs, proposals, contracts):
141
- NEVER write the entire document in one file_write. DECOMPOSE:
142
- 1. file_write a skeleton with ONLY section headers (##) and 1-line descriptions
142
+ NEVER write an existing document in one file_write. DECOMPOSE:
143
+ 1. For a new document, file_write a skeleton with ONLY section headers (##) and 1-line descriptions; for an existing document, file_read first and use file_patch/file_edit.
143
144
  2. For EACH section: file_edit to add 100-250 words of content
144
145
  3. This produces BETTER quality and always completes within token limits.
145
146