omnius 1.0.174 → 1.0.175

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
@@ -5738,6 +5738,108 @@ var init_change_log = __esm({
5738
5738
  }
5739
5739
  });
5740
5740
 
5741
+ // packages/execution/dist/tools/text-encoding.js
5742
+ import { TextDecoder as TextDecoder2 } from "node:util";
5743
+ function decodeTextArgument(args, options2) {
5744
+ const direct = firstString(args, options2.textKeys);
5745
+ const b64 = firstString(args, options2.base64Keys);
5746
+ const encodingRaw = firstString(args, options2.encodingKeys ?? ["content_encoding", "encoding"]);
5747
+ const encoding = encodingRaw?.value.trim().toLowerCase().replace(/_/g, "-");
5748
+ if (b64) {
5749
+ return decodeBase64Text(b64.value, b64.key);
5750
+ }
5751
+ if (encoding === "base64") {
5752
+ if (!direct) {
5753
+ return {
5754
+ ok: false,
5755
+ reason: "missing",
5756
+ error: `${options2.fieldLabel} is required when content_encoding="base64". ` + options2.example
5757
+ };
5758
+ }
5759
+ return decodeBase64Text(direct.value, direct.key);
5760
+ }
5761
+ if (encoding && encoding !== "utf-8" && encoding !== "utf8" && encoding !== "text") {
5762
+ return {
5763
+ ok: false,
5764
+ reason: "invalid",
5765
+ error: `Unsupported ${options2.fieldLabel} encoding "${encodingRaw?.value}". Use plain UTF-8 text or base64-encoded UTF-8 text.`
5766
+ };
5767
+ }
5768
+ if (direct) {
5769
+ return { ok: true, value: direct.value, source: direct.key, encoded: false };
5770
+ }
5771
+ return {
5772
+ ok: false,
5773
+ reason: "missing",
5774
+ error: `${options2.fieldLabel} is required. ${options2.example}`
5775
+ };
5776
+ }
5777
+ function firstString(args, keys) {
5778
+ for (const key of keys) {
5779
+ if (typeof args[key] === "string") {
5780
+ return { key, value: args[key] };
5781
+ }
5782
+ }
5783
+ return null;
5784
+ }
5785
+ function decodeBase64Text(raw, source) {
5786
+ const compact3 = raw.replace(/\s+/g, "").replace(/-/g, "+").replace(/_/g, "/");
5787
+ if (compact3.length > 0 && /[^A-Za-z0-9+/=]/.test(compact3)) {
5788
+ return {
5789
+ ok: false,
5790
+ reason: "invalid",
5791
+ error: `${source} is not valid base64: contains characters outside the base64 alphabet.`
5792
+ };
5793
+ }
5794
+ if (compact3.length % 4 === 1) {
5795
+ return {
5796
+ ok: false,
5797
+ reason: "invalid",
5798
+ error: `${source} is not valid base64: invalid length.`
5799
+ };
5800
+ }
5801
+ const padded = compact3.padEnd(Math.ceil(compact3.length / 4) * 4, "=");
5802
+ let bytes;
5803
+ try {
5804
+ bytes = Buffer.from(padded, "base64");
5805
+ } catch (err) {
5806
+ return {
5807
+ ok: false,
5808
+ reason: "invalid",
5809
+ error: `${source} could not be decoded as base64: ${err instanceof Error ? err.message : String(err)}`
5810
+ };
5811
+ }
5812
+ const canonical = bytes.toString("base64").replace(/=+$/g, "");
5813
+ if (compact3.replace(/=+$/g, "") !== canonical) {
5814
+ return {
5815
+ ok: false,
5816
+ reason: "invalid",
5817
+ error: `${source} is not canonical base64 for the bytes it decodes to.`
5818
+ };
5819
+ }
5820
+ try {
5821
+ return {
5822
+ ok: true,
5823
+ value: UTF8_DECODER.decode(bytes),
5824
+ source,
5825
+ encoded: true
5826
+ };
5827
+ } catch {
5828
+ return {
5829
+ ok: false,
5830
+ reason: "invalid",
5831
+ error: `${source} decoded successfully but is not valid UTF-8 text. file_write/file_edit/file_patch are text tools.`
5832
+ };
5833
+ }
5834
+ }
5835
+ var UTF8_DECODER;
5836
+ var init_text_encoding = __esm({
5837
+ "packages/execution/dist/tools/text-encoding.js"() {
5838
+ "use strict";
5839
+ UTF8_DECODER = new TextDecoder2("utf-8", { fatal: true });
5840
+ }
5841
+ });
5842
+
5741
5843
  // packages/execution/dist/tools/file-write.js
5742
5844
  import { writeFile, mkdir, readFile as readFile2 } from "node:fs/promises";
5743
5845
  import { existsSync as existsSync9 } from "node:fs";
@@ -5756,14 +5858,27 @@ var init_file_write = __esm({
5756
5858
  "use strict";
5757
5859
  init_change_log();
5758
5860
  init_edit_metadata();
5861
+ init_text_encoding();
5759
5862
  FileWriteTool = class {
5760
5863
  name = "file_write";
5761
- description = "Write content to a file, creating directories as needed";
5864
+ description = "Write UTF-8 text to a file, creating directories as needed. For content with heavy quoting, raw JSON, control characters, or large full-file payloads, pass content_base64 instead of shell heredocs/cat/tee.";
5762
5865
  parameters = {
5763
5866
  type: "object",
5764
5867
  properties: {
5765
5868
  path: { type: "string", description: "Absolute or relative file path" },
5766
- content: { type: "string", description: "File content to write" },
5869
+ content: {
5870
+ type: "string",
5871
+ description: "UTF-8 text content to write. Use content_base64 when JSON escaping is fragile."
5872
+ },
5873
+ content_base64: {
5874
+ type: "string",
5875
+ description: "Base64-encoded UTF-8 text content. Preferred for full-file writes with quotes, backticks, raw JSON, or control characters."
5876
+ },
5877
+ content_encoding: {
5878
+ type: "string",
5879
+ enum: ["utf-8", "base64"],
5880
+ description: "Set to base64 when passing base64 text in the content field. Not needed when using content_base64."
5881
+ },
5767
5882
  overwrite: {
5768
5883
  type: "boolean",
5769
5884
  description: "Required when overwriting an existing file with different content."
@@ -5773,7 +5888,11 @@ var init_file_write = __esm({
5773
5888
  description: "SHA-256 hash from the most recent file_read. Required with overwrite=true for existing files."
5774
5889
  }
5775
5890
  },
5776
- required: ["path", "content"]
5891
+ required: ["path"],
5892
+ anyOf: [
5893
+ { required: ["content"] },
5894
+ { required: ["content_base64"] }
5895
+ ]
5777
5896
  };
5778
5897
  workingDir;
5779
5898
  constructor(workingDir) {
@@ -5781,7 +5900,13 @@ var init_file_write = __esm({
5781
5900
  }
5782
5901
  async execute(args) {
5783
5902
  const filePath = extractWritePath(args);
5784
- const content = args["content"] ?? args["text"] ?? args["data"];
5903
+ const decodedContent = decodeTextArgument(args, {
5904
+ fieldLabel: "file_write content",
5905
+ textKeys: ["content", "text", "data"],
5906
+ base64Keys: ["content_base64", "contentBase64", "base64_content", "base64Content"],
5907
+ encodingKeys: ["content_encoding", "contentEncoding", "encoding"],
5908
+ example: `Call: file_write({"path": "${filePath ?? "src/file.ts"}", "content": "..."}) or file_write({"path": "${filePath ?? "src/file.ts"}", "content_base64": "<base64 utf-8>"})`
5909
+ });
5785
5910
  const overwrite = args["overwrite"] === true || args["overwriteExisting"] === true;
5786
5911
  const expectedHash = extractExpectedHash(args);
5787
5912
  const start2 = performance.now();
@@ -5793,14 +5918,15 @@ var init_file_write = __esm({
5793
5918
  durationMs: performance.now() - start2
5794
5919
  };
5795
5920
  }
5796
- if (content === void 0 || content === null || typeof content !== "string") {
5921
+ if (!decodedContent.ok) {
5797
5922
  return {
5798
5923
  success: false,
5799
5924
  output: "",
5800
- error: `file_write requires "content" parameter. Call: file_write({"path": "${filePath}", "content": "..."})`,
5925
+ error: `${decodedContent.error} Do not fall back to shell cat/tee/heredoc for project file writes; retry file_write with content_base64 when quoting or JSON encoding is the problem.`,
5801
5926
  durationMs: performance.now() - start2
5802
5927
  };
5803
5928
  }
5929
+ const content = decodedContent.value;
5804
5930
  try {
5805
5931
  const fullPath = resolve3(this.workingDir, filePath);
5806
5932
  const isNew = !existsSync9(fullPath);
@@ -8539,6 +8665,7 @@ var init_file_edit = __esm({
8539
8665
  init_change_log();
8540
8666
  init_edit_snippet_finder();
8541
8667
  init_edit_metadata();
8668
+ init_text_encoding();
8542
8669
  FileEditTool = class {
8543
8670
  name = "file_edit";
8544
8671
  description = "Make a precise edit to a file by replacing an exact string match. The old_string must be unique in the file unless replace_all is true. Use replace_all to rename variables or change repeated patterns throughout the file.";
@@ -8548,11 +8675,24 @@ var init_file_edit = __esm({
8548
8675
  path: { type: "string", description: "Absolute or relative file path" },
8549
8676
  old_string: {
8550
8677
  type: "string",
8551
- description: "The exact string to search for and replace. Must be unique in the file (or use replace_all)."
8678
+ 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."
8679
+ },
8680
+ old_string_base64: {
8681
+ type: "string",
8682
+ description: "Base64-encoded UTF-8 old_string. Use when exact current text contains quotes, raw JSON, or control characters."
8552
8683
  },
8553
8684
  new_string: {
8554
8685
  type: "string",
8555
- description: "The replacement string"
8686
+ description: "The replacement string. Use new_string_base64 when JSON escaping is fragile."
8687
+ },
8688
+ new_string_base64: {
8689
+ type: "string",
8690
+ description: "Base64-encoded UTF-8 replacement string."
8691
+ },
8692
+ string_encoding: {
8693
+ type: "string",
8694
+ enum: ["utf-8", "base64"],
8695
+ description: "Set to base64 only when both old_string and new_string are base64. Prefer old_string_base64/new_string_base64 for mixed cases."
8556
8696
  },
8557
8697
  replace_all: {
8558
8698
  type: "boolean",
@@ -8563,7 +8703,11 @@ var init_file_edit = __esm({
8563
8703
  description: "Optional SHA-256 from the most recent file_read. If provided, edit is refused when the file changed."
8564
8704
  }
8565
8705
  },
8566
- required: ["path", "old_string", "new_string"]
8706
+ required: ["path"],
8707
+ allOf: [
8708
+ { anyOf: [{ required: ["old_string"] }, { required: ["old_string_base64"] }] },
8709
+ { anyOf: [{ required: ["new_string"] }, { required: ["new_string_base64"] }] }
8710
+ ]
8567
8711
  };
8568
8712
  workingDir;
8569
8713
  constructor(workingDir) {
@@ -8571,8 +8715,20 @@ var init_file_edit = __esm({
8571
8715
  }
8572
8716
  async execute(args) {
8573
8717
  const filePath = extractEditPath(args);
8574
- const oldString = args["old_string"] ?? args["oldString"] ?? args["search"] ?? args["find"];
8575
- const newString = args["new_string"] ?? args["newString"] ?? args["replace"] ?? args["replacement"];
8718
+ const oldDecoded = decodeTextArgument(args, {
8719
+ fieldLabel: "file_edit old_string",
8720
+ textKeys: ["old_string", "oldString", "search", "find"],
8721
+ base64Keys: ["old_string_base64", "oldStringBase64", "search_base64", "searchBase64"],
8722
+ encodingKeys: ["old_string_encoding", "oldStringEncoding", "string_encoding", "stringEncoding"],
8723
+ example: `Call: file_edit({"path": "${filePath ?? "src/file.ts"}", "old_string": "...", "new_string": "..."}) or pass old_string_base64 for exact UTF-8 text that is hard to JSON-escape.`
8724
+ });
8725
+ const newDecoded = decodeTextArgument(args, {
8726
+ fieldLabel: "file_edit new_string",
8727
+ textKeys: ["new_string", "newString", "replace", "replacement"],
8728
+ base64Keys: ["new_string_base64", "newStringBase64", "replacement_base64", "replacementBase64"],
8729
+ encodingKeys: ["new_string_encoding", "newStringEncoding", "string_encoding", "stringEncoding"],
8730
+ example: `Call: file_edit({"path": "${filePath ?? "src/file.ts"}", "old_string": "...", "new_string": "..."}) or pass new_string_base64 for replacement text that is hard to JSON-escape.`
8731
+ });
8576
8732
  const replaceAll = args["replace_all"] === true || args["replaceAll"] === true;
8577
8733
  const expectedHash = extractExpectedHash(args);
8578
8734
  const start2 = performance.now();
@@ -8584,22 +8740,24 @@ var init_file_edit = __esm({
8584
8740
  durationMs: performance.now() - start2
8585
8741
  };
8586
8742
  }
8587
- if (!oldString || typeof oldString !== "string") {
8743
+ if (!oldDecoded.ok || oldDecoded.value.length === 0) {
8588
8744
  return {
8589
8745
  success: false,
8590
8746
  output: "",
8591
- error: `file_edit requires "old_string" parameter. Call: file_edit({"path": "${filePath}", "old_string": "text to find", "new_string": "replacement"})`,
8747
+ error: `${oldDecoded.ok ? "file_edit old_string must be non-empty." : oldDecoded.error} Do not switch to shell sed/perl/python file rewrites for this; retry file_edit with old_string_base64 if exact text encoding is the problem.`,
8592
8748
  durationMs: performance.now() - start2
8593
8749
  };
8594
8750
  }
8595
- if (newString === void 0 || newString === null || typeof newString !== "string") {
8751
+ if (!newDecoded.ok) {
8596
8752
  return {
8597
8753
  success: false,
8598
8754
  output: "",
8599
- error: `file_edit requires "new_string" parameter. Call: file_edit({"path": "${filePath}", "old_string": "...", "new_string": "replacement text"})`,
8755
+ error: `${newDecoded.error} Do not switch to shell sed/perl/python file rewrites for this; retry file_edit with new_string_base64 if exact text encoding is the problem.`,
8600
8756
  durationMs: performance.now() - start2
8601
8757
  };
8602
8758
  }
8759
+ const oldString = oldDecoded.value;
8760
+ const newString = newDecoded.value;
8603
8761
  try {
8604
8762
  const fullPath = resolve7(this.workingDir, filePath);
8605
8763
  const content = await readFile3(fullPath, "utf-8");
@@ -10757,6 +10915,7 @@ var init_file_patch = __esm({
10757
10915
  "use strict";
10758
10916
  init_change_log();
10759
10917
  init_edit_metadata();
10918
+ init_text_encoding();
10760
10919
  FilePatchTool = class {
10761
10920
  name = "file_patch";
10762
10921
  description = "Edit specific line ranges in a file. More precise than string matching for large files. Modes: 'replace' replaces lines start_line..end_line with new_content, 'insert_before' inserts before start_line, 'insert_after' inserts after start_line, 'delete' removes lines start_line..end_line. Use dry_run to preview changes.";
@@ -10777,7 +10936,16 @@ var init_file_patch = __esm({
10777
10936
  },
10778
10937
  new_content: {
10779
10938
  type: "string",
10780
- description: "Replacement content (for replace mode) or content to insert (for insert modes). Required for replace and insert modes. Not needed for delete mode."
10939
+ description: "Replacement content (for replace mode) or content to insert (for insert modes). Required for replace and insert modes. Not needed for delete mode. Use new_content_base64 when JSON escaping is fragile."
10940
+ },
10941
+ new_content_base64: {
10942
+ type: "string",
10943
+ description: "Base64-encoded UTF-8 replacement/insert content. Use for content with quotes, raw JSON, or control characters."
10944
+ },
10945
+ new_content_encoding: {
10946
+ type: "string",
10947
+ enum: ["utf-8", "base64"],
10948
+ description: "Set to base64 when passing base64 text in new_content. Not needed when using new_content_base64."
10781
10949
  },
10782
10950
  expected_hash: {
10783
10951
  type: "string",
@@ -10813,13 +10981,28 @@ var init_file_patch = __esm({
10813
10981
  const endLine = args["end_line"] ?? startLine;
10814
10982
  const mode = args["mode"] ?? "replace";
10815
10983
  const dryRun = args["dry_run"] === true;
10816
- const hasNewContent = typeof args["new_content"] === "string";
10817
- const newContent = hasNewContent ? args["new_content"] : "";
10984
+ const decodedNewContent = decodeTextArgument(args, {
10985
+ fieldLabel: "file_patch new_content",
10986
+ textKeys: ["new_content", "newContent"],
10987
+ base64Keys: ["new_content_base64", "newContentBase64"],
10988
+ encodingKeys: ["new_content_encoding", "newContentEncoding", "content_encoding", "contentEncoding", "encoding"],
10989
+ example: `Call: file_patch({"path": "${filePath ?? "src/file.ts"}", "start_line": 1, "end_line": 1, "new_content": "..."}) or pass "new_content_base64": "<base64 utf-8>".`
10990
+ });
10991
+ const hasNewContent = decodedNewContent.ok;
10992
+ const newContent = decodedNewContent.ok ? decodedNewContent.value : "";
10818
10993
  const expectedHash = extractExpectedHash(args);
10819
10994
  const expectedOldContent = typeof args["expected_old_content"] === "string" ? args["expected_old_content"] : void 0;
10820
10995
  const allowEmptyContent = args["allow_empty_content"] === true;
10821
10996
  const start2 = performance.now();
10822
10997
  try {
10998
+ if (!decodedNewContent.ok && decodedNewContent.reason === "invalid") {
10999
+ return {
11000
+ success: false,
11001
+ output: "",
11002
+ error: `${decodedNewContent.error} Do not use shell heredocs to patch project files; retry file_patch with new_content_base64 or use expected_old_content with exact text.`,
11003
+ durationMs: performance.now() - start2
11004
+ };
11005
+ }
10823
11006
  if (!Number.isInteger(startLine) || startLine < 1) {
10824
11007
  return {
10825
11008
  success: false,
@@ -548287,6 +548470,39 @@ function repairJson(raw) {
548287
548470
  }
548288
548471
  return found ? result : null;
548289
548472
  }
548473
+ function malformedToolArgsMessage(toolName, raw) {
548474
+ const rawStr = String(raw).slice(0, 240);
548475
+ const base3 = `Your ${toolName} tool call had malformed JSON arguments and did not reach the tool implementation. Raw argument preview: "${rawStr}".`;
548476
+ if (toolName === "file_write") {
548477
+ return [
548478
+ base3,
548479
+ `This is usually tool-call JSON encoding, not a filesystem write failure: raw newlines, unescaped quotes, backticks, or control characters broke the arguments before file_write could run.`,
548480
+ `Retry file_write with valid JSON. For full-file content that is hard to escape, pass base64-encoded UTF-8 text as content_base64 instead of content.`,
548481
+ `Do NOT pivot to shell cat/tee/heredoc/python just to write project files; that bypasses file_write overwrite/hash checks, mutation tracking, and diagnostics.`,
548482
+ `Example shape: file_write({"path":"src/file.ts","content_base64":"<base64 utf-8>","overwrite":true,"expected_hash":"<sha256 from file_read if overwriting>"})`
548483
+ ].join(" ");
548484
+ }
548485
+ if (toolName === "file_edit") {
548486
+ return [
548487
+ base3,
548488
+ `This is usually JSON encoding of old_string/new_string, not an edit failure on disk.`,
548489
+ `Retry file_edit with old_string_base64 and/or new_string_base64 for exact UTF-8 text that is hard to JSON-escape.`,
548490
+ `Do NOT pivot to shell sed/perl/python rewrites just to edit project files; keep edits tracked through file_edit/file_patch/file_write.`
548491
+ ].join(" ");
548492
+ }
548493
+ if (toolName === "file_patch") {
548494
+ return [
548495
+ base3,
548496
+ `This is usually JSON encoding of new_content, not a patch failure on disk.`,
548497
+ `Retry file_patch with new_content_base64 for replacement/insert text that is hard to JSON-escape, or use expected_old_content copied exactly from file_read.`,
548498
+ `Do NOT pivot to shell heredocs just to patch project files; keep edits tracked through file_patch/file_edit/file_write.`
548499
+ ].join(" ");
548500
+ }
548501
+ return `${base3} Please call ${toolName} again with valid JSON arguments: double-quoted keys and string values, escaped newlines/quotes, no trailing commas, and no comments.`;
548502
+ }
548503
+ function looksLikeShellFileWrite(command) {
548504
+ return /\bcat\s+[^|;&\n]*>\s*[^&|;\n]+/.test(command) || /\b(?:tee|dd)\b[\s\S]*(?:\bof=|>\s*[^&|;\n]+)/.test(command) || /\b(?:echo|printf)\b[\s\S]*>\s*[^&|;\n]+/.test(command) || /\bpython(?:3)?\b[\s\S]*(?:write_text|open\([^)]*["']w["'])/.test(command) || /<<\s*['"]?\w+['"]?[\s\S]*>\s*[^&|;\n]+/.test(command);
548505
+ }
548290
548506
  function normalizeProviderToolMessage(msg, model) {
548291
548507
  const rawContent2 = typeof msg.content === "string" ? msg.content : "";
548292
548508
  const profile = resolveModelProfile(model);
@@ -553344,7 +553560,7 @@ If this matches your current shape, try it before continuing.`
553344
553560
  ``,
553345
553561
  `Pick ONE of these for your next response:`,
553346
553562
  ``,
553347
- ` (a) PRODUCE: emit a file_write / file_edit / file_patch / shell-mutation that creates or changes content. Even a partial draft of the next planned file is progress.`,
553563
+ ` (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.`,
553348
553564
  ``,
553349
553565
  ` (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.`,
553350
553566
  ``,
@@ -555050,11 +555266,10 @@ ${header}${truncatedCache}`
555050
555266
  const tool = resolvedTool?.tool;
555051
555267
  let result;
555052
555268
  if (tc.arguments && "_raw" in tc.arguments) {
555053
- const rawStr = String(tc.arguments._raw).slice(0, 200);
555054
555269
  result = {
555055
555270
  success: false,
555056
555271
  output: "",
555057
- error: `Your tool call had malformed JSON arguments that could not be parsed: "${rawStr}". Please call ${tc.name} again with valid JSON arguments. Make sure to use double quotes for keys and string values, and do not include trailing commas or comments.`
555272
+ error: malformedToolArgsMessage(tc.name, tc.arguments._raw)
555058
555273
  };
555059
555274
  } else if (!tool) {
555060
555275
  result = {
@@ -556795,6 +557010,18 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
556795
557010
  if (codeBlockMatch && codeBlockMatch[1]?.trim()) {
556796
557011
  const shellCommands = codeBlockMatch[1].trim().split("\n").filter((l2) => l2.trim() && !l2.trim().startsWith("#")).join(" && ");
556797
557012
  if (shellCommands.length > 0 && shellCommands.length < 2e3) {
557013
+ if (looksLikeShellFileWrite(shellCommands)) {
557014
+ messages2.push({
557015
+ role: "user",
557016
+ content: `You wrote a shell code block that appears to create or overwrite files. I did NOT auto-execute it because shell redirection bypasses file_write/file_edit/file_patch tracking and version checks. Use file_write/content_base64 for full-file writes, file_edit for exact replacements, or file_patch/new_content_base64 for line-range patches.`
557017
+ });
557018
+ this.emit({
557019
+ type: "status",
557020
+ content: `Blocked auto-execution of file-writing shell code block; steering back to edit tools`,
557021
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
557022
+ });
557023
+ continue;
557024
+ }
556798
557025
  const shellTool = this.tools.get("shell");
556799
557026
  if (shellTool) {
556800
557027
  this.emit({
@@ -594668,16 +594895,16 @@ var require_source = __commonJS({
594668
594895
  };
594669
594896
  var template;
594670
594897
  var chalkTag = (chalk2, ...strings) => {
594671
- const [firstString] = strings;
594672
- if (!isArray(firstString) || !isArray(firstString.raw)) {
594898
+ const [firstString2] = strings;
594899
+ if (!isArray(firstString2) || !isArray(firstString2.raw)) {
594673
594900
  return strings.join(" ");
594674
594901
  }
594675
594902
  const arguments_ = strings.slice(1);
594676
- const parts = [firstString.raw[0]];
594677
- for (let i2 = 1; i2 < firstString.length; i2++) {
594903
+ const parts = [firstString2.raw[0]];
594904
+ for (let i2 = 1; i2 < firstString2.length; i2++) {
594678
594905
  parts.push(
594679
594906
  String(arguments_[i2 - 1]).replace(/[{}\\]/g, "\\$&"),
594680
- String(firstString.raw[i2])
594907
+ String(firstString2.raw[i2])
594681
594908
  );
594682
594909
  }
594683
594910
  if (template === void 0) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.174",
3
+ "version": "1.0.175",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.174",
9
+ "version": "1.0.175",
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.174",
3
+ "version": "1.0.175",
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",
@@ -41,9 +41,9 @@ If you anticipate a large result before calling a tool, prefer narrow flags firs
41
41
  ## Available Tools
42
42
 
43
43
  - file_read: Read file contents (always read before editing). Supports path, offset, limit.
44
- - file_write: Create or overwrite a file with complete content
45
- - file_edit: Make a precise string replacement in a file (preferred over rewriting). Uses old_string/new_string. old_string must be unique unless replace_all=true. Use replace_all for variable renames.
46
- - file_patch: Edit specific line ranges in large files. Modes: replace (swap lines), insert_before, insert_after, delete. Use dry_run to preview. Best for large files (500+ lines) where string matching is fragile.
44
+ - file_write: Create or overwrite a file with complete UTF-8 text. If full-file content is hard to JSON-escape, use content_base64 instead of shell cat/tee/heredoc.
45
+ - file_edit: Make a precise string replacement in a file (preferred over rewriting). Uses old_string/new_string. old_string must be unique unless replace_all=true. Use old_string_base64/new_string_base64 if exact text is hard to JSON-escape.
46
+ - file_patch: Edit specific line ranges in large files. Modes: replace (swap lines), insert_before, insert_after, delete. Use dry_run to preview. Use new_content_base64 if replacement text is hard to JSON-escape. Best for large files (500+ lines) where string matching is fragile.
47
47
  - find_files: Find files by name pattern (glob). Searches recursively, excludes node_modules/.git.
48
48
  - grep_search: Search file contents with regex. Returns matching lines with paths and line numbers.
49
49
  - shell: Execute any shell command (tests, builds, git, npm, etc.). Supports stdin parameter for input. Commands run with CI=true for non-interactive mode.
@@ -73,6 +73,12 @@ Order: web_search (find) → web_fetch (read) → web_crawl (if JS/multi-page)
73
73
  - debate: Multi-agent debate on a hard sub-decision. Spawns N parallel reasoners that propose, critique each other, and converge on a consensus. Use AFTER you've tried 3-4 different approaches and they have all failed.
74
74
  - replay_with_intervention: DoVer-style replay of a turn-boundary checkpoint with a corrective directive. When you suspect a specific past turn is where you went wrong, replay it under an alternative directive and compare. Run op="list_checkpoints" first to see what's available.
75
75
 
76
+ ## File Editing Discipline
77
+
78
+ - Shell is for commands, builds, tests, and system operations. Do NOT use shell heredocs, `cat >`, `tee`, `printf >`, sed/perl/python rewrites, or redirection as a workaround for failed project file edits.
79
+ - If file_write/file_edit/file_patch reports malformed JSON or encoding trouble, the tool call did not reach the filesystem. Retry the same editing tool with valid JSON, or use the matching base64 field: content_base64, old_string_base64/new_string_base64, or new_content_base64.
80
+ - For existing files, read first and preserve version checks: file_edit/file_patch for targeted changes; file_write with overwrite=true and expected_hash only for intentional full rewrites.
81
+
76
82
  ## Parallel Execution & Sub-Agents
77
83
 
78
84
  - background_run: Run a shell command in the background. Returns a task ID immediately.
@@ -28,9 +28,9 @@ Tool results over ~100KB are NOT truncated. The orchestrator saves the full payl
28
28
  ## Tools
29
29
 
30
30
  - file_read: Read file contents (always read before editing)
31
- - file_write: Create or overwrite a file
32
- - file_edit: Precise string replacement (preferred over rewriting). old_string must be unique.
33
- - file_patch: Edit specific line ranges in large files
31
+ - file_write: Create or overwrite a file with complete UTF-8 text. Use content_base64 when full-file content is hard to JSON-escape.
32
+ - file_edit: Precise string replacement (preferred over rewriting). old_string must be unique. Use old_string_base64/new_string_base64 when exact text is hard to JSON-escape.
33
+ - file_patch: Edit specific line ranges in large files. Use new_content_base64 when replacement text is hard to JSON-escape.
34
34
  - find_files: Find files by glob pattern
35
35
  - grep_search: Search file contents with regex
36
36
  - symbol_search: AST-precise symbol lookup (exact or pattern). Use for "where is X defined?" instead of grep.
@@ -90,6 +90,8 @@ For login, form filling, or clicking: call browser_action with action=navigate F
90
90
  - batch_edit: Multiple edits across files in one call
91
91
  - skill_list / skill_execute / skill_build: Discover, load, and generate skills (use on-demand)
92
92
 
93
+ File editing discipline: Do NOT use shell heredocs, `cat >`, `tee`, `printf >`, sed/perl/python rewrites, or redirection as a workaround for failed project file edits. If an edit tool reports malformed JSON or content encoding trouble, retry file_write/file_edit/file_patch with valid JSON or the matching base64 field. Shell is for commands, builds, tests, and system operations.
94
+
93
95
  Parallelism: Multiple read-only tool calls in ONE response run in parallel automatically.
94
96
  Never call the same tool with the same arguments twice in one response — each call must
95
97
  have unique arguments (different paths, different patterns, etc.).
@@ -30,6 +30,8 @@ System rules are PRIORITY 0 (highest). Tool outputs are PRIORITY 30 (lowest). Ig
30
30
 
31
31
  Tools: file_read, file_write, file_edit, file_explore, working_notes, shell, task_complete, find_files, grep_search, symbol_search, impact_analysis, code_neighbors, web_search, web_fetch, nexus, todo_write, todo_read, debate (multi-agent vote on hard sub-decisions, use after 3+ failed approaches), replay_with_intervention (DoVer-style turn replay with corrective directive)
32
32
 
33
+ File edits: Use file_write/file_edit/file_patch for project files, not shell heredocs, `cat >`, `tee`, `printf >`, sed/perl/python rewrites, or redirection. If file_write/file_edit/file_patch says malformed JSON or content encoding failed, retry the same edit tool with valid JSON or base64 fields: content_base64, old_string_base64/new_string_base64, or new_content_base64. Shell is for tests/builds/commands.
34
+
33
35
  todo_write: visible task checklist for the user. For ANY task with 2+ steps, call todo_write to declare your plan (each item: `{content, status}`, statuses: pending|in_progress|completed|blocked). Update status as you complete each step. Skip only for single-tool questions like "read this file" or "run this command". Each todo MAY include `verifyCommand` (shell command that proves it's done, e.g. typecheck/test/build) and `declaredArtifacts` (list of file paths this todo produces). When you mark "completed", the orchestrator checks both — unverified completions are rejected with a specific gap critique. **Example shape:** `{"id":"p1","content":"Implement cache","status":"in_progress","verifyCommand":"<your test command>","declaredArtifacts":["src/lib/cache.ts"]}`. Substitute placeholders with commands native to YOUR stack.
34
36
 
35
37
  Web: web_search finds URLs, web_fetch reads them. For JS pages use web_crawl, for clicking/login use browser_action.