omnius 1.0.346 → 1.0.347

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
@@ -567219,7 +567219,7 @@ function adversarySystemPrompt() {
567219
567219
  "",
567220
567220
  "Given the agent's latest message and recent tool outcomes, decide whether the claim is proven.",
567221
567221
  "Respond with ONLY a JSON object, no prose, no code fences:",
567222
- '{"class":"false_success|unproven_claim|weak_evidence|false_failure|ok",',
567222
+ '{"class":"false_success|unproven_claim|weak_evidence|false_failure|repeated_action|ok",',
567223
567223
  ' "shortText":"<=12 word headline",',
567224
567224
  ' "confidence":0.0-1.0, // how strongly the claim is NOT proven',
567225
567225
  ' "details":"2-4 sentence skeptical critique citing the specific gap",',
@@ -567232,6 +567232,24 @@ function adversarySystemPrompt() {
567232
567232
  function buildObservationPrompt(obs, recentLedger) {
567233
567233
  const outcomes = obs.recentToolOutcomes.slice(-8).map((o2) => ` - ${o2.tool}: ${o2.succeeded ? "OK" : "FAIL"} — ${o2.preview.slice(0, 120)}`).join("\n");
567234
567234
  const priorDoubts = recentLedger.slice(-3).filter((e2) => e2.verdict !== "ok").map((e2) => ` - turn ${e2.turn}: ${e2.verdict} — demanded: ${e2.demand}`).join("\n");
567235
+ if (obs.loopSignal) {
567236
+ const ls2 = obs.loopSignal;
567237
+ return [
567238
+ `The agent has repeated the SAME action ${ls2.count}× this run: ${ls2.tool} on ${ls2.target}.`,
567239
+ `This is a loop. Reason about WHY — for THIS specific call, not generically.`,
567240
+ ls2.alreadyHave ? `Evidence the agent ALREADY obtained from a prior identical call:
567241
+ ${ls2.alreadyHave.slice(0, 900)}` : `(No cached result available for the repeated call.)`,
567242
+ "",
567243
+ "Agent's latest message:",
567244
+ obs.assistantText.slice(0, 1200) || "(empty)",
567245
+ "",
567246
+ "Recent tool outcomes:",
567247
+ outcomes || " (none)",
567248
+ "",
567249
+ `Decide: does the agent already HAVE what this repeated call would return (class "repeated_action"), and if so what should it do INSTEAD? Be concrete — cite the specific content above, name the next action. If the repeat is actually justified (state genuinely changed), say class "ok".`,
567250
+ "Return ONLY the JSON object."
567251
+ ].join("\n");
567252
+ }
567235
567253
  return [
567236
567254
  obs.claimsCompletion ? "The agent is asserting COMPLETION this turn." : "The agent produced a progress/success-flavored claim this turn.",
567237
567255
  "",
@@ -567263,6 +567281,7 @@ function parseAdversaryCritique(raw) {
567263
567281
  }
567264
567282
  const cls = String(obj["class"] ?? "").toLowerCase();
567265
567283
  const valid = [
567284
+ "repeated_action",
567266
567285
  "false_success",
567267
567286
  "unproven_claim",
567268
567287
  "weak_evidence",
@@ -567316,17 +567335,22 @@ var init_adversaryStream = __esm({
567316
567335
  shouldAudit(obs) {
567317
567336
  if (obs.claimsCompletion)
567318
567337
  return true;
567338
+ if (obs.loopSignal)
567339
+ return true;
567319
567340
  return SUCCESS_LANGUAGE.test(obs.assistantText);
567320
567341
  }
567321
567342
  /** Ingest an observation. Replaces any prior un-audited pending observation. */
567322
567343
  observe(obs) {
567323
567344
  if (!this.shouldAudit(obs))
567324
567345
  return;
567325
- const sig = `${obs.turn}:${obs.assistantText.slice(0, 200)}`;
567346
+ const loopKey = obs.loopSignal ? `loop:${obs.loopSignal.tool}:${obs.loopSignal.target}:${obs.loopSignal.count}` : "";
567347
+ const sig = `${obs.turn}:${loopKey}:${obs.assistantText.slice(0, 200)}`;
567326
567348
  if (sig === this.lastAuditedSignature)
567327
567349
  return;
567328
567350
  this.pending = obs;
567351
+ this._pendingSig = sig;
567329
567352
  }
567353
+ _pendingSig = "";
567330
567354
  /**
567331
567355
  * Fire the adversary inference if there is a pending observation and no call
567332
567356
  * in flight. Detached/non-blocking — resolves when (or if) a critique lands.
@@ -567337,21 +567361,23 @@ var init_adversaryStream = __esm({
567337
567361
  return null;
567338
567362
  const obs = this.pending;
567339
567363
  this.pending = null;
567340
- this.lastAuditedSignature = `${obs.turn}:${obs.assistantText.slice(0, 200)}`;
567364
+ this.lastAuditedSignature = this._pendingSig || `${obs.turn}:${obs.assistantText.slice(0, 200)}`;
567341
567365
  this.inFlight = true;
567342
567366
  try {
567343
- const resp = await this.backend.chatCompletion({
567344
- messages: [
567345
- { role: "system", content: adversarySystemPrompt() },
567346
- { role: "user", content: buildObservationPrompt(obs, this.ledger) }
567347
- ],
567348
- tools: [],
567349
- temperature: 0,
567350
- maxTokens: 400,
567351
- timeoutMs: this.timeoutMs
567352
- });
567353
- const content = resp.choices?.[0]?.message?.content ?? "";
567354
- const critique2 = parseAdversaryCritique(content);
567367
+ let critique2 = null;
567368
+ for (let attempt = 0; attempt < 2 && !critique2; attempt++) {
567369
+ const resp = await this.backend.chatCompletion({
567370
+ messages: [
567371
+ { role: "system", content: adversarySystemPrompt() },
567372
+ { role: "user", content: buildObservationPrompt(obs, this.ledger) }
567373
+ ],
567374
+ tools: [],
567375
+ temperature: 0,
567376
+ maxTokens: 900,
567377
+ timeoutMs: this.timeoutMs
567378
+ });
567379
+ critique2 = parseAdversaryCritique(resp.choices?.[0]?.message?.content ?? "");
567380
+ }
567355
567381
  if (!critique2)
567356
567382
  return null;
567357
567383
  this.ledger.push({
@@ -570624,6 +570650,29 @@ Your hypotheses MUST address this specific error, not generic causes.
570624
570650
  }
570625
570651
  return best && best.count >= 3 ? best : null;
570626
570652
  }
570653
+ /**
570654
+ * Backend adapter for AUXILIARY inference (adversary critiques, branch
570655
+ * extraction) — tool-less, think-off, JSON-shaped calls. The main backend's
570656
+ * chatCompletion routes to Ollama's /v1/chat/completions, where qwen3-family
570657
+ * models IGNORE think:false and /no_think and (with no tools to anchor
570658
+ * output) emit a reasoning-only response that gets stripped to EMPTY. The
570659
+ * native /api/chat path honors think:false. This adapter prefers it and sets
570660
+ * a responseFormat so the native path enforces JSON mode. Falls back to
570661
+ * chatCompletion for non-Ollama backends.
570662
+ */
570663
+ _auxInferenceBackend() {
570664
+ const b = this.backend;
570665
+ const useNative = typeof b.nativeOllamaChatCompletion === "function";
570666
+ return {
570667
+ chatCompletion: (req3) => {
570668
+ const r2 = {
570669
+ ...req3,
570670
+ responseFormat: req3.responseFormat ?? { type: "json_object" }
570671
+ };
570672
+ return useNative ? b.nativeOllamaChatCompletion(r2) : b.chatCompletion(r2);
570673
+ }
570674
+ };
570675
+ }
570627
570676
  /**
570628
570677
  * Detect a failing approach and return a decisive root-cause directive, or
570629
570678
  * null. Fires when a non-transient error recurs ≥3× in the recent window
@@ -575187,7 +575236,9 @@ TASK: ${scrubbedTask}` : scrubbedTask;
575187
575236
  if (this.options.disableAdversaryCritic !== true && this.backend && typeof this.backend.chatCompletion === "function") {
575188
575237
  const persistPath = this._workingDirectory ? _pathJoin(this._workingDirectory, ".omnius", "memory", "adversary-stream.json") : null;
575189
575238
  this._adversaryStream = new AdversaryStream({
575190
- backend: this.backend,
575239
+ // Native /api/chat (think:false honored) — NOT /v1, which returns empty
575240
+ // for tool-less think-off calls on qwen3-family models.
575241
+ backend: this._auxInferenceBackend(),
575191
575242
  persistPath,
575192
575243
  onCritique: (critique2, sourceTurn) => {
575193
575244
  if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
@@ -577755,6 +577806,24 @@ Use the saved fact to continue the promised synthesis or next concrete step, or
577755
577806
  },
577756
577807
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
577757
577808
  });
577809
+ if (this._adversaryStream && criticDecision.hitNumber >= 2) {
577810
+ const _args = tc.arguments;
577811
+ const _target = String(_args?.["path"] ?? _args?.["file"] ?? _args?.["command"] ?? JSON.stringify(_args ?? {}).slice(0, 80));
577812
+ this._adversaryStream.observe({
577813
+ turn,
577814
+ assistantText: "",
577815
+ recentToolOutcomes: this._adversaryToolOutcomes.slice(-8).map((o2) => ({ tool: o2.tool, succeeded: o2.succeeded, preview: o2.preview })),
577816
+ claimsCompletion: false,
577817
+ loopSignal: {
577818
+ tool: tc.name,
577819
+ target: _target,
577820
+ count: criticDecision.hitNumber,
577821
+ alreadyHave: _existingFp?.result
577822
+ }
577823
+ });
577824
+ void this._adversaryStream.tick().catch(() => {
577825
+ });
577826
+ }
577758
577827
  const _repeatGateMax = this._resolveRepeatGateMax();
577759
577828
  const repeatGateEligible = isReadLike || tc.name === "memory_write";
577760
577829
  if (repeatGateEligible && _existingFp !== void 0 && _repeatGateMax > 0) {
@@ -578868,7 +578937,9 @@ Respond with EXACTLY this structure before your next tool call:
578868
578937
  content: fullContent,
578869
578938
  // the REAL body, not the preview
578870
578939
  fileVersion: this._worldFacts.files.get(pRaw)?.writeCount ?? 0,
578871
- backend: this.backend,
578940
+ // Native /api/chat so the extractor LLM fallback isn't
578941
+ // silently empty on qwen3-family models.
578942
+ backend: this._auxInferenceBackend(),
578872
578943
  timeoutMs: 3e4
578873
578944
  });
578874
578945
  output = [
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.346",
3
+ "version": "1.0.347",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.346",
9
+ "version": "1.0.347",
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.346",
3
+ "version": "1.0.347",
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",