omnius 1.0.270 → 1.0.272

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
@@ -546546,7 +546546,10 @@ var init_agent_tool = __esm({
546546
546546
  lines.push(prompt);
546547
546547
  return lines.join("\n");
546548
546548
  })();
546549
- if (isolation === "worktree") {
546549
+ const fullDefault = process.env["OMNIUS_FULL_SUBAGENT_DEFAULT"] !== "0";
546550
+ const workCapableType = subagentType === "general" || subagentType === "coordinator";
546551
+ const routeToSubprocess = isolation === "worktree" || fullDefault && runInBackground && workCapableType;
546552
+ if (routeToSubprocess) {
546550
546553
  this.callbacks.onViewRegister?.(agentId, label);
546551
546554
  const spawn35 = this.callbacks.spawnSubprocess({
546552
546555
  id: agentId,
@@ -555351,6 +555354,31 @@ var init_textSanitize = __esm({
555351
555354
  }
555352
555355
  });
555353
555356
 
555357
+ // packages/orchestrator/dist/permissionRuleset.js
555358
+ function checkDoomLoop(context2) {
555359
+ const history = context2.toolCallHistory;
555360
+ if (!history || history.length < 3)
555361
+ return null;
555362
+ const lastCall = history[history.length - 1];
555363
+ const recent = history.slice(-4);
555364
+ let identicalCount = 1;
555365
+ for (let i2 = history.length - 2; i2 >= Math.max(0, history.length - 4); i2--) {
555366
+ const prev = history[i2];
555367
+ if (prev.tool === lastCall.tool && JSON.stringify(prev.args) === JSON.stringify(lastCall.args)) {
555368
+ identicalCount++;
555369
+ }
555370
+ }
555371
+ if (identicalCount >= 3) {
555372
+ return "ask";
555373
+ }
555374
+ return null;
555375
+ }
555376
+ var init_permissionRuleset = __esm({
555377
+ "packages/orchestrator/dist/permissionRuleset.js"() {
555378
+ "use strict";
555379
+ }
555380
+ });
555381
+
555354
555382
  // packages/orchestrator/dist/completionContract.js
555355
555383
  function normalizeText(value2, max = 500) {
555356
555384
  const text2 = String(value2 ?? "").trim().replace(/\s+/g, " ");
@@ -564853,7 +564881,7 @@ RECOVERY: cd to the directory containing '${file}', run a plain install with no
564853
564881
 
564854
564882
  // packages/orchestrator/dist/agenticRunner.js
564855
564883
  import { existsSync as _fsExistsSync, readFileSync as _fsReadFileSync, writeFileSync as _fsWriteFileSync, appendFileSync as _fsAppendFileSync, unlinkSync as _fsUnlinkSync, mkdirSync as _fsMkdirSync } from "node:fs";
564856
- import { execFile as _execFile } from "node:child_process";
564884
+ import { execFile as _execFile, spawn as _spawn } from "node:child_process";
564857
564885
  import { createHash as _createHash } from "node:crypto";
564858
564886
  import { join as _pathJoin, resolve as _pathResolve } from "node:path";
564859
564887
  import { tmpdir as _osTmpdir } from "node:os";
@@ -565337,6 +565365,7 @@ var init_agenticRunner = __esm({
565337
565365
  "packages/orchestrator/dist/agenticRunner.js"() {
565338
565366
  "use strict";
565339
565367
  init_textSanitize();
565368
+ init_permissionRuleset();
565340
565369
  init_completionLedger();
565341
565370
  init_dist6();
565342
565371
  init_ollama_pool();
@@ -565686,6 +565715,10 @@ var init_agenticRunner = __esm({
565686
565715
  // post-implementation reviewer.
565687
565716
  _fileWritesThisRun = 0;
565688
565717
  _backwardPassCyclesUsed = 0;
565718
+ // Completion compile/verify gate: count of times task_complete was blocked
565719
+ // because the configured verify command failed. Bounded by
565720
+ // OMNIUS_COMPLETION_VERIFY_MAX to avoid an endless verify→fix→verify loop.
565721
+ _completionVerifyRejections = 0;
565689
565722
  _lastBackwardPassVerdict = null;
565690
565723
  _lastBackwardPassCritique = null;
565691
565724
  // Run-local completion contract inferred from the user's ask/context before
@@ -566057,6 +566090,7 @@ ${parts.join("\n")}
566057
566090
  allowTurnExtension: options2?.allowTurnExtension ?? true,
566058
566091
  completionProvenanceGuard: options2?.completionProvenanceGuard ?? true,
566059
566092
  backwardPassReview: options2?.backwardPassReview,
566093
+ completionVerifyCommand: options2?.completionVerifyCommand,
566060
566094
  disableAdversaryCritic,
566061
566095
  disableStepCritic: disableAdversaryCritic,
566062
566096
  modelTier: options2?.modelTier ?? "large",
@@ -567322,7 +567356,114 @@ ${input.answerText ?? ""}`.toLowerCase().trim();
567322
567356
  * up auto-blocking and surfaces a status event so the caller can take
567323
567357
  * a different path (eg. surface to user). max cycles enforced here.
567324
567358
  */
567359
+ /**
567360
+ * Completion compile/verify gate (opt-in). Runs a configured shell command
567361
+ * (e.g. a typecheck/build) before `task_complete` is accepted, but ONLY when
567362
+ * the run actually modified code files. A non-zero exit blocks completion and
567363
+ * feeds the error output back so the model fixes it before retrying. Bounded
567364
+ * by OMNIUS_COMPLETION_VERIFY_MAX rejections so a persistently-failing build
567365
+ * does not trap the run forever (it completes with a caveat instead — the
567366
+ * same anti-collapse philosophy as the backward-pass cycle cap).
567367
+ *
567368
+ * Default OFF: unset command → `{ proceed: true }`, preserving the prior
567369
+ * "no deterministic verification gate" behaviour for every existing caller.
567370
+ */
567371
+ async _runCompletionVerifyGate(turn) {
567372
+ const cmd = this.options.completionVerifyCommand || process.env["OMNIUS_COMPLETION_VERIFY_CMD"] || "";
567373
+ if (!cmd.trim())
567374
+ return { proceed: true };
567375
+ const changed = [...this._taskState.modifiedFiles.keys()];
567376
+ const codeChanged = changed.some((p2) => /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|c|cc|cpp|h|hpp|swift|rb|php)$/i.test(p2));
567377
+ if (!codeChanged)
567378
+ return { proceed: true };
567379
+ const maxRejections = parseInt(process.env["OMNIUS_COMPLETION_VERIFY_MAX"] || "3", 10) || 3;
567380
+ if (this._completionVerifyRejections >= maxRejections) {
567381
+ this._completionCaveat = [
567382
+ `[COMPLETION CAVEAT] Verify command still failing after ${this._completionVerifyRejections} attempt(s): ${cmd}`,
567383
+ "Completing with unresolved build/verify errors — treat as a follow-up blocker."
567384
+ ].join("\n");
567385
+ this.emit({
567386
+ type: "status",
567387
+ content: `completion verify gate exhausted after ${this._completionVerifyRejections} attempts; completing with caveat`,
567388
+ turn,
567389
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
567390
+ });
567391
+ return { proceed: true };
567392
+ }
567393
+ const timeoutMs = parseInt(process.env["OMNIUS_COMPLETION_VERIFY_TIMEOUT_MS"] || "180000", 10) || 18e4;
567394
+ const wd = this._workingDirectory || process.cwd();
567395
+ this.emit({
567396
+ type: "status",
567397
+ content: `completion verify gate: running '${cmd}' (cwd=${wd})`,
567398
+ turn,
567399
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
567400
+ });
567401
+ const { exitCode, outTail } = await new Promise((resolve70) => {
567402
+ const chunks = [];
567403
+ let settled = false;
567404
+ const child = _spawn(cmd, {
567405
+ cwd: wd,
567406
+ shell: true,
567407
+ env: process.env,
567408
+ stdio: ["ignore", "pipe", "pipe"]
567409
+ });
567410
+ const onData = (d2) => {
567411
+ chunks.push(d2.toString());
567412
+ if (chunks.length > 400)
567413
+ chunks.splice(0, chunks.length - 400);
567414
+ };
567415
+ child.stdout?.on("data", onData);
567416
+ child.stderr?.on("data", onData);
567417
+ const killTimer = setTimeout(() => {
567418
+ try {
567419
+ child.kill("SIGKILL");
567420
+ } catch {
567421
+ }
567422
+ }, timeoutMs);
567423
+ killTimer.unref?.();
567424
+ const finish = (code8) => {
567425
+ if (settled)
567426
+ return;
567427
+ settled = true;
567428
+ clearTimeout(killTimer);
567429
+ resolve70({ exitCode: code8, outTail: chunks.join("").slice(-4e3) });
567430
+ };
567431
+ child.on("error", () => finish(1));
567432
+ child.on("close", (code8) => finish(code8 ?? 1));
567433
+ });
567434
+ if (exitCode === 0) {
567435
+ this.emit({
567436
+ type: "status",
567437
+ content: `completion verify gate PASSED ('${cmd}')`,
567438
+ turn,
567439
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
567440
+ });
567441
+ return { proceed: true };
567442
+ }
567443
+ this._completionVerifyRejections++;
567444
+ this.emit({
567445
+ type: "status",
567446
+ content: `completion verify gate FAILED (exit ${exitCode}); task_complete blocked (attempt ${this._completionVerifyRejections}/${maxRejections})`,
567447
+ turn,
567448
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
567449
+ });
567450
+ const feedback = [
567451
+ `[COMPLETION BLOCKED — verification failed] You modified code, but the verify command exited ${exitCode}:`,
567452
+ ` $ ${cmd}`,
567453
+ "",
567454
+ "You CANNOT call task_complete until this passes. Fix the errors below, then",
567455
+ "re-run the verify command yourself (via shell) to confirm before completing.",
567456
+ `(attempt ${this._completionVerifyRejections}/${maxRejections} — after that the run completes with a caveat)`,
567457
+ "",
567458
+ "--- verify output (tail) ---",
567459
+ outTail || "(no output captured)"
567460
+ ].join("\n");
567461
+ return { proceed: false, feedback };
567462
+ }
567325
567463
  async _runBackwardPassReview(turn, toolCallLog, proposedSummary = "") {
567464
+ const _verify = await this._runCompletionVerifyGate(turn);
567465
+ if (!_verify.proceed)
567466
+ return _verify;
567326
567467
  if (this._completionLedger && proposedSummary) {
567327
567468
  const _newClaims = deriveClaimsFromProposedText({
567328
567469
  text: proposedSummary,
@@ -567832,10 +567973,12 @@ ${body}`;
567832
567973
  }
567833
567974
  const prior = seen.get(fp);
567834
567975
  if (prior) {
567976
+ const priorContent = typeof messages2[prior.idx]?.content === "string" ? messages2[prior.idx].content : "";
567977
+ const priorFailed = ERROR_MARKERS.test(priorContent);
567835
567978
  pending2.push({
567836
567979
  idx: prior.idx,
567837
567980
  reason: "dedupe",
567838
- replacement: `${DEDUPE_PREFIX} ${scanTurn} — duplicate ${name10}() call]`
567981
+ replacement: priorFailed ? `${DEDUPE_PREFIX} ${scanTurn} — duplicate ${name10}() call; the earlier call FAILED (same error as the retained copy below). Do not retry identically.]` : `${DEDUPE_PREFIX} ${scanTurn} — duplicate ${name10}() call]`
567839
567982
  });
567840
567983
  seen.set(fp, { turn: scanTurn, idx: resultIdx });
567841
567984
  continue;
@@ -567847,10 +567990,11 @@ ${body}`;
567847
567990
  if (priorResource && priorResource.idx !== resultIdx) {
567848
567991
  const priorContent = typeof messages2[priorResource.idx]?.content === "string" ? messages2[priorResource.idx].content : "";
567849
567992
  if (!priorContent.startsWith(PRUNE_PREFIX) && !priorContent.startsWith(DEDUPE_PREFIX) && !priorContent.startsWith(FILE_AGED_PREFIX)) {
567993
+ const priorResFailed = ERROR_MARKERS.test(priorContent);
567850
567994
  pending2.push({
567851
567995
  idx: priorResource.idx,
567852
567996
  reason: "dedupe",
567853
- replacement: `${DEDUPE_PREFIX} ${scanTurn} — semantic duplicate ${name10}() (same resource: ${rkey})]`
567997
+ replacement: priorResFailed ? `${DEDUPE_PREFIX} ${scanTurn} — semantic duplicate ${name10}() (same resource: ${rkey}); the earlier call FAILED. Do not retry identically.]` : `${DEDUPE_PREFIX} ${scanTurn} — semantic duplicate ${name10}() (same resource: ${rkey})]`
567854
567998
  });
567855
567999
  }
567856
568000
  }
@@ -570164,6 +570308,7 @@ Respond with your assessment, then take action.`;
570164
570308
  this._abortController = new AbortController();
570165
570309
  this._fileWritesThisRun = 0;
570166
570310
  this._backwardPassCyclesUsed = 0;
570311
+ this._completionVerifyRejections = 0;
570167
570312
  this._lastBackwardPassVerdict = null;
570168
570313
  this._lastBackwardPassCritique = null;
570169
570314
  this._completionContract = null;
@@ -572790,6 +572935,44 @@ Corrective action: try a different approach first: read relevant files, adjust a
572790
572935
  timestampMs: Date.now()
572791
572936
  });
572792
572937
  const _toolLogTailIdx = toolCallLog.length - 1;
572938
+ if (tc.name !== "task_complete") {
572939
+ const doomHistory = toolCallLog.slice(-5).map((e2) => ({ tool: e2.name, args: e2.argsKey }));
572940
+ doomHistory.push({
572941
+ tool: tc.name,
572942
+ args: this._buildExactArgsKey(tc.arguments ?? {})
572943
+ });
572944
+ const doom = checkDoomLoop({
572945
+ permission: tc.name,
572946
+ target: "",
572947
+ toolCallHistory: doomHistory
572948
+ });
572949
+ if (doom === "ask" && cohort && cohort.failure >= 3 && cohort.success === 0) {
572950
+ const stopMsg = `[blocked: doom-loop] '${tc.name}' has been called with identical arguments 3+ times and has failed every time. This exact call was NOT executed. Stop repeating it. Choose ONE: (a) change the arguments or approach, (b) read/inspect to understand why it fails, or (c) if genuinely blocked, call task_complete and state the blocker plainly.`;
572951
+ this.emit({
572952
+ type: "tool_call",
572953
+ toolName: tc.name,
572954
+ toolArgs: tc.arguments,
572955
+ turn,
572956
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
572957
+ });
572958
+ const lastLog = toolCallLog[_toolLogTailIdx];
572959
+ if (lastLog) {
572960
+ lastLog.success = false;
572961
+ lastLog.mutated = false;
572962
+ lastLog.mutatedFiles = [];
572963
+ lastLog.outputPreview = stopMsg.slice(0, 100);
572964
+ }
572965
+ this.emit({
572966
+ type: "tool_result",
572967
+ toolName: tc.name,
572968
+ success: false,
572969
+ content: stopMsg.slice(0, 200),
572970
+ turn,
572971
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
572972
+ });
572973
+ return { tc, output: stopMsg, success: false };
572974
+ }
572975
+ }
572793
572976
  if (editFeedbackRequiredBeforeMoreEdits && this._isProjectEditTool(tc.name)) {
572794
572977
  this.emit({
572795
572978
  type: "tool_call",
@@ -573971,8 +574154,23 @@ Respond with EXACTLY this structure before your next tool call:
573971
574154
  }
573972
574155
  }
573973
574156
  const isFileMutation = realFileMutation;
573974
- if (isFileMutation && dedupHitCount.size > 0) {
573975
- dedupHitCount.clear();
574157
+ if (isFileMutation) {
574158
+ if (realMutationPaths.length > 0) {
574159
+ for (const mp of realMutationPaths) {
574160
+ if (!mp)
574161
+ continue;
574162
+ for (const key of Array.from(dedupHitCount.keys())) {
574163
+ if (key.includes(mp))
574164
+ dedupHitCount.delete(key);
574165
+ }
574166
+ for (const key of Array.from(recentToolResults.keys())) {
574167
+ if (key.includes(mp))
574168
+ recentToolResults.delete(key);
574169
+ }
574170
+ }
574171
+ } else if (dedupHitCount.size > 0) {
574172
+ dedupHitCount.clear();
574173
+ }
573976
574174
  }
573977
574175
  if (isFileMutation && recentToolResults.size > 0) {
573978
574176
  for (const key of Array.from(recentToolResults.keys())) {
@@ -602335,7 +602533,7 @@ var init_call_agent = __esm({
602335
602533
  }
602336
602534
  /** Initialize the runner with appropriate tools for the access tier */
602337
602535
  async init() {
602338
- const backend = new OllamaAgenticBackend(this.config.backendUrl, this.config.model, this.config.apiKey);
602536
+ const backend = new OllamaAgenticBackend(this.config.backendUrl, this.config.model, this.config.apiKey, false);
602339
602537
  this.backend = backend;
602340
602538
  const feed = getActivityFeed();
602341
602539
  const systemPrompt = this.buildSystemPrompt();
@@ -609025,15 +609223,10 @@ ${CONTENT_BG_SEQ}`);
609025
609223
  copySessionToClipboard() {
609026
609224
  const lines = this._contentLines;
609027
609225
  if (!lines || lines.length === 0) return;
609028
- const offset = this._contentScrollOffset;
609029
- const visible = this.contentHeight;
609030
- const startIdx = Math.max(0, offset);
609031
- const endIdx = Math.min(lines.length, startIdx + visible);
609032
- const visibleLines = lines.slice(startIdx, endIdx);
609033
- const stripped = visibleLines.map(
609226
+ const stripped = lines.map(
609034
609227
  (line) => line.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1B\]?[^\x07]*\x07/g, "")
609035
609228
  );
609036
- const text2 = stripped.join("\n");
609229
+ const text2 = stripped.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
609037
609230
  if (text2.length === 0) return;
609038
609231
  const ok3 = copyText(text2);
609039
609232
  const pos = this.rowPositions(termRows());
@@ -705646,7 +705839,13 @@ Review its full output via sub_agent(action='output', id='${id}')`
705646
705839
  backend = new OllamaAgenticBackend(
705647
705840
  config.backendUrl,
705648
705841
  opts.model,
705649
- config.apiKey
705842
+ config.apiKey,
705843
+ // Sub-agents: force thinking OFF explicitly. Thinking models
705844
+ // interleave <think> blocks that corrupt tool-call parsing for
705845
+ // Qwen/Ollama. Relying on the constructor default is fragile
705846
+ // (breaks if OMNIUS_ENABLE_THINKING=1 is inherited); pass false
705847
+ // so sub-agent tool loops are always clean.
705848
+ false
705650
705849
  );
705651
705850
  }
705652
705851
  const subTier = getModelTier(opts.model);
@@ -705683,6 +705882,10 @@ Review its full output via sub_agent(action='output', id='${id}')`
705683
705882
  `Sub-agent ${subAgentId}`,
705684
705883
  "sub"
705685
705884
  );
705885
+ subRunner.onEvent((event) => {
705886
+ const line = formatSubAgentEventForView(event);
705887
+ if (line) statusBar.writeToAgentView(subAgentId, line);
705888
+ });
705686
705889
  subRunner.registerTool(
705687
705890
  createTaskCompleteTool(subTier, repoRoot, true)
705688
705891
  );
@@ -705725,11 +705928,21 @@ ${result.summary}`
705725
705928
  };
705726
705929
  },
705727
705930
  spawnSubprocess: (opts) => {
705728
- const entry = spawnFullSubAgent(opts.task, {
705729
- model: opts.model,
705730
- backendUrl: config.backendUrl,
705731
- workingDir: opts.workingDir
705732
- });
705931
+ const entry = spawnFullSubAgent(
705932
+ opts.task,
705933
+ {
705934
+ model: opts.model,
705935
+ backendUrl: config.backendUrl,
705936
+ workingDir: opts.workingDir
705937
+ },
705938
+ // Stream the full sub-agent's stdout into its toolbar view so the
705939
+ // clickable tab shows live output instead of staying empty.
705940
+ (text2) => statusBar.writeToAgentView(opts.id, text2),
705941
+ (id, exitCode) => statusBar.updateAgentViewStatus(
705942
+ opts.id,
705943
+ exitCode === 0 ? "completed" : "failed"
705944
+ )
705945
+ );
705733
705946
  return { id: entry.id, pid: entry.pid ?? 0 };
705734
705947
  },
705735
705948
  trackTask: (id, promise) => {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.270",
3
+ "version": "1.0.272",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.270",
9
+ "version": "1.0.272",
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.270",
3
+ "version": "1.0.272",
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",