omnius 1.0.456 → 1.0.458

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
@@ -14315,7 +14315,7 @@ var init_file_edit = __esm({
14315
14315
  path: { type: "string", description: "Absolute or relative file path" },
14316
14316
  old_string: {
14317
14317
  type: "string",
14318
- description: "The exact string to search for and replace. Must be unique in the file (or use replace_all). Use old_string_base64 when JSON escaping is fragile."
14318
+ description: "The exact current string to search for and replace, copied from the most recent file_read for this file. Must be unique in the file (or use replace_all). Use old_string_base64 when JSON escaping is fragile."
14319
14319
  },
14320
14320
  old_string_base64: {
14321
14321
  type: "string",
@@ -14340,14 +14340,14 @@ var init_file_edit = __esm({
14340
14340
  },
14341
14341
  expected_hash: {
14342
14342
  type: "string",
14343
- description: "Optional SHA-256 from the most recent file_read. If provided, edit is refused when the file changed."
14343
+ description: "SHA-256 from the most recent file_read for this file. The runner refuses unverified exact replacements that omit or mismatch this hash."
14344
14344
  },
14345
14345
  dry_run: {
14346
14346
  type: "boolean",
14347
14347
  description: "Validate the edit and preview the diff without modifying disk."
14348
14348
  }
14349
14349
  },
14350
- required: ["path"],
14350
+ required: ["path", "expected_hash"],
14351
14351
  allOf: [
14352
14352
  { anyOf: [{ required: ["old_string"] }, { required: ["old_string_base64"] }] },
14353
14353
  { anyOf: [{ required: ["new_string"] }, { required: ["new_string_base64"] }] }
@@ -28570,7 +28570,7 @@ var init_batch_edit = __esm({
28570
28570
  },
28571
28571
  old_string: {
28572
28572
  type: "string",
28573
- description: "Exact string to find and replace (must be unique unless replace_all). Use old_string_base64 when JSON escaping is fragile."
28573
+ description: "Exact current string to find and replace, copied from the most recent file_read for this file. Must be unique unless replace_all. Use old_string_base64 when JSON escaping is fragile."
28574
28574
  },
28575
28575
  old_string_base64: {
28576
28576
  type: "string",
@@ -28595,10 +28595,10 @@ var init_batch_edit = __esm({
28595
28595
  },
28596
28596
  expected_hash: {
28597
28597
  type: "string",
28598
- description: "SHA-256 from the most recent file_read for this file. All edits for a file must use the same hash if provided."
28598
+ description: "SHA-256 from the most recent file_read for this file. All edits for a file must use the same verified hash."
28599
28599
  }
28600
28600
  },
28601
- required: ["path"],
28601
+ required: ["path", "expected_hash"],
28602
28602
  allOf: [
28603
28603
  { anyOf: [{ required: ["old_string"] }, { required: ["old_string_base64"] }] },
28604
28604
  { anyOf: [{ required: ["new_string"] }, { required: ["new_string_base64"] }] }
@@ -566824,6 +566824,7 @@ function shouldIgnore(relPath, base3, patterns) {
566824
566824
  function scanWorkspace(opts) {
566825
566825
  const root = opts.root;
566826
566826
  const maxFiles = Math.max(1, Math.min(2e3, opts.maxFiles ?? 200));
566827
+ const maxDirs = Math.max(1, Math.min(5e3, opts.maxDirs ?? 1e3));
566827
566828
  const noGitignore = opts.noGitignore === true;
566828
566829
  const noDefaults = opts.noDefaults === true;
566829
566830
  const patterns = [];
@@ -566834,6 +566835,7 @@ function scanWorkspace(opts) {
566834
566835
  if (opts.extraIgnore)
566835
566836
  patterns.push(...opts.extraIgnore);
566836
566837
  const files = [];
566838
+ const dirs = [];
566837
566839
  let totalBytes = 0;
566838
566840
  let truncated = false;
566839
566841
  let dirsVisited = 0;
@@ -566849,6 +566851,17 @@ function scanWorkspace(opts) {
566849
566851
  continue;
566850
566852
  visited.add(dir);
566851
566853
  dirsVisited++;
566854
+ if (dirs.length < maxDirs) {
566855
+ try {
566856
+ const st = statSync32(dir);
566857
+ dirs.push({
566858
+ abs: dir,
566859
+ rel: relative8(root, dir) || ".",
566860
+ mtimeMs: st.mtimeMs
566861
+ });
566862
+ } catch {
566863
+ }
566864
+ }
566852
566865
  let entries = [];
566853
566866
  try {
566854
566867
  entries = readdirSync30(dir);
@@ -566883,7 +566896,87 @@ function scanWorkspace(opts) {
566883
566896
  }
566884
566897
  }
566885
566898
  files.sort((a2, b) => a2.rel.localeCompare(b.rel));
566886
- return { files, totalBytes, truncated, dirsVisited };
566899
+ dirs.sort((a2, b) => a2.rel.localeCompare(b.rel));
566900
+ return { files, dirs, totalBytes, truncated, dirsVisited };
566901
+ }
566902
+ function humanBytes(n2) {
566903
+ if (n2 < 1024)
566904
+ return `${n2}B`;
566905
+ if (n2 < 1024 * 1024)
566906
+ return `${(n2 / 1024).toFixed(1)}KB`;
566907
+ return `${(n2 / 1024 / 1024).toFixed(1)}MB`;
566908
+ }
566909
+ function ensureTreeNode(root, rel) {
566910
+ if (!rel || rel === ".")
566911
+ return root;
566912
+ let node = root;
566913
+ for (const part of rel.split("/").filter(Boolean)) {
566914
+ let child = node.children.get(part);
566915
+ if (!child) {
566916
+ child = { name: part, children: /* @__PURE__ */ new Map(), directory: true };
566917
+ node.children.set(part, child);
566918
+ }
566919
+ node = child;
566920
+ }
566921
+ return node;
566922
+ }
566923
+ function renderWorkspaceTree(scan, opts = {}) {
566924
+ const maxLines = Math.max(10, Math.min(2e3, opts.maxLines ?? 120));
566925
+ const includeFileSizes = opts.includeFileSizes !== false;
566926
+ const root = {
566927
+ name: ".",
566928
+ children: /* @__PURE__ */ new Map(),
566929
+ directory: true
566930
+ };
566931
+ for (const dir of scan.dirs)
566932
+ ensureTreeNode(root, dir.rel).directory = true;
566933
+ for (const file of scan.files) {
566934
+ const parts = file.rel.split("/").filter(Boolean);
566935
+ const name10 = parts.pop();
566936
+ if (!name10)
566937
+ continue;
566938
+ const parent = ensureTreeNode(root, parts.join("/"));
566939
+ parent.children.set(name10, {
566940
+ name: name10,
566941
+ children: /* @__PURE__ */ new Map(),
566942
+ file,
566943
+ directory: false
566944
+ });
566945
+ }
566946
+ const lines = ["./"];
566947
+ let truncated = false;
566948
+ const walk2 = (node, depth) => {
566949
+ const children2 = [...node.children.values()].sort((a2, b) => {
566950
+ if (a2.directory !== b.directory)
566951
+ return a2.directory ? -1 : 1;
566952
+ return a2.name.localeCompare(b.name);
566953
+ });
566954
+ for (const child of children2) {
566955
+ if (lines.length >= maxLines) {
566956
+ truncated = true;
566957
+ return;
566958
+ }
566959
+ const indent2 = " ".repeat(depth);
566960
+ if (child.directory) {
566961
+ lines.push(`${indent2}${child.name}/`);
566962
+ walk2(child, depth + 1);
566963
+ if (truncated)
566964
+ return;
566965
+ } else {
566966
+ const size = includeFileSizes && child.file ? ` (${humanBytes(child.file.bytes)})` : "";
566967
+ lines.push(`${indent2}${child.name}${size}`);
566968
+ }
566969
+ }
566970
+ };
566971
+ walk2(root, 1);
566972
+ if (truncated || scan.truncated) {
566973
+ if (lines.length >= maxLines)
566974
+ lines[lines.length - 1] = "... [workspace tree truncated]";
566975
+ else
566976
+ lines.push("... [workspace tree truncated]");
566977
+ truncated = true;
566978
+ }
566979
+ return { text: lines.join("\n"), lineCount: lines.length, truncated };
566887
566980
  }
566888
566981
  var UNIVERSAL_DEFAULTS;
566889
566982
  var init_world_state_disk_scan = __esm({
@@ -567578,9 +567671,9 @@ function regenerate(opts) {
567578
567671
  lines.push(` No files found in workspace (excluding ignored paths).`);
567579
567672
  } else {
567580
567673
  const totalBytes = files.reduce((sum, f2) => sum + f2.bytes, 0);
567581
- lines.push(` ${files.length} file(s)${truncated ? " (truncated — more exist)" : ""}, total ${humanBytes(totalBytes)}.`);
567674
+ lines.push(` ${files.length} file(s)${truncated ? " (truncated — more exist)" : ""}, total ${humanBytes2(totalBytes)}.`);
567582
567675
  if (lastWriteFile) {
567583
- lines.push(` Most recently modified: ${lastWriteFile.rel} (${humanBytes(lastWriteFile.bytes)}, ${ago(nowMs - lastWriteFile.mtimeMs)}).`);
567676
+ lines.push(` Most recently modified: ${lastWriteFile.rel} (${humanBytes2(lastWriteFile.bytes)}, ${ago(nowMs - lastWriteFile.mtimeMs)}).`);
567584
567677
  }
567585
567678
  const topDirs = /* @__PURE__ */ new Map();
567586
567679
  for (const f2 of files) {
@@ -567676,7 +567769,7 @@ function reconciledMark(t2) {
567676
567769
  return "BLOCK";
567677
567770
  }
567678
567771
  }
567679
- function humanBytes(n2) {
567772
+ function humanBytes2(n2) {
567680
567773
  if (n2 < 1024)
567681
567774
  return `${n2}B`;
567682
567775
  if (n2 < 1024 * 1024)
@@ -573437,6 +573530,7 @@ var init_evidenceLedger = __esm({
573437
573530
  this.entries.set(path12, {
573438
573531
  ...existing,
573439
573532
  content: keepNew ? built.text : existing.content,
573533
+ contentHash: input.contentHash ?? existing.contentHash,
573440
573534
  fidelity: keepNew ? built.fidelity : existing.fidelity,
573441
573535
  ranges: mergeRanges([...existing.ranges, range]),
573442
573536
  lastReadTurn: turn,
@@ -573447,6 +573541,7 @@ var init_evidenceLedger = __esm({
573447
573541
  }
573448
573542
  this.entries.set(path12, {
573449
573543
  path: path12,
573544
+ contentHash: input.contentHash,
573450
573545
  readVersion: fileVersion,
573451
573546
  mtimeMs: input.mtimeMs ?? 0,
573452
573547
  lastReadTurn: turn,
@@ -573475,7 +573570,7 @@ var init_evidenceLedger = __esm({
573475
573570
  * like a manual chmod, device reconnect, or human edit), mark as stale.
573476
573571
  * Returns true if evidence was valid, false if became stale or stat failed.
573477
573572
  */
573478
- validateFreshness(path12) {
573573
+ validateFreshness(path12, statPath = path12) {
573479
573574
  const e2 = this.entries.get(path12);
573480
573575
  if (!e2)
573481
573576
  return false;
@@ -573484,7 +573579,7 @@ var init_evidenceLedger = __esm({
573484
573579
  if (e2.mtimeMs <= 0)
573485
573580
  return true;
573486
573581
  try {
573487
- const st = statSync36(path12, { throwIfNoEntry: false });
573582
+ const st = statSync36(statPath, { throwIfNoEntry: false });
573488
573583
  if (!st)
573489
573584
  return true;
573490
573585
  if (st.mtimeMs > e2.mtimeMs) {
@@ -573499,6 +573594,11 @@ var init_evidenceLedger = __esm({
573499
573594
  get(path12) {
573500
573595
  return this.entries.get(path12);
573501
573596
  }
573597
+ markStale(path12) {
573598
+ const e2 = this.entries.get(path12);
573599
+ if (e2)
573600
+ e2.stale = true;
573601
+ }
573502
573602
  size() {
573503
573603
  return this.entries.size;
573504
573604
  }
@@ -573521,12 +573621,12 @@ var init_evidenceLedger = __esm({
573521
573621
  ];
573522
573622
  let used = lines.join("\n").length;
573523
573623
  for (const e2 of sorted) {
573524
- const header = e2.stale ? `▸ ${e2.path} @v${e2.readVersion} [STALE — changed since read; re-read needed] (${rangeLabel(e2.ranges)})` : `▸ ${e2.path} @v${e2.readVersion} [FRESH] (${rangeLabel(e2.ranges)}):`;
573624
+ const header = e2.stale ? `▸ ${e2.path} @v${e2.readVersion} [STALE — changed since read; re-read needed] (${rangeLabel(e2.ranges)})` : `▸ ${e2.path} @v${e2.readVersion} [FRESH${e2.contentHash ? ` sha256=${e2.contentHash}` : ""}] (${rangeLabel(e2.ranges)}):`;
573525
573625
  const body = e2.stale ? "" : "\n" + indent(e2.content);
573526
573626
  const chunk = `${header}${body}
573527
573627
  `;
573528
573628
  if (used + chunk.length > maxChars) {
573529
- const stub = `▸ ${e2.path} @v${e2.readVersion} [${e2.stale ? "STALE" : "FRESH"}] (${rangeLabel(e2.ranges)})
573629
+ const stub = `▸ ${e2.path} @v${e2.readVersion} [${e2.stale ? "STALE" : `FRESH${e2.contentHash ? ` sha256=${e2.contentHash}` : ""}`}] (${rangeLabel(e2.ranges)})
573530
573630
  `;
573531
573631
  if (used + stub.length <= maxChars) {
573532
573632
  lines.push(stub);
@@ -574603,9 +574703,22 @@ ${input.error}`;
574603
574703
  }
574604
574704
  if (input.realFileMutation)
574605
574705
  diagnoses.add("project-file-mutation");
574606
- const focusDecisionJson = previewJson(input.focusDecision ?? {});
574607
- if (focusDecisionJson.includes("block_tool_call")) {
574608
- diagnoses.add("focus-supervisor-block");
574706
+ const focusDecision = input.focusDecision && typeof input.focusDecision === "object" ? input.focusDecision : null;
574707
+ const focusDecisionKind = typeof focusDecision?.["kind"] === "string" ? focusDecision["kind"] : null;
574708
+ if (focusDecisionKind === "block_tool_call") {
574709
+ if (focusDecision?.["success"] === true || input.success) {
574710
+ diagnoses.add("focus-supervisor-cached-evidence");
574711
+ } else {
574712
+ diagnoses.add("focus-supervisor-block");
574713
+ }
574714
+ } else {
574715
+ const focusDecisionJson = previewJson(input.focusDecision ?? {});
574716
+ if (focusDecisionJson.includes("block_tool_call")) {
574717
+ if (input.success)
574718
+ diagnoses.add("focus-supervisor-cached-evidence");
574719
+ else
574720
+ diagnoses.add("focus-supervisor-block");
574721
+ }
574609
574722
  }
574610
574723
  const focusJson = previewJson(input.focusSupervisor ?? {});
574611
574724
  if (focusJson.includes("block_tool_call") || focusJson.includes("requiredNextAction")) {
@@ -574736,7 +574849,8 @@ function writeContract(contractPath) {
574736
574849
  "",
574737
574850
  "- `shell-brace-whitespace`: a shell command used brace expansion containing whitespace, which can split an intended path set into unrelated shell words.",
574738
574851
  "- `successful-command-needs-post-state-audit`: the command exited zero but its shape is risky enough that filesystem state should be inspected.",
574739
- "- `focus-supervisor-block`: the tool result or supervisor state shows a blocked next action.",
574852
+ "- `focus-supervisor-block`: the tool result or supervisor state shows an unsuccessful blocked next action.",
574853
+ "- `focus-supervisor-cached-evidence`: the focus supervisor short-circuited a call successfully by reusing current cached evidence.",
574740
574854
  "- `background-task-id-missing`: task status lookup could not find the spawned task id.",
574741
574855
  "- `raw-discovery-dominates-context`: context-window metrics show low signal/noise around raw discovery output.",
574742
574856
  ""
@@ -575305,6 +575419,10 @@ var init_focusSupervisor = __esm({
575305
575419
  this.clearSatisfiedDirective("requested edit already applied", input.turn);
575306
575420
  return;
575307
575421
  }
575422
+ if (input.success && input.noop && input.isReadLike && (this.directive?.requiredNextAction === "use_cached_evidence" || this.directive?.requiredNextAction === "read_authoritative_target")) {
575423
+ this.clearSatisfiedDirective(input.runtimeAuthored ? "cached authoritative evidence reused" : "authoritative evidence already available", input.turn);
575424
+ return;
575425
+ }
575308
575426
  if (input.runtimeAuthored) {
575309
575427
  this.lastDecision = "runtime_authored_not_satisfied";
575310
575428
  this.lastReason = "runtime-authored control result did not execute the requested tool";
@@ -577519,7 +577637,7 @@ RECOVERY: cd to the directory containing '${file}', run a plain install with no
577519
577637
  import { existsSync as _fsExistsSync, readFileSync as _fsReadFileSync, writeFileSync as _fsWriteFileSync, appendFileSync as _fsAppendFileSync, unlinkSync as _fsUnlinkSync, mkdirSync as _fsMkdirSync, statSync as _fsStatSync, readdirSync as _fsReaddirSync } from "node:fs";
577520
577638
  import { execFile as _execFile, spawn as _spawn } from "node:child_process";
577521
577639
  import { createHash as _createHash } from "node:crypto";
577522
- import { join as _pathJoin, resolve as _pathResolve } from "node:path";
577640
+ import { join as _pathJoin, relative as _pathRelative, resolve as _pathResolve } from "node:path";
577523
577641
  import { tmpdir as _osTmpdir } from "node:os";
577524
577642
  import { homedir as _osHomedir } from "node:os";
577525
577643
  import { z as z17 } from "zod";
@@ -578182,6 +578300,7 @@ var init_agenticRunner = __esm({
578182
578300
  init_lesson_bank();
578183
578301
  init_intervention_replay();
578184
578302
  init_world_state_regenerator();
578303
+ init_world_state_disk_scan();
578185
578304
  init_backward_pass_runner();
578186
578305
  init_world_state_plan_reconciler();
578187
578306
  init_process_async2();
@@ -581015,7 +581134,7 @@ ${result.error ?? ""}`;
581015
581134
  if (result.success !== true)
581016
581135
  return false;
581017
581136
  if (toolName === "file_read") {
581018
- return targetPaths.length > 0 && targetPaths.some((path12) => this._evidenceLedger.hasFresh(path12));
581137
+ return targetPaths.length > 0 && targetPaths.some((path12) => this._evidenceLedger.hasFresh(this._normalizeEvidencePath(path12)));
581019
581138
  }
581020
581139
  return toolName === "list_directory" || toolName === "find_files" || toolName === "grep_search";
581021
581140
  }
@@ -583124,6 +583243,133 @@ ${latest.output || ""}`.trim();
583124
583243
  return null;
583125
583244
  }
583126
583245
  }
583246
+ /**
583247
+ * Render a fresh, bounded workspace tree from disk for the active context
583248
+ * frame, and persist the fuller capped tree under .omnius/world-state/tree.
583249
+ * This is deliberately independent of tool history: it answers "what exists
583250
+ * where right now?" without forcing the model to list_directory for basic
583251
+ * orientation.
583252
+ */
583253
+ _renderWorkspaceTreeBlock(turn) {
583254
+ const rawEnabled = String(process.env["OMNIUS_CONTEXT_WORKSPACE_TREE"] ?? "1").trim().toLowerCase();
583255
+ if (rawEnabled === "0" || rawEnabled === "false" || rawEnabled === "off") {
583256
+ return null;
583257
+ }
583258
+ const intEnv = (name10, fallback, min, max) => {
583259
+ const parsed = Number.parseInt(process.env[name10] ?? "", 10);
583260
+ return Number.isFinite(parsed) ? Math.max(min, Math.min(max, parsed)) : fallback;
583261
+ };
583262
+ const root = this.authoritativeWorkingDirectory();
583263
+ const maxFiles = intEnv("OMNIUS_CONTEXT_TREE_MAX_FILES", 1e3, 20, 2e3);
583264
+ const maxDirs = intEnv("OMNIUS_CONTEXT_TREE_MAX_DIRS", 1500, 20, 5e3);
583265
+ const contextLines = intEnv("OMNIUS_CONTEXT_TREE_LINES", 90, 20, 400);
583266
+ const artifactLines = intEnv("OMNIUS_CONTEXT_TREE_ARTIFACT_LINES", 1800, 100, 4e3);
583267
+ try {
583268
+ const scan = scanWorkspace({ root, maxFiles, maxDirs });
583269
+ const compactTree = renderWorkspaceTree(scan, { maxLines: contextLines });
583270
+ const fullTree = renderWorkspaceTree(scan, { maxLines: artifactLines });
583271
+ const totalBytes = scan.files.reduce((sum, file) => sum + file.bytes, 0);
583272
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
583273
+ const focusSnapshot = this._focusSupervisor?.snapshot();
583274
+ const recentActions = this._adversaryToolOutcomes.slice(-12).map((item) => ({
583275
+ turn: item.turn,
583276
+ tool: item.tool,
583277
+ success: item.succeeded,
583278
+ path: item.path,
583279
+ stateVersion: item.stateVersion,
583280
+ preview: item.preview.slice(0, 240)
583281
+ }));
583282
+ const recentActionLines = recentActions.slice(-6).map((item) => {
583283
+ const path12 = item.path ? ` path=${item.path}` : "";
583284
+ return `- turn=${item.turn} ${item.tool} ${item.success ? "ok" : "err"}${path12}: ${item.preview.replace(/\s+/g, " ").slice(0, 160)}`;
583285
+ });
583286
+ const treeDir = _pathJoin(this.omniusStateDir(), "world-state", "tree");
583287
+ const turnName = `turn-${String(turn).padStart(4, "0")}`;
583288
+ const latestTextPath = _pathJoin(treeDir, "latest.txt");
583289
+ const latestJsonPath = _pathJoin(treeDir, "latest.json");
583290
+ const turnTextPath = _pathJoin(treeDir, `${turnName}.txt`);
583291
+ const turnJsonPath = _pathJoin(treeDir, `${turnName}.json`);
583292
+ let artifactDisplay = latestTextPath;
583293
+ try {
583294
+ _fsMkdirSync(treeDir, { recursive: true });
583295
+ const header = [
583296
+ `WORKSPACE TREE SNAPSHOT`,
583297
+ `generated_at=${generatedAt}`,
583298
+ `turn=${turn}`,
583299
+ `session=${this._sessionId ?? ""}`,
583300
+ `root=${root}`,
583301
+ `files=${scan.files.length}${scan.truncated ? "+" : ""}`,
583302
+ `dirs=${scan.dirs.length}`,
583303
+ `bytes=${totalBytes}`,
583304
+ focusSnapshot?.directive?.requiredNextAction ? `supervisor_required_next_action=${focusSnapshot.directive.requiredNextAction}` : `supervisor_required_next_action=none`,
583305
+ recentActionLines.length ? `recent_actions=${recentActions.length}` : `recent_actions=0`,
583306
+ recentActionLines.length ? recentActionLines.join("\n") : "",
583307
+ ""
583308
+ ].join("\n");
583309
+ const text2 = `${header}${fullTree.text}
583310
+ `;
583311
+ const payload = {
583312
+ generatedAt,
583313
+ turn,
583314
+ sessionId: this._sessionId,
583315
+ root,
583316
+ supervisor: focusSnapshot ? {
583317
+ state: focusSnapshot.state,
583318
+ lastDecision: focusSnapshot.lastDecision,
583319
+ lastReason: focusSnapshot.lastReason,
583320
+ requiredNextAction: focusSnapshot.directive?.requiredNextAction ?? null,
583321
+ directiveReason: focusSnapshot.directive?.reason ?? null
583322
+ } : null,
583323
+ recentActions,
583324
+ stats: {
583325
+ files: scan.files.length,
583326
+ dirs: scan.dirs.length,
583327
+ totalBytes,
583328
+ truncated: scan.truncated || fullTree.truncated,
583329
+ dirsVisited: scan.dirsVisited
583330
+ },
583331
+ files: scan.files.map((file) => ({
583332
+ path: file.rel,
583333
+ bytes: file.bytes,
583334
+ mtimeMs: file.mtimeMs
583335
+ })),
583336
+ dirs: scan.dirs.map((dir) => ({
583337
+ path: dir.rel,
583338
+ mtimeMs: dir.mtimeMs
583339
+ }))
583340
+ };
583341
+ _fsWriteFileSync(latestTextPath, text2, "utf8");
583342
+ _fsWriteFileSync(turnTextPath, text2, "utf8");
583343
+ _fsWriteFileSync(latestJsonPath, JSON.stringify(payload, null, 2), "utf8");
583344
+ _fsWriteFileSync(turnJsonPath, JSON.stringify(payload, null, 2), "utf8");
583345
+ const rel = _pathRelative(root, latestTextPath).replace(/\\/g, "/");
583346
+ if (rel && !rel.startsWith(".."))
583347
+ artifactDisplay = rel;
583348
+ } catch {
583349
+ artifactDisplay = "(workspace tree artifact write failed; inline digest still current)";
583350
+ }
583351
+ return [
583352
+ "[WORKSPACE TREE — fresh per-turn disk crawl]",
583353
+ `generated_at=${generatedAt}`,
583354
+ `root=${root}`,
583355
+ `files=${scan.files.length}${scan.truncated ? "+" : ""} dirs=${scan.dirs.length} total_bytes=${totalBytes}`,
583356
+ `artifact=${artifactDisplay}`,
583357
+ focusSnapshot?.directive?.requiredNextAction ? `supervisor_required_next_action=${focusSnapshot.directive.requiredNextAction}` : `supervisor_required_next_action=none`,
583358
+ recentActionLines.length ? `recent_actions:
583359
+ ${recentActionLines.join("\n")}` : "recent_actions=none",
583360
+ "Use this tree for path orientation. It includes empty directories; it does not include file contents.",
583361
+ compactTree.truncated || scan.truncated ? "Inline tree is truncated; read the artifact path above for the fuller capped tree before broad rediscovery." : "Inline tree is complete within the configured scan cap.",
583362
+ compactTree.text
583363
+ ].join("\n");
583364
+ } catch (error) {
583365
+ return [
583366
+ "[WORKSPACE TREE — fresh per-turn disk crawl]",
583367
+ `root=${root}`,
583368
+ `scan_failed=${error instanceof Error ? error.message : String(error)}`,
583369
+ "Do not assume the tree is current; use list_directory/find_files if path orientation is required."
583370
+ ].join("\n");
583371
+ }
583372
+ }
583127
583373
  /**
583128
583374
  * REG-2: Render a compact "files known to me" block from `_worldFacts`.
583129
583375
  * Goal: tell the model what it has already touched this run so it stops
@@ -583663,6 +583909,7 @@ ${chunk.content}`, {
583663
583909
  this._renderRuntimeRootBlock(),
583664
583910
  this._taskState.goal ? `Active task: (see user message above — the goal block defers to the user message as the authoritative source)` : null
583665
583911
  ].filter(Boolean).join("\n\n");
583912
+ const workspaceTreeBlock = this._renderWorkspaceTreeBlock(turn);
583666
583913
  const filesystemBlock = this._renderFilesystemStateBlock(turn);
583667
583914
  const artifactContractBlock = this._contractRegistry.format(15);
583668
583915
  const todoBlock = this._renderTodoStateBlock(turn);
@@ -583707,6 +583954,13 @@ ${this._lastPprMemoryLines.slice(0, 5).join("\n")}` : null;
583707
583954
  createdTurn: turn,
583708
583955
  ttlTurns: 1
583709
583956
  }),
583957
+ signalFromBlock("known_files", "turn.workspace-tree", workspaceTreeBlock, {
583958
+ id: "workspace-tree",
583959
+ dedupeKey: "turn.workspace-tree",
583960
+ priority: 84,
583961
+ createdTurn: turn,
583962
+ ttlTurns: 1
583963
+ }),
583710
583964
  signalFromBlock("known_files", "turn.artifact-contracts", artifactContractBlock, {
583711
583965
  id: "artifact-contracts",
583712
583966
  dedupeKey: "turn.artifact-contracts",
@@ -583795,7 +584049,7 @@ ${this._lastPprMemoryLines.slice(0, 5).join("\n")}` : null;
583795
584049
  // Raised from 10k to fit the durable read-evidence content (known_files)
583796
584050
  // that replaces the re-read loop; the per-kind budget below caps it.
583797
584051
  maxChars: 13e3,
583798
- kindCharBudget: { known_files: 6500 },
584052
+ kindCharBudget: { known_files: 8500 },
583799
584053
  includeDiagnostics: process.env["OMNIUS_CONTEXT_FABRIC_DIAGNOSTICS"] === "1"
583800
584054
  });
583801
584055
  this._lastContextFrameDiagnostics = frame.diagnostics;
@@ -583999,10 +584253,12 @@ ${blob}
583999
584253
  if (p2 && content.length >= 100) {
584000
584254
  try {
584001
584255
  this._evidenceLedger.recordRead({
584002
- path: p2,
584256
+ path: this._normalizeEvidencePath(p2),
584003
584257
  content,
584258
+ contentHash: this._extractFileReadSha256(content) ?? void 0,
584004
584259
  range: { start: 0, end: Number.POSITIVE_INFINITY },
584005
584260
  fileVersion: this._worldFacts.files.get(p2)?.writeCount ?? 0,
584261
+ mtimeMs: this._statMtimeMsForToolPath(p2),
584006
584262
  turn: this._taskState?.toolCallCount ?? 0
584007
584263
  });
584008
584264
  } catch {
@@ -587901,6 +588157,18 @@ ${memoryLines.join("\n")}`
587901
588157
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
587902
588158
  });
587903
588159
  }
588160
+ for (const tc of msg.toolCalls) {
588161
+ const patched = this._attachFreshExpectedHashesToEditArgs(tc.name, tc.arguments ?? {});
588162
+ if (patched.patchedCount > 0) {
588163
+ tc.arguments = patched.args;
588164
+ this.emit({
588165
+ type: "status",
588166
+ content: `Edit freshness: attached expected_hash to ${patched.patchedCount} ${tc.name} edit target${patched.patchedCount === 1 ? "" : "s"} from fresh file_read evidence`,
588167
+ turn,
588168
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
588169
+ });
588170
+ }
588171
+ }
587904
588172
  messages2.push({
587905
588173
  role: "assistant",
587906
588174
  content: msg.content || null,
@@ -588640,7 +588908,8 @@ Read the current file if needed, make a different concrete edit, or move to veri
588640
588908
  const readArgs = tc.arguments;
588641
588909
  const readPath2 = String(readArgs?.["path"] ?? readArgs?.["file"] ?? "");
588642
588910
  if (readPath2) {
588643
- const wasFresh = this._evidenceLedger.validateFreshness(readPath2);
588911
+ const evidencePath = this._normalizeEvidencePath(readPath2);
588912
+ const wasFresh = evidencePath && this._evidenceLedger.validateFreshness(evidencePath, this._absoluteToolPath(readPath2));
588644
588913
  if (!wasFresh) {
588645
588914
  recentToolResults.delete(toolFingerprint);
588646
588915
  dedupHitCount.delete(toolFingerprint);
@@ -588770,13 +589039,13 @@ ${cachedResult}`,
588770
589039
  error: this.unknownToolError(tc.name)
588771
589040
  };
588772
589041
  } else {
588773
- let validationError = null;
588774
- if (tool.inputSchema) {
589042
+ let validationError = this._validateFreshExpectedHashesForEdit(resolvedTool?.name ?? tc.name, tc.arguments ?? {});
589043
+ if (!validationError && tool.inputSchema) {
588775
589044
  const parseResult = tool.inputSchema.safeParse(tc.arguments);
588776
589045
  if (!parseResult.success) {
588777
589046
  validationError = parseResult.error.issues.map((iss) => `${iss.path.join(".")}: ${iss.message}`).join("; ");
588778
589047
  }
588779
- } else if (tool.parameters && typeof tool.parameters === "object") {
589048
+ } else if (!validationError && tool.parameters && typeof tool.parameters === "object") {
588780
589049
  const required = tool.parameters.required;
588781
589050
  if (Array.isArray(required)) {
588782
589051
  const missing = required.filter((field) => tc.arguments[field] === void 0 || tc.arguments[field] === null);
@@ -589017,7 +589286,7 @@ Respond with EXACTLY this structure before your next tool call:
589017
589286
  lastWriteTurn: turn,
589018
589287
  writeCount: nextWriteCount
589019
589288
  });
589020
- this._evidenceLedger.markMutated(p2, nextWriteCount, turn);
589289
+ this._evidenceLedger.markMutated(this._normalizeEvidencePath(p2), nextWriteCount, turn);
589021
589290
  }
589022
589291
  if (paths.length > 0) {
589023
589292
  this._writesSinceLastTodoWrite += paths.length;
@@ -589061,11 +589330,12 @@ Respond with EXACTLY this structure before your next tool call:
589061
589330
  const start3 = rOffset === void 0 && rLimit === void 0 ? 0 : Math.max(1, rOffset ?? 1);
589062
589331
  const end = rOffset === void 0 && rLimit === void 0 ? Number.POSITIVE_INFINITY : rLimit !== void 0 ? Math.max(1, rOffset ?? 1) + Math.max(0, rLimit) - 1 : Number.POSITIVE_INFINITY;
589063
589332
  this._evidenceLedger.recordRead({
589064
- path: p2,
589333
+ path: this._normalizeEvidencePath(p2),
589065
589334
  content: result.output,
589335
+ contentHash: typeof result.beforeHash === "string" ? result.beforeHash : void 0,
589066
589336
  range: { start: start3, end },
589067
589337
  fileVersion: this._worldFacts.files.get(p2)?.writeCount ?? 0,
589068
- mtimeMs: Date.now(),
589338
+ mtimeMs: this._statMtimeMsForToolPath(p2),
589069
589339
  turn
589070
589340
  });
589071
589341
  }
@@ -590286,6 +590556,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
590286
590556
  focusSupervisor: focusAfterToolResult,
590287
590557
  focusDecision: !focusDecision || focusDecision.kind === "pass" ? void 0 : {
590288
590558
  kind: focusDecision.kind,
590559
+ success: focusDecision.kind === "block_tool_call" ? focusDecision.success : void 0,
590289
590560
  state: focusDecision.directive.state,
590290
590561
  reason: focusDecision.directive.reason,
590291
590562
  requiredNextAction: focusDecision.directive.requiredNextAction
@@ -592760,13 +593031,14 @@ ${marker}` : marker);
592760
593031
  const displayOutput = this.unwrapToolOutputForDisplay(output);
592761
593032
  if (toolName === "file_read") {
592762
593033
  const path13 = this.extractPrimaryToolPath(args);
592763
- if (!path13 || !this._evidenceLedger.hasFresh(path13))
593034
+ const evidencePath = path13 ? this._normalizeEvidencePath(path13) : "";
593035
+ if (!evidencePath || !this._evidenceLedger.hasFresh(evidencePath))
592764
593036
  return output;
592765
- const entry = this._evidenceLedger.get(path13);
593037
+ const entry = this._evidenceLedger.get(evidencePath);
592766
593038
  return this.wrapToolOutputForModel(toolName, [
592767
593039
  "[DISCOVERY COMPACTED - file_read evidence captured]",
592768
593040
  `path=${path13}`,
592769
- `captured=${this.countTextLines(displayOutput)} lines, ${displayOutput.length} chars${entry ? `, fidelity=${entry.fidelity}` : ""}`,
593041
+ `captured=${this.countTextLines(displayOutput)} lines, ${displayOutput.length} chars${entry ? `, fidelity=${entry.fidelity}${entry.contentHash ? `, sha256=${entry.contentHash}` : ""}` : ""}`,
592770
593042
  'active_context="Evidence already gathered this run"',
592771
593043
  "Use the active evidence frame. Re-read only if that frame marks this file STALE or you need a distinct range."
592772
593044
  ].join("\n"));
@@ -592912,6 +593184,175 @@ ${marker}` : marker);
592912
593184
  return trimmed.replace(/\\/g, "/").replace(/^\.\/+/, "");
592913
593185
  }
592914
593186
  }
593187
+ _absoluteToolPath(path12) {
593188
+ return _pathResolve(this.authoritativeWorkingDirectory(), path12.trim() || ".");
593189
+ }
593190
+ _normalizeEvidencePath(path12) {
593191
+ const trimmed = path12.trim();
593192
+ if (!trimmed)
593193
+ return "";
593194
+ try {
593195
+ const root = this.authoritativeWorkingDirectory();
593196
+ const abs = _pathResolve(root, trimmed);
593197
+ const rel = _pathRelative(root, abs).replace(/\\/g, "/");
593198
+ if (rel && !rel.startsWith("..") && rel !== ".")
593199
+ return rel;
593200
+ if (rel === "")
593201
+ return ".";
593202
+ return abs.replace(/\\/g, "/");
593203
+ } catch {
593204
+ return trimmed.replace(/\\/g, "/").replace(/^\.\/+/, "");
593205
+ }
593206
+ }
593207
+ _statMtimeMsForToolPath(path12) {
593208
+ try {
593209
+ const st = _fsStatSync(this._absoluteToolPath(path12), {
593210
+ throwIfNoEntry: false
593211
+ });
593212
+ return st?.mtimeMs;
593213
+ } catch {
593214
+ return void 0;
593215
+ }
593216
+ }
593217
+ _normalizeExpectedHashValue(value2) {
593218
+ if (typeof value2 !== "string")
593219
+ return null;
593220
+ const raw = value2.trim();
593221
+ if (!raw)
593222
+ return null;
593223
+ const stripped = raw.replace(/^sha256[:=]/i, "").toLowerCase();
593224
+ return /^[a-f0-9]{64}$/.test(stripped) ? stripped : null;
593225
+ }
593226
+ _extractFileReadSha256(text2) {
593227
+ const match = text2.match(/\bsha256=([a-f0-9]{64})\b/i);
593228
+ return match ? match[1].toLowerCase() : null;
593229
+ }
593230
+ _rawExpectedHashValue(rec) {
593231
+ return rec["expected_hash"] ?? rec["expectedHash"] ?? rec["before_hash"] ?? rec["beforeHash"];
593232
+ }
593233
+ _freshReadHashForEditPath(path12) {
593234
+ const key = this._normalizeEvidencePath(path12);
593235
+ if (!key)
593236
+ return null;
593237
+ const entry = this._evidenceLedger.get(key);
593238
+ if (!entry || entry.stale || !entry.contentHash)
593239
+ return null;
593240
+ const statPath = this._absoluteToolPath(path12);
593241
+ if (!this._evidenceLedger.validateFreshness(key, statPath))
593242
+ return null;
593243
+ const fresh = this._evidenceLedger.get(key);
593244
+ if (!fresh || fresh.stale || !fresh.contentHash)
593245
+ return null;
593246
+ return {
593247
+ key,
593248
+ hash: fresh.contentHash,
593249
+ lastReadTurn: fresh.lastReadTurn
593250
+ };
593251
+ }
593252
+ _attachFreshExpectedHashesToEditArgs(toolName, args) {
593253
+ if (process.env["OMNIUS_ALLOW_UNVERIFIED_OLD_STRING_EDIT"] === "1") {
593254
+ return { args, patchedCount: 0 };
593255
+ }
593256
+ if (toolName === "file_edit") {
593257
+ if (this._normalizeExpectedHashValue(this._rawExpectedHashValue(args))) {
593258
+ return { args, patchedCount: 0 };
593259
+ }
593260
+ const path12 = this.extractPrimaryToolPath(args);
593261
+ const fresh = path12 ? this._freshReadHashForEditPath(path12) : null;
593262
+ if (!fresh)
593263
+ return { args, patchedCount: 0 };
593264
+ return { args: { ...args, expected_hash: fresh.hash }, patchedCount: 1 };
593265
+ }
593266
+ if (toolName !== "batch_edit" || !Array.isArray(args["edits"])) {
593267
+ return { args, patchedCount: 0 };
593268
+ }
593269
+ let patchedCount = 0;
593270
+ const edits = args["edits"].map((edit) => {
593271
+ if (!edit || typeof edit !== "object" || Array.isArray(edit))
593272
+ return edit;
593273
+ const rec = edit;
593274
+ if (this._normalizeExpectedHashValue(this._rawExpectedHashValue(rec))) {
593275
+ return rec;
593276
+ }
593277
+ const path12 = typeof rec["path"] === "string" ? rec["path"] : typeof rec["file"] === "string" ? rec["file"] : "";
593278
+ const fresh = path12 ? this._freshReadHashForEditPath(path12) : null;
593279
+ if (!fresh)
593280
+ return rec;
593281
+ patchedCount++;
593282
+ return { ...rec, expected_hash: fresh.hash };
593283
+ });
593284
+ return patchedCount > 0 ? { args: { ...args, edits }, patchedCount } : { args, patchedCount: 0 };
593285
+ }
593286
+ _validateFreshExpectedHashesForEdit(toolName, args) {
593287
+ if (process.env["OMNIUS_ALLOW_UNVERIFIED_OLD_STRING_EDIT"] === "1") {
593288
+ return null;
593289
+ }
593290
+ const targets = [];
593291
+ if (toolName === "file_edit") {
593292
+ const path12 = this.extractPrimaryToolPath(args);
593293
+ if (path12) {
593294
+ targets.push({
593295
+ label: path12,
593296
+ path: path12,
593297
+ expectedHash: this._normalizeExpectedHashValue(this._rawExpectedHashValue(args)),
593298
+ rawExpectedHash: this._rawExpectedHashValue(args)
593299
+ });
593300
+ }
593301
+ } else if (toolName === "batch_edit" && Array.isArray(args["edits"])) {
593302
+ args["edits"].forEach((edit, index) => {
593303
+ if (!edit || typeof edit !== "object" || Array.isArray(edit))
593304
+ return;
593305
+ const rec = edit;
593306
+ const path12 = typeof rec["path"] === "string" ? rec["path"] : typeof rec["file"] === "string" ? rec["file"] : "";
593307
+ if (!path12)
593308
+ return;
593309
+ targets.push({
593310
+ label: `edit #${index + 1} ${path12}`,
593311
+ path: path12,
593312
+ expectedHash: this._normalizeExpectedHashValue(this._rawExpectedHashValue(rec)),
593313
+ rawExpectedHash: this._rawExpectedHashValue(rec)
593314
+ });
593315
+ });
593316
+ } else {
593317
+ return null;
593318
+ }
593319
+ if (targets.length === 0)
593320
+ return null;
593321
+ const problems = [];
593322
+ for (const target of targets) {
593323
+ const fresh = this._freshReadHashForEditPath(target.path);
593324
+ if (target.rawExpectedHash !== void 0 && !target.expectedHash) {
593325
+ problems.push(`${target.label}: expected_hash is present but is not a valid 64-character SHA-256`);
593326
+ continue;
593327
+ }
593328
+ if (!fresh && target.expectedHash) {
593329
+ continue;
593330
+ }
593331
+ if (!fresh) {
593332
+ problems.push(`${target.label}: missing expected_hash and no fresh file_read hash is available for this file in the current run`);
593333
+ continue;
593334
+ }
593335
+ if (!target.expectedHash) {
593336
+ problems.push(`${target.label}: missing expected_hash; use sha256=${fresh.hash} from the fresh file_read evidence`);
593337
+ continue;
593338
+ }
593339
+ if (target.expectedHash !== fresh.hash) {
593340
+ problems.push(`${target.label}: expected_hash=${target.expectedHash} does not match fresh file_read sha256=${fresh.hash}`);
593341
+ }
593342
+ }
593343
+ if (problems.length === 0)
593344
+ return null;
593345
+ return [
593346
+ "[EDIT FRESHNESS CONTRACT]",
593347
+ "Exact replacement edits must be built from verified current file content, not remembered text.",
593348
+ "Before file_edit or batch_edit can run, each target must carry expected_hash from file_read. If the runner has fresh read evidence, it will attach that hash automatically; otherwise the edit tool verifies the provided hash against disk before mutating.",
593349
+ "For multiple edits to the same file, use one batch_edit against the same expected_hash so all replacements validate atomically before the file changes.",
593350
+ "",
593351
+ ...problems.map((problem) => `- ${problem}`),
593352
+ "",
593353
+ "Next action: file_read the target file or range, then retry the edit with old_string copied exactly from that read and expected_hash set to the read's sha256."
593354
+ ].join("\n");
593355
+ }
592915
593356
  staleEditFamilyKey(toolName, pathKey, errorKind, targetHash, latestFileHash) {
592916
593357
  return [
592917
593358
  toolName,
@@ -631048,6 +631489,7 @@ var init_status_bar = __esm({
631048
631489
  this._contentScrollOffset = 0;
631049
631490
  if (this.active) {
631050
631491
  this.repaintContent();
631492
+ this.refreshDynamicBlocks();
631051
631493
  this._contentCursorNeedsReplay = true;
631052
631494
  if (this.writeDepth > 0) this._contentCursorReplayActive = true;
631053
631495
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.456",
3
+ "version": "1.0.458",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.456",
9
+ "version": "1.0.458",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
@@ -4138,9 +4138,9 @@
4138
4138
  }
4139
4139
  },
4140
4140
  "node_modules/iconv-lite": {
4141
- "version": "0.7.2",
4142
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
4143
- "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
4141
+ "version": "0.7.3",
4142
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
4143
+ "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
4144
4144
  "license": "MIT",
4145
4145
  "dependencies": {
4146
4146
  "safer-buffer": ">= 2.1.2 < 3.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.456",
3
+ "version": "1.0.458",
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",