omnius 1.0.355 → 1.0.357

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
@@ -33658,7 +33658,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
33658
33658
  }
33659
33659
  const nodeModulesDir = resolve18(this.repoRoot, "node_modules");
33660
33660
  let nexusResolved = false;
33661
- const execAsync3 = (cmd, opts = {}) => new Promise((res, rej) => {
33661
+ const execAsync4 = (cmd, opts = {}) => new Promise((res, rej) => {
33662
33662
  const { exec: ex } = __require("node:child_process");
33663
33663
  ex(cmd, { encoding: "utf8", timeout: opts.timeout ?? 3e4, cwd: opts.cwd, maxBuffer: 10 * 1024 * 1024 }, (err, stdout) => {
33664
33664
  if (err)
@@ -33673,7 +33673,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
33673
33673
  const failures = [];
33674
33674
  for (const cmd of cmds) {
33675
33675
  try {
33676
- await execAsync3(cmd, { cwd: this.repoRoot, timeout: 18e4 });
33676
+ await execAsync4(cmd, { cwd: this.repoRoot, timeout: 18e4 });
33677
33677
  return `Repaired node-datachannel native addon with: ${cmd.replace(" 2>&1", "")}`;
33678
33678
  } catch (err) {
33679
33679
  failures.push(`${cmd.replace(" 2>&1", "")}: ${err?.message ?? String(err)}`.slice(0, 500));
@@ -33688,7 +33688,7 @@ ${failures.join("\n")}`;
33688
33688
  nexusResolved = true;
33689
33689
  } else {
33690
33690
  try {
33691
- const globalDir2 = await execAsync3("npm root -g", { timeout: 5e3 });
33691
+ const globalDir2 = await execAsync4("npm root -g", { timeout: 5e3 });
33692
33692
  const globalPkg = join31(globalDir2, "open-agents-nexus", "package.json");
33693
33693
  if (existsSync30(globalPkg)) {
33694
33694
  nexusResolved = true;
@@ -33701,13 +33701,13 @@ ${failures.join("\n")}`;
33701
33701
  if (!nexusResolved) {
33702
33702
  const installSpec = `open-agents-nexus@${OPEN_AGENTS_NEXUS_BUNDLED_SPEC}`;
33703
33703
  try {
33704
- await execAsync3(`npm install ${installSpec} 2>&1`, {
33704
+ await execAsync4(`npm install ${installSpec} 2>&1`, {
33705
33705
  cwd: this.repoRoot,
33706
33706
  timeout: 12e4
33707
33707
  });
33708
33708
  } catch {
33709
33709
  try {
33710
- await execAsync3(`npm install -g ${installSpec} 2>&1`, { timeout: 12e4 });
33710
+ await execAsync4(`npm install -g ${installSpec} 2>&1`, { timeout: 12e4 });
33711
33711
  } catch {
33712
33712
  throw new Error(`Failed to install ${installSpec}. Run: npm install ${installSpec}`);
33713
33713
  }
@@ -33752,7 +33752,7 @@ ${failures.join("\n")}`;
33752
33752
  const agentType = args.agent_type || "general";
33753
33753
  const nodePaths = [nodeModulesDir];
33754
33754
  try {
33755
- const globalDir2 = await execAsync3("npm root -g", { timeout: 5e3 });
33755
+ const globalDir2 = await execAsync4("npm root -g", { timeout: 5e3 });
33756
33756
  nodePaths.push(globalDir2);
33757
33757
  } catch {
33758
33758
  }
@@ -98730,12 +98730,12 @@ var require_x509_cjs = __commonJS({
98730
98730
  var require_crypto = __commonJS({
98731
98731
  "../node_modules/acme-client/src/crypto/index.js"(exports) {
98732
98732
  var net5 = __require("net");
98733
- var { promisify: promisify8 } = __require("util");
98733
+ var { promisify: promisify9 } = __require("util");
98734
98734
  var crypto14 = __require("crypto");
98735
98735
  var asn1js4 = require_build2();
98736
98736
  var x5093 = require_x509_cjs();
98737
- var randomInt2 = promisify8(crypto14.randomInt);
98738
- var generateKeyPair2 = promisify8(crypto14.generateKeyPair);
98737
+ var randomInt2 = promisify9(crypto14.randomInt);
98738
+ var generateKeyPair2 = promisify9(crypto14.generateKeyPair);
98739
98739
  x5093.cryptoProvider.set(crypto14.webcrypto);
98740
98740
  var subjectAltNameOID = "2.5.29.17";
98741
98741
  var alpnAcmeIdentifierOID = "1.3.6.1.5.5.7.1.31";
@@ -133580,9 +133580,9 @@ var require_lib = __commonJS({
133580
133580
  var require_forge2 = __commonJS({
133581
133581
  "../node_modules/acme-client/src/crypto/forge.js"(exports) {
133582
133582
  var net5 = __require("net");
133583
- var { promisify: promisify8 } = __require("util");
133583
+ var { promisify: promisify9 } = __require("util");
133584
133584
  var forge = require_lib();
133585
- var generateKeyPair2 = promisify8(forge.pki.rsa.generateKeyPair);
133585
+ var generateKeyPair2 = promisify9(forge.pki.rsa.generateKeyPair);
133586
133586
  function forgeObjectFromPem(input) {
133587
133587
  const msg = forge.pem.decode(input)[0];
133588
133588
  let result;
@@ -150808,7 +150808,7 @@ var require_mock_interceptor = __commonJS({
150808
150808
  var require_mock_client = __commonJS({
150809
150809
  "../node_modules/undici/lib/mock/mock-client.js"(exports, module) {
150810
150810
  "use strict";
150811
- var { promisify: promisify8 } = __require("node:util");
150811
+ var { promisify: promisify9 } = __require("node:util");
150812
150812
  var Client2 = require_client2();
150813
150813
  var { buildMockDispatch } = require_mock_utils();
150814
150814
  var {
@@ -150856,7 +150856,7 @@ var require_mock_client = __commonJS({
150856
150856
  this[kDispatches] = [];
150857
150857
  }
150858
150858
  async [kClose]() {
150859
- await promisify8(this[kOriginalClose])();
150859
+ await promisify9(this[kOriginalClose])();
150860
150860
  this[kConnected] = 0;
150861
150861
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
150862
150862
  }
@@ -151069,7 +151069,7 @@ var require_mock_call_history = __commonJS({
151069
151069
  var require_mock_pool = __commonJS({
151070
151070
  "../node_modules/undici/lib/mock/mock-pool.js"(exports, module) {
151071
151071
  "use strict";
151072
- var { promisify: promisify8 } = __require("node:util");
151072
+ var { promisify: promisify9 } = __require("node:util");
151073
151073
  var Pool = require_pool();
151074
151074
  var { buildMockDispatch } = require_mock_utils();
151075
151075
  var {
@@ -151117,7 +151117,7 @@ var require_mock_pool = __commonJS({
151117
151117
  this[kDispatches] = [];
151118
151118
  }
151119
151119
  async [kClose]() {
151120
- await promisify8(this[kOriginalClose])();
151120
+ await promisify9(this[kOriginalClose])();
151121
151121
  this[kConnected] = 0;
151122
151122
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
151123
151123
  }
@@ -557168,8 +557168,8 @@ var init_verifierRunner = __esm({
557168
557168
  if (patch.testsToRun.length === 0)
557169
557169
  return "(no tests specified)";
557170
557170
  const { execFile: execFile9 } = await import("node:child_process");
557171
- const { promisify: promisify8 } = await import("node:util");
557172
- const execFileAsync6 = promisify8(execFile9);
557171
+ const { promisify: promisify9 } = await import("node:util");
557172
+ const execFileAsync6 = promisify9(execFile9);
557173
557173
  const outputs = [];
557174
557174
  const workDir = this.options.workingDir || repoRoot;
557175
557175
  for (const cmd of patch.testsToRun.slice(0, 3)) {
@@ -566182,8 +566182,8 @@ var init_streaming_executor = __esm({
566182
566182
  startExecution(entry) {
566183
566183
  entry.state = "executing";
566184
566184
  entry.startedAt = Date.now();
566185
- const exec6 = this.executeFn;
566186
- entry.promise = exec6(entry.name, entry.args).then((result) => {
566185
+ const exec7 = this.executeFn;
566186
+ entry.promise = exec7(entry.name, entry.args).then((result) => {
566187
566187
  entry.state = "completed";
566188
566188
  entry.result = result;
566189
566189
  entry.completedAt = Date.now();
@@ -567216,10 +567216,13 @@ function adversarySystemPrompt() {
567216
567216
  " • simulation / mock / placeholder ≠ real.",
567217
567217
  " • partial progress ≠ done.",
567218
567218
  "Mixed results are the norm: do NOT let one success excuse an unproven completion claim.",
567219
+ " • a repeating IDENTICAL error while the agent keeps editing the same target = a FAILING",
567220
+ " APPROACH: the change itself is wrong (an unsupported token / wrong API / missing",
567221
+ ' prerequisite), not a value to retry. Class "failing_approach".',
567219
567222
  "",
567220
567223
  "Given the agent's latest message and recent tool outcomes, decide whether the claim is proven.",
567221
567224
  "Respond with ONLY a JSON object, no prose, no code fences:",
567222
- '{"class":"false_success|unproven_claim|weak_evidence|false_failure|repeated_action|ok",',
567225
+ '{"class":"false_success|unproven_claim|weak_evidence|false_failure|repeated_action|failing_approach|ok",',
567223
567226
  ' "shortText":"<=12 word headline",',
567224
567227
  ' "confidence":0.0-1.0, // how strongly the claim is NOT proven',
567225
567228
  ' "details":"2-4 sentence skeptical critique citing the specific gap",',
@@ -567250,6 +567253,26 @@ ${ls2.alreadyHave.slice(0, 900)}` : `(No cached result available for the repeate
567250
567253
  "Return ONLY the JSON object."
567251
567254
  ].join("\n");
567252
567255
  }
567256
+ if (obs.failingApproach) {
567257
+ const fa = obs.failingApproach;
567258
+ const target = fa.churnTarget ? `${fa.churnTarget} (edited ${fa.churnWrites ?? "several"}× recently)` : "the same target";
567259
+ return [
567260
+ `The agent is in a FAILING APPROACH: the SAME error has recurred ${fa.count}× while it keeps changing ${target}.`,
567261
+ `The recurring error:`,
567262
+ ` ${fa.sample.slice(0, 400)}`,
567263
+ "",
567264
+ "A repeating identical error means the CHANGE ITSELF is wrong — an unsupported attribute/option, a wrong API/signature, or a missing prerequisite — NOT a value the agent hasn't guessed yet.",
567265
+ "",
567266
+ "Agent's latest message:",
567267
+ obs.assistantText.slice(0, 900) || "(empty)",
567268
+ "",
567269
+ "Recent tool outcomes:",
567270
+ outcomes || " (none)",
567271
+ "",
567272
+ `Reason about THIS specific error. Identify the EXACT token it rejects (attribute/symbol/flag/path) and the root cause. Return class "failing_approach" with a "demand" that names the ONE authoritative source to verify the correct contract (the defining file / schema / API doc) and the supported alternative to use — or, if the feature is genuinely unsupported, to REMOVE it. If the error is actually transient or unrelated, class "ok".`,
567273
+ "Return ONLY the JSON object."
567274
+ ].join("\n");
567275
+ }
567253
567276
  return [
567254
567277
  obs.claimsCompletion ? "The agent is asserting COMPLETION this turn." : "The agent produced a progress/success-flavored claim this turn.",
567255
567278
  "",
@@ -567286,6 +567309,7 @@ function parseAdversaryCritique(raw) {
567286
567309
  "unproven_claim",
567287
567310
  "weak_evidence",
567288
567311
  "false_failure",
567312
+ "failing_approach",
567289
567313
  "ok"
567290
567314
  ];
567291
567315
  const klass = valid.includes(cls) ? cls : "unproven_claim";
@@ -567337,13 +567361,15 @@ var init_adversaryStream = __esm({
567337
567361
  return true;
567338
567362
  if (obs.loopSignal)
567339
567363
  return true;
567364
+ if (obs.failingApproach)
567365
+ return true;
567340
567366
  return SUCCESS_LANGUAGE.test(obs.assistantText);
567341
567367
  }
567342
567368
  /** Ingest an observation. Replaces any prior un-audited pending observation. */
567343
567369
  observe(obs) {
567344
567370
  if (!this.shouldAudit(obs))
567345
567371
  return;
567346
- const loopKey = obs.loopSignal ? `loop:${obs.loopSignal.tool}:${obs.loopSignal.target}:${obs.loopSignal.count}` : "";
567372
+ const loopKey = obs.loopSignal ? `loop:${obs.loopSignal.tool}:${obs.loopSignal.target}:${obs.loopSignal.count}` : obs.failingApproach ? `fail:${obs.failingApproach.count}:${obs.failingApproach.sample.slice(0, 60)}` : "";
567347
567373
  const sig = `${obs.turn}:${loopKey}:${obs.assistantText.slice(0, 200)}`;
567348
567374
  if (sig === this.lastAuditedSignature)
567349
567375
  return;
@@ -570962,7 +570988,15 @@ Your hypotheses MUST address this specific error, not generic causes.
570962
570988
  * (optionally corroborated by the error-cluster tracker), self-cooldown 8
570963
570989
  * turns. OMNIUS_DISABLE_FAILING_APPROACH=1 disables.
570964
570990
  */
570965
- _detectFailingApproach(turn) {
570991
+ /**
570992
+ * Detect a FAILING APPROACH (same error recurring ≥N× while churning the same
570993
+ * target) and return the STRUCTURED signal. The runtime routes this signal
570994
+ * through the generative AdversaryStream (real per-case inference) rather than
570995
+ * emitting a canned directive; `_failingApproachDirective` builds the templated
570996
+ * fallback used ONLY when the adversary is disabled. Sets the cooldown so a
570997
+ * detection (via either path) does not re-fire immediately.
570998
+ */
570999
+ _detectFailingApproachSignal(turn) {
570966
571000
  if (process.env["OMNIUS_DISABLE_FAILING_APPROACH"] === "1")
570967
571001
  return null;
570968
571002
  if (turn <= this._failingApproachCooldownUntil)
@@ -570970,7 +571004,7 @@ Your hypotheses MUST address this specific error, not generic causes.
570970
571004
  const recurring = this._recurringFailureSignature(turn);
570971
571005
  if (!recurring)
570972
571006
  return null;
570973
- let churnPath = null;
571007
+ let churnPath;
570974
571008
  let churnWrites = 0;
570975
571009
  const wf = this._worldFacts;
570976
571010
  if (wf) {
@@ -570984,11 +571018,25 @@ Your hypotheses MUST address this specific error, not generic causes.
570984
571018
  }
570985
571019
  }
570986
571020
  this._failingApproachCooldownUntil = turn + 8;
570987
- const target = churnPath ? `${churnPath} (edited ${churnWrites}× recently)` : "the same target";
571021
+ return {
571022
+ count: recurring.count,
571023
+ sample: recurring.sample,
571024
+ churnTarget: churnPath,
571025
+ churnWrites: churnWrites || void 0
571026
+ };
571027
+ }
571028
+ /**
571029
+ * Deterministic templated directive built from a failing-approach signal.
571030
+ * Used ONLY as a fallback when the generative adversary is disabled
571031
+ * (disableAdversaryCritic). Normally the signal is routed through real
571032
+ * inference so the wording is reasoned per-case, not canned.
571033
+ */
571034
+ _failingApproachDirective(fa) {
571035
+ const target = fa.churnTarget ? `${fa.churnTarget} (edited ${fa.churnWrites ?? "several"}× recently)` : "the same target";
570988
571036
  return [
570989
571037
  `[FAILING APPROACH DETECTED — stop retrying variants]`,
570990
- `The SAME error has recurred ${recurring.count}× in your recent attempts while you keep changing ${target}:`,
570991
- ` ${recurring.sample}`,
571038
+ `The SAME error has recurred ${fa.count}× in your recent attempts while you keep changing ${target}:`,
571039
+ ` ${fa.sample}`,
570992
571040
  ``,
570993
571041
  `Trying another variant of the same change is not converging. A repeating error means the CHANGE ITSELF is wrong — an unsupported attribute/option, a wrong API or signature, or a missing prerequisite — not that you haven't hit the right value yet.`,
570994
571042
  `MANDATORY before your next edit:`,
@@ -570999,6 +571047,11 @@ Your hypotheses MUST address this specific error, not generic causes.
570999
571047
  `Do NOT re-apply another variant of the rejected change.`
571000
571048
  ].join("\n");
571001
571049
  }
571050
+ /** Back-compat wrapper: structured detection → templated directive. */
571051
+ _detectFailingApproach(turn) {
571052
+ const fa = this._detectFailingApproachSignal(turn);
571053
+ return fa ? this._failingApproachDirective(fa) : null;
571054
+ }
571002
571055
  /**
571003
571056
  * REG-61 sliding-window first-edit / sustained-edit nudge.
571004
571057
  *
@@ -576113,25 +576166,52 @@ TASK: ${scrubbedTask}` : scrubbedTask;
576113
576166
  }
576114
576167
  this._runReg61Check(turn, toolCallLog, messages2);
576115
576168
  if (turn > stagnationCooldownUntilTurn) {
576116
- const failingApproach = this._detectFailingApproach(turn);
576117
- if (failingApproach) {
576118
- messages2.push({ role: "system", content: failingApproach });
576169
+ const faSignal = this._detectFailingApproachSignal(turn);
576170
+ if (faSignal) {
576119
576171
  stagnationCooldownUntilTurn = turn + 4;
576120
- this.emit({
576121
- type: "adversary_reaction",
576122
- adversary: {
576123
- class: "guidance",
576124
- shortText: "Failing approach — same error recurring; diagnose root cause",
576125
- confidence: 0.9,
576126
- details: failingApproach
576127
- },
576128
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
576129
- });
576130
- this.emit({
576131
- type: "status",
576132
- content: `FAILING-APPROACH detected at turn ${turn} — injected root-cause directive`,
576133
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
576134
- });
576172
+ if (this._adversaryStream) {
576173
+ this._adversaryStream.observe({
576174
+ turn,
576175
+ assistantText: "",
576176
+ recentToolOutcomes: this._adversaryToolOutcomes.slice(-8).map((o2) => ({
576177
+ tool: o2.tool,
576178
+ succeeded: o2.succeeded,
576179
+ preview: o2.preview
576180
+ })),
576181
+ claimsCompletion: false,
576182
+ failingApproach: {
576183
+ count: faSignal.count,
576184
+ sample: faSignal.sample,
576185
+ churnTarget: faSignal.churnTarget,
576186
+ churnWrites: faSignal.churnWrites
576187
+ }
576188
+ });
576189
+ void this._adversaryStream.tick().catch(() => {
576190
+ });
576191
+ this.emit({
576192
+ type: "status",
576193
+ content: `FAILING-APPROACH detected at turn ${turn} — routed to adversary inference`,
576194
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
576195
+ });
576196
+ } else {
576197
+ const directive = this._failingApproachDirective(faSignal);
576198
+ messages2.push({ role: "system", content: directive });
576199
+ this.emit({
576200
+ type: "adversary_reaction",
576201
+ adversary: {
576202
+ class: "guidance",
576203
+ shortText: "Failing approach — same error recurring; diagnose root cause",
576204
+ confidence: 0.9,
576205
+ details: directive
576206
+ },
576207
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
576208
+ });
576209
+ this.emit({
576210
+ type: "status",
576211
+ content: `FAILING-APPROACH detected at turn ${turn} — injected root-cause directive (deterministic fallback; adversary disabled)`,
576212
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
576213
+ });
576214
+ }
576135
576215
  }
576136
576216
  }
576137
576217
  const REG58_NO_WRITE_BUDGET = 30;
@@ -647430,9 +647510,9 @@ async function handleUpdate(subcommand, ctx3) {
647430
647510
  }
647431
647511
  };
647432
647512
  }
647433
- const { exec: exec6, spawn: spawn35, execSync: es2 } = await import("node:child_process");
647513
+ const { exec: exec7, spawn: spawn35, execSync: es2 } = await import("node:child_process");
647434
647514
  const execA = (cmd, opts) => new Promise(
647435
- (res, rej) => exec6(
647515
+ (res, rej) => exec7(
647436
647516
  cmd,
647437
647517
  {
647438
647518
  encoding: "utf8",
@@ -648179,7 +648259,7 @@ async function handleUpdate(subcommand, ctx3) {
648179
648259
  installOverlay.setPhase("Native Modules");
648180
648260
  installOverlay.setStatus("Rebuilding native modules...");
648181
648261
  await new Promise((resolve71) => {
648182
- const child = exec6(
648262
+ const child = exec7(
648183
648263
  `${sudoPrefix}npm rebuild -g omnius 2>/dev/null || true`,
648184
648264
  { timeout: 12e4 },
648185
648265
  () => resolve71(true)
@@ -648243,7 +648323,7 @@ async function handleUpdate(subcommand, ctx3) {
648243
648323
  if (fsExists(venvPip2)) {
648244
648324
  installOverlay.setStatus("Upgrading Python packages...");
648245
648325
  await new Promise((resolve71) => {
648246
- const child = exec6(
648326
+ const child = exec7(
648247
648327
  `"${venvPip2}" install --upgrade moondream-station pytesseract Pillow opencv-python-headless numpy 2>/dev/null || true`,
648248
648328
  { timeout: 3e5 },
648249
648329
  (err) => resolve71(!err)
@@ -656103,7 +656183,9 @@ var init_bless_engine = __esm({
656103
656183
  // packages/cli/src/tui/dmn-engine.ts
656104
656184
  import { existsSync as existsSync142, readFileSync as readFileSync115, writeFileSync as writeFileSync74, mkdirSync as mkdirSync86, readdirSync as readdirSync51, unlinkSync as unlinkSync29 } from "node:fs";
656105
656185
  import { join as join154, basename as basename33 } from "node:path";
656106
- function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
656186
+ import { exec as exec6 } from "node:child_process";
656187
+ import { promisify as promisify8 } from "node:util";
656188
+ function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer, projectOpportunities = []) {
656107
656189
  const competenceReport = competence.length > 0 ? competence.map((c8) => {
656108
656190
  const rate = c8.attempts > 0 ? Math.round(c8.successes / c8.attempts * 100) : 0;
656109
656191
  return ` - ${c8.taskType}: ${c8.attempts} attempts, ${rate}% success`;
@@ -656117,11 +656199,16 @@ function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems,
656117
656199
  dueReminders: dueReminders.length > 0 ? dueReminders.map((r2) => ` - ${r2}`).join("\n") : " (none)",
656118
656200
  attentionItems: attentionItems.length > 0 ? attentionItems.map((a2) => ` - ${a2}`).join("\n") : " (none)",
656119
656201
  memoryTopics: memoryTopics.length > 0 ? memoryTopics.map((t2) => ` - ${t2}`).join("\n") : " (none — fresh start)",
656120
- capabilities: capabilities.map((c8) => ` - ${c8}`).join("\n")
656202
+ capabilities: capabilities.map((c8) => ` - ${c8}`).join("\n"),
656203
+ projectOpportunities: projectOpportunities.length > 0 ? projectOpportunities.map((o2) => ` - ${o2}`).join("\n") : " (none detected this scan)"
656121
656204
  });
656122
656205
  } catch {
656206
+ const oppText = projectOpportunities.length > 0 ? `
656207
+
656208
+ Concrete project opportunities (prefer a small, testable capability task):
656209
+ ${projectOpportunities.map((o2) => ` - ${o2}`).join("\n")}` : "";
656123
656210
  return `Gather observations about the agent's recent work:
656124
- ${competenceReport}
656211
+ ${competenceReport}${oppText}
656125
656212
 
656126
656213
  Reflect on what went well and what could improve.`;
656127
656214
  }
@@ -656185,7 +656272,7 @@ function renderDMNResting(consecutiveNulls) {
656185
656272
  process.stdout.write(` ${c3.dim("◌")} DMN resting — ${consecutiveNulls} cycles with nothing to do. Waiting for stimulus...
656186
656273
  `);
656187
656274
  }
656188
- var DMNEngine;
656275
+ var execAsync3, DMNEngine;
656189
656276
  var init_dmn_engine = __esm({
656190
656277
  "packages/cli/src/tui/dmn-engine.ts"() {
656191
656278
  "use strict";
@@ -656196,6 +656283,7 @@ var init_dmn_engine = __esm({
656196
656283
  init_render();
656197
656284
  init_promptLoader3();
656198
656285
  init_tool_adapter();
656286
+ execAsync3 = promisify8(exec6);
656199
656287
  DMNEngine = class {
656200
656288
  constructor(config, repoRoot) {
656201
656289
  this.config = config;
@@ -656213,12 +656301,17 @@ var init_dmn_engine = __esm({
656213
656301
  tasksGenerated: 0,
656214
656302
  consecutiveNulls: 0,
656215
656303
  competence: [],
656216
- reflectionBuffer: []
656304
+ reflectionBuffer: [],
656305
+ recentProposalFingerprints: [],
656306
+ tasksGeneratedToday: 0,
656307
+ dayStamp: ""
656217
656308
  };
656218
656309
  stateDir;
656219
656310
  historyDir;
656220
656311
  /** Recent task summaries — appended by the bless engine after each task */
656221
656312
  recentTaskSummaries = [];
656313
+ /** A: the proposal returned by the most recent runCycle, awaiting its outcome. */
656314
+ _lastProposal = null;
656222
656315
  /** Maximum consecutive null cycles before entering extended rest */
656223
656316
  maxConsecutiveNulls = 3;
656224
656317
  /** TUI writeContent callback — wraps stdout writes in scroll buffer management.
@@ -656246,9 +656339,11 @@ var init_dmn_engine = __esm({
656246
656339
  if (this.recentTaskSummaries.length > 10) {
656247
656340
  this.recentTaskSummaries.shift();
656248
656341
  }
656249
- if (category) {
656250
- this.updateCompetence(category, true);
656342
+ const cat2 = category ?? this._lastProposal?.category;
656343
+ if (cat2) {
656344
+ this.updateCompetence(cat2, true);
656251
656345
  }
656346
+ this._lastProposal = null;
656252
656347
  this.saveState();
656253
656348
  }
656254
656349
  /** Record a task failure for Reflexion-style learning */
@@ -656260,9 +656355,11 @@ var init_dmn_engine = __esm({
656260
656355
  if (this.state.reflectionBuffer.length > 5) {
656261
656356
  this.state.reflectionBuffer.shift();
656262
656357
  }
656263
- if (category) {
656264
- this.updateCompetence(category, false);
656358
+ const cat2 = category ?? this._lastProposal?.category;
656359
+ if (cat2) {
656360
+ this.updateCompetence(cat2, false);
656265
656361
  }
656362
+ this._lastProposal = null;
656266
656363
  this.saveState();
656267
656364
  }
656268
656365
  /** Update competence tracker for a task category */
@@ -656293,10 +656390,11 @@ var init_dmn_engine = __esm({
656293
656390
  this.state.totalCycles++;
656294
656391
  const cycleNum = this.state.totalCycles;
656295
656392
  const startMs = Date.now();
656296
- const [reminders, attention, memoryTopics] = await Promise.all([
656393
+ const [reminders, attention, memoryTopics, projectOpportunities] = await Promise.all([
656297
656394
  this.gatherReminders(),
656298
656395
  this.gatherAttentionItems(),
656299
- this.gatherMemoryTopics()
656396
+ this.gatherMemoryTopics(),
656397
+ this.gatherProjectOpportunities()
656300
656398
  ]);
656301
656399
  const capabilities = [
656302
656400
  "file_read — read files",
@@ -656313,13 +656411,15 @@ var init_dmn_engine = __esm({
656313
656411
  memoryTopics,
656314
656412
  capabilities,
656315
656413
  this.state.competence,
656316
- this.state.reflectionBuffer
656414
+ this.state.reflectionBuffer,
656415
+ projectOpportunities
656317
656416
  );
656318
656417
  const modelTier2 = getModelTier(this.config.model);
656319
656418
  this.write(() => renderDMNCycleStart(cycleNum, modelTier2 === "large"));
656320
656419
  const useDeliberation = modelTier2 === "large";
656321
656420
  let result;
656322
656421
  let deliberationMeta = null;
656422
+ let deliberationFallbackProposals = [];
656323
656423
  if (useDeliberation) {
656324
656424
  onEvent?.({
656325
656425
  type: "status",
@@ -656328,6 +656428,7 @@ var init_dmn_engine = __esm({
656328
656428
  });
656329
656429
  const delib = await this.runDeliberationCycle(prompt, memoryTopics, onEvent);
656330
656430
  result = { summary: delib.summary };
656431
+ deliberationFallbackProposals = delib.workspace.proposals ?? [];
656331
656432
  deliberationMeta = {
656332
656433
  rounds: delib.workspace.rounds,
656333
656434
  evidence: delib.workspace.evidenceAccumulated,
@@ -656338,7 +656439,40 @@ var init_dmn_engine = __esm({
656338
656439
  result = { summary: mono.summary };
656339
656440
  }
656340
656441
  const durationMs = Date.now() - startMs;
656341
- const proposal = this.parseProposal(result.summary);
656442
+ let proposal = this.parseProposal(result.summary, deliberationFallbackProposals);
656443
+ const today2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
656444
+ if (this.state.dayStamp !== today2) {
656445
+ this.state.dayStamp = today2;
656446
+ this.state.tasksGeneratedToday = 0;
656447
+ }
656448
+ const dailyMax = (() => {
656449
+ const raw = Number(process.env["OMNIUS_DMN_DAILY_TASK_MAX"]);
656450
+ return Number.isFinite(raw) && raw >= 0 ? Math.floor(raw) : 12;
656451
+ })();
656452
+ if (proposal) {
656453
+ const fp = this.fingerprintProposal(proposal);
656454
+ const recent = this.state.recentProposalFingerprints ?? [];
656455
+ if (recent.includes(fp)) {
656456
+ onEvent?.({
656457
+ type: "status",
656458
+ content: "DMN: proposal already proposed recently — resting (dedup)",
656459
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
656460
+ });
656461
+ proposal = null;
656462
+ } else if ((this.state.tasksGeneratedToday ?? 0) >= dailyMax) {
656463
+ onEvent?.({
656464
+ type: "status",
656465
+ content: `DMN: daily task budget reached (${dailyMax}) — resting`,
656466
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
656467
+ });
656468
+ proposal = null;
656469
+ } else {
656470
+ recent.push(fp);
656471
+ if (recent.length > 20) recent.shift();
656472
+ this.state.recentProposalFingerprints = recent;
656473
+ this.state.tasksGeneratedToday = (this.state.tasksGeneratedToday ?? 0) + 1;
656474
+ }
656475
+ }
656342
656476
  const cycleResult = {
656343
656477
  cycleNumber: cycleNum,
656344
656478
  startedAt: new Date(startMs).toISOString(),
@@ -656362,6 +656496,7 @@ var init_dmn_engine = __esm({
656362
656496
  }
656363
656497
  this.state.lastCycleAt = (/* @__PURE__ */ new Date()).toISOString();
656364
656498
  this.saveState();
656499
+ this._lastProposal = proposal;
656365
656500
  return proposal;
656366
656501
  }
656367
656502
  /** Run the DMN's internal LLM agent */
@@ -656625,14 +656760,15 @@ If the Amygdala flagged high urgency (due reminders, critical attention items),
656625
656760
  LOWER the threshold to 0.5 — urgent situations demand faster action even with
656626
656761
  less certainty (urgency-gating model, Cisek et al. 2009).
656627
656762
 
656628
- OUTPUT: Call task_complete with JSON:
656763
+ OUTPUT: Call task_complete with JSON (the JSON object MUST be the FINAL thing you output):
656629
656764
  {
656630
656765
  "decision": "GO" | "NOGO",
656631
656766
  "evidenceScore": 0.X,
656632
656767
  "selectedTask": { task, rationale, provenance, category, confidence, challengeResult } | null,
656633
656768
  "reasoning": "how evidence from each region contributed to the decision",
656634
656769
  "innerVoice": "final self-talk summary — what the agent 'says to itself' before acting"
656635
- }`, onEvent);
656770
+ }
656771
+ If decision is GO, selectedTask MUST be a fully-populated object (NEVER null) — copy the chosen proposal verbatim. A GO with selectedTask:null is invalid.`, onEvent);
656636
656772
  if (gateResult) {
656637
656773
  workspace.findings.push({
656638
656774
  from: "basal_ganglia",
@@ -656782,39 +656918,108 @@ OUTPUT: Call task_complete with JSON:
656782
656918
  };
656783
656919
  }
656784
656920
  /** Parse the DMN agent's output into a structured proposal */
656785
- parseProposal(summary) {
656786
- try {
656787
- const jsonMatch = summary.match(/\{[\s\S]*"selectedTask"[\s\S]*\}/);
656788
- if (!jsonMatch) return null;
656789
- const parsed = JSON.parse(jsonMatch[0]);
656790
- if (!parsed.selectedTask || parsed.selectedTask === null) return null;
656791
- const t2 = parsed.selectedTask;
656792
- return {
656793
- task: String(t2.task ?? ""),
656794
- rationale: String(t2.rationale ?? ""),
656795
- provenance: Array.isArray(t2.provenance) ? t2.provenance.map(String) : [],
656796
- category: ["directive", "exploration", "capability", "maintenance", "social", "autoresearch"].includes(t2.category) ? t2.category : "exploration",
656797
- confidence: typeof t2.confidence === "number" ? Math.min(1, Math.max(0, t2.confidence)) : 0.5,
656798
- challengeResult: t2.challengeResult ? String(t2.challengeResult) : void 0
656799
- };
656921
+ /**
656922
+ * B: layered, GO-aware, markdown-tolerant proposal extraction.
656923
+ *
656924
+ * The basal-ganglia gate emits `{decision, selectedTask, reasoning}` — but in
656925
+ * production it has emitted a GO verdict with `selectedTask:null` or in
656926
+ * markdown ("VERDICT: GO Execute <name>"), which the old single-regex parser
656927
+ * silently dropped to null (wasted deliberation). This recovers the GO target
656928
+ * from the named task or the workspace proposals so a GO is never lost, while
656929
+ * still resting on NOGO / NO_TASK.
656930
+ */
656931
+ parseProposal(summary, fallbackProposals = []) {
656932
+ const coerce2 = (t2) => ({
656933
+ task: String(t2["task"] ?? ""),
656934
+ rationale: String(t2["rationale"] ?? ""),
656935
+ provenance: Array.isArray(t2["provenance"]) ? t2["provenance"].map(String) : [],
656936
+ category: ["directive", "exploration", "capability", "maintenance", "social", "autoresearch"].includes(t2["category"]) ? t2["category"] : "exploration",
656937
+ confidence: typeof t2["confidence"] === "number" ? Math.min(1, Math.max(0, t2["confidence"])) : 0.5,
656938
+ challengeResult: t2["challengeResult"] ? String(t2["challengeResult"]) : void 0
656939
+ });
656940
+ try {
656941
+ const start2 = summary.indexOf("{");
656942
+ const end = summary.lastIndexOf("}");
656943
+ if (start2 >= 0 && end > start2) {
656944
+ const parsed = JSON.parse(summary.slice(start2, end + 1));
656945
+ const decision2 = String(parsed["decision"] ?? "").toUpperCase();
656946
+ const t2 = parsed["selectedTask"];
656947
+ if (decision2 === "NOGO") return null;
656948
+ if (t2 && typeof t2 === "object" && String(t2["task"] ?? "").trim()) return coerce2(t2);
656949
+ }
656800
656950
  } catch {
656801
- if (summary.includes("NO_TASK") || summary.toLowerCase().includes("no task")) {
656802
- return null;
656951
+ }
656952
+ if (/\bNO_?TASK\b/i.test(summary) || /\bNOGO\b/i.test(summary)) return null;
656953
+ const go = /\bVERDICT\b[^\n]*\bGO\b/i.test(summary) || /\bdecision\b[^\n]*\bGO\b/i.test(summary);
656954
+ if (go) {
656955
+ const m2 = summary.match(/Execute[:\s]+[`"']?([A-Za-z0-9_./ -]{3,80})[`"']?/i);
656956
+ const named = m2?.[1]?.trim();
656957
+ if (named) {
656958
+ const lc = named.toLowerCase();
656959
+ const fromWs = fallbackProposals.find(
656960
+ (p2) => p2.task.toLowerCase().includes(lc) || lc.includes(p2.task.toLowerCase().slice(0, 24))
656961
+ );
656962
+ if (fromWs) return fromWs;
656963
+ return coerce2({
656964
+ task: named,
656965
+ rationale: "Recovered from GO verdict (markdown).",
656966
+ provenance: ["dmn:go-verdict-recovery"],
656967
+ category: fallbackProposals[0]?.category ?? "exploration",
656968
+ confidence: 0.55
656969
+ });
656803
656970
  }
656804
- const lines = summary.split("\n").filter((l2) => l2.trim().length > 20);
656805
- if (lines.length > 0) {
656806
- return {
656807
- task: lines[0].trim().slice(0, 500),
656808
- rationale: "Extracted from DMN natural language output",
656809
- provenance: ["dmn:natural-language-extraction"],
656810
- category: "exploration",
656811
- confidence: 0.3
656812
- };
656971
+ if (fallbackProposals.length > 0) {
656972
+ return [...fallbackProposals].sort((a2, b) => b.confidence - a2.confidence)[0];
656813
656973
  }
656814
- return null;
656815
656974
  }
656975
+ return null;
656816
656976
  }
656817
656977
  // ── Context gathering helpers ──────────────────────────────────────────
656978
+ /** Bounded read-only shell — never freezes a cycle (4s timeout, 1MB cap). */
656979
+ async runBounded(cmd) {
656980
+ try {
656981
+ const { stdout } = await execAsync3(cmd, {
656982
+ cwd: this.repoRoot,
656983
+ timeout: 4e3,
656984
+ maxBuffer: 1 << 20
656985
+ });
656986
+ return stdout ?? "";
656987
+ } catch {
656988
+ return "";
656989
+ }
656990
+ }
656991
+ /**
656992
+ * C: scan the actual repo for concrete development opportunities so the DMN
656993
+ * proposes grounded capability work (implement a plan, fix a TODO, harden a
656994
+ * recently-changed module) rather than abstract infra. All signals are cheap
656995
+ * and bounded — never runs a heavy test suite here.
656996
+ */
656997
+ async gatherProjectOpportunities() {
656998
+ const out = [];
656999
+ const root = this.repoRoot;
657000
+ try {
657001
+ const plansDir = join154(root, ".aiwg", "plans");
657002
+ if (existsSync142(plansDir)) {
657003
+ for (const f2 of readdirSync51(plansDir).filter((p2) => p2.endsWith(".md")).slice(0, 8)) {
657004
+ out.push(`Plan backlog: implement .aiwg/plans/${f2}`);
657005
+ }
657006
+ }
657007
+ const todos = await this.runBounded(
657008
+ `rg -n --no-heading -e "TODO|FIXME" -g "*.ts" -g "*.py" packages src 2>/dev/null | head -12`
657009
+ );
657010
+ for (const line of todos.split("\n").filter(Boolean).slice(0, 8)) {
657011
+ out.push(`Code gap: ${line.slice(0, 160)}`);
657012
+ }
657013
+ const churn = await this.runBounded(
657014
+ `git diff --name-only HEAD~5 2>/dev/null | head -10`
657015
+ );
657016
+ for (const f2 of churn.split("\n").filter(Boolean).slice(0, 6)) {
657017
+ out.push(`Recently changed (consider hardening/tests): ${f2}`);
657018
+ }
657019
+ } catch {
657020
+ }
657021
+ return [...new Set(out)].slice(0, 14);
657022
+ }
656818
657023
  async gatherReminders() {
656819
657024
  try {
656820
657025
  const reminders = await getDueReminders(this.repoRoot);
@@ -656917,6 +657122,16 @@ OUTPUT: Call task_complete with JSON:
656917
657122
  }
656918
657123
  }
656919
657124
  }
657125
+ /** F1: stable fingerprint of a proposal for dedup (category + task + rationale). */
657126
+ fingerprintProposal(p2) {
657127
+ const normalized = cleanForStorage(`${p2.category} ${p2.task} ${p2.rationale}`).toLowerCase().replace(/\d+/g, "#").replace(/\s+/g, " ").trim().slice(0, 600);
657128
+ let hash = 2166136261;
657129
+ for (let i2 = 0; i2 < normalized.length; i2++) {
657130
+ hash ^= normalized.charCodeAt(i2);
657131
+ hash = Math.imul(hash, 16777619);
657132
+ }
657133
+ return (hash >>> 0).toString(16);
657134
+ }
656920
657135
  fingerprintCycle(result) {
656921
657136
  const selected = result.selectedTask ? [
656922
657137
  result.selectedTask.category,
@@ -712089,10 +712304,10 @@ ${incompleteList}${more}
712089
712304
  const scripts = pkg.scripts || {};
712090
712305
  const checkScript = scripts["typecheck"] ? "typecheck" : scripts["build"] ? "build" : null;
712091
712306
  if (checkScript) {
712092
- const { exec: exec6 } = await import("node:child_process");
712093
- const { promisify: promisify8 } = await import("node:util");
712307
+ const { exec: exec7 } = await import("node:child_process");
712308
+ const { promisify: promisify9 } = await import("node:util");
712094
712309
  try {
712095
- await promisify8(exec6)(`npm run ${checkScript} --silent 2>&1`, {
712310
+ await promisify9(exec7)(`npm run ${checkScript} --silent 2>&1`, {
712096
712311
  cwd: cwd4,
712097
712312
  timeout: 12e4,
712098
712313
  encoding: "utf-8",
@@ -713294,6 +713509,10 @@ Meta-critique: quality ${meta.quality}/5, thorough: ${meta.thorough}`;
713294
713509
  }
713295
713510
  };
713296
713511
  }
713512
+ function dmnDevDiscipline(category) {
713513
+ if (category !== "capability") return "";
713514
+ return "\n\nYou are acting as a self-directed developer. Work on a NEW git branch. Implement the change, then make the project's tests pass for what you touched. Do NOT call task_complete until the change is implemented AND its tests pass AND the result is evidenced (the completion gates will block an unverified claim). If the change is not feasible, stop with a BLOCKED: summary explaining why.";
713515
+ }
713297
713516
  function gatherMemorySnippets(root) {
713298
713517
  const snippets = [];
713299
713518
  const dirs = [
@@ -713402,36 +713621,28 @@ function buildNewTaskIntakePacket(ingress, interpretation, previousPrompt, previ
713402
713621
  function createDMNEventHandler(verbose, writeContent) {
713403
713622
  return (event) => {
713404
713623
  switch (event.type) {
713624
+ // DMN tool activity renders in the SAME unicode boxes + format as regular
713625
+ // (main-loop) tool calls for consistent observability — the verbose flag
713626
+ // controls detail exactly as in the main loop, not whether the box shows.
713627
+ // The DMN remains automatic/self-directed; only its rendering is unified.
713405
713628
  case "tool_call":
713406
- if (verbose) {
713407
- writeContent(() => {
713408
- const args = event.toolArgs ?? {};
713409
- renderToolCallStart(event.toolName ?? "unknown", args, true);
713410
- });
713411
- } else {
713412
- const argSummary = formatDMNToolArgs(
713413
- event.toolName ?? "",
713414
- event.toolArgs ?? {}
713415
- );
713416
- writeContent(
713417
- () => process.stdout.write(
713418
- ` ${c3.dim(`DMN → ${event.toolName}`)}${argSummary ? c3.dim(`: ${argSummary}`) : ""}
713419
- `
713420
- )
713629
+ writeContent(() => {
713630
+ renderToolCallStart(
713631
+ event.toolName ?? "unknown",
713632
+ event.toolArgs ?? {},
713633
+ verbose
713421
713634
  );
713422
- }
713635
+ });
713423
713636
  break;
713424
713637
  case "tool_result":
713425
- if (verbose) {
713426
- writeContent(() => {
713427
- renderToolResult(
713428
- event.toolName ?? "unknown",
713429
- event.success ?? false,
713430
- event.content ?? "",
713431
- true
713432
- );
713433
- });
713434
- }
713638
+ writeContent(() => {
713639
+ renderToolResult(
713640
+ event.toolName ?? "unknown",
713641
+ event.success ?? false,
713642
+ event.content ?? "",
713643
+ verbose
713644
+ );
713645
+ });
713435
713646
  break;
713436
713647
  case "model_response":
713437
713648
  if (verbose && event.content) {
@@ -713468,27 +713679,6 @@ ${event.content}`
713468
713679
  }
713469
713680
  };
713470
713681
  }
713471
- function formatDMNToolArgs(toolName, args) {
713472
- switch (toolName) {
713473
- case "memory_read":
713474
- case "memory_search":
713475
- return String(args["topic"] ?? args["query"] ?? "").slice(0, 60);
713476
- case "file_read":
713477
- return String(args["path"] ?? "").slice(0, 80);
713478
- case "list_directory":
713479
- return String(args["path"] ?? ".").slice(0, 80);
713480
- case "shell":
713481
- return String(args["command"] ?? "").slice(0, 80);
713482
- case "memory_write":
713483
- return `${args["topic"] ?? ""}.${args["key"] ?? ""}`;
713484
- case "grep_search":
713485
- return String(args["pattern"] ?? "").slice(0, 60);
713486
- case "find_files":
713487
- return String(args["pattern"] ?? "").slice(0, 60);
713488
- default:
713489
- return "";
713490
- }
713491
- }
713492
713682
  function truncateByLines(text2, maxLines, maxChars = 600) {
713493
713683
  const lines = text2.split("\n");
713494
713684
  if (lines.length <= maxLines && text2.length <= maxChars) return text2;
@@ -715419,7 +715609,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
715419
715609
  turns: result.turns,
715420
715610
  toolCalls: result.toolCalls,
715421
715611
  durationMs: result.durationMs,
715422
- model: config.model
715612
+ model: config.model,
715613
+ verified: result.completed === true
715423
715614
  });
715424
715615
  _tasksSinceImprove++;
715425
715616
  if (_tasksSinceImprove >= SELF_IMPROVE_INTERVAL) {
@@ -716680,6 +716871,7 @@ ${result.summary}`
716680
716871
  let dreamEngine = null;
716681
716872
  let blessEngine = null;
716682
716873
  let dmnEngine = null;
716874
+ let dmnOriginatedTask = false;
716683
716875
  const snrEngine = new SNREngine(currentConfig, repoRoot);
716684
716876
  const emotionEngine = new EmotionEngine({
716685
716877
  backendUrl: currentConfig.backendUrl,
@@ -716773,7 +716965,8 @@ Respond concisely and safely.`;
716773
716965
 
716774
716966
  ${proposal.task}
716775
716967
 
716776
- Rationale: ${proposal.rationale}${provenanceNote}`;
716968
+ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.category)}`;
716969
+ dmnOriginatedTask = true;
716777
716970
  writeContent(
716778
716971
  () => renderInfo(
716779
716972
  `DMN auto-cycle: ${proposal.category} task (${Math.round(proposal.confidence * 100)}% confidence)`
@@ -721327,8 +721520,8 @@ ${taskInput}`;
721327
721520
  const updateInfo = await checkForUpdate(version4);
721328
721521
  if (updateInfo) {
721329
721522
  _autoUpdatedThisSession = true;
721330
- const { exec: exec6 } = await import("node:child_process");
721331
- exec6(
721523
+ const { exec: exec7 } = await import("node:child_process");
721524
+ exec7(
721332
721525
  `npm install -g omnius@latest --prefer-online`,
721333
721526
  { timeout: 18e4 },
721334
721527
  (err) => {
@@ -721385,7 +721578,18 @@ ${emotion.emoji} ${emotion.label}`);
721385
721578
  }
721386
721579
  if (blessEngine?.isActive) {
721387
721580
  blessEngine.recordTaskComplete();
721388
- if (dmnEngine && lastCompletedSummary) {
721581
+ if (dmnEngine && dmnOriginatedTask && lastCompletedSummary) {
721582
+ const verified = lastTaskMeta?.verified === true;
721583
+ if (verified) {
721584
+ dmnEngine.recordTaskCompletion(lastCompletedSummary);
721585
+ } else {
721586
+ dmnEngine.recordTaskFailure(
721587
+ lastCompletedSummary,
721588
+ "run did not pass the completion/resolution gates (unverified)"
721589
+ );
721590
+ }
721591
+ dmnOriginatedTask = false;
721592
+ } else if (dmnEngine && lastCompletedSummary) {
721389
721593
  dmnEngine.recordTaskCompletion(lastCompletedSummary);
721390
721594
  }
721391
721595
  const nextTask = await blessEngine.getNextTask();
@@ -721430,7 +721634,8 @@ Respond concisely and safely.`;
721430
721634
 
721431
721635
  ${proposal.task}
721432
721636
 
721433
- Rationale: ${proposal.rationale}${provenanceNote}`;
721637
+ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.category)}`;
721638
+ dmnOriginatedTask = true;
721434
721639
  writeContent(
721435
721640
  () => renderInfo(
721436
721641
  `DMN auto-cycle: ${proposal.category} task (${Math.round(proposal.confidence * 100)}% confidence)`
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.355",
3
+ "version": "1.0.357",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.355",
9
+ "version": "1.0.357",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
@@ -5354,9 +5354,9 @@
5354
5354
  }
5355
5355
  },
5356
5356
  "node_modules/node-addon-api": {
5357
- "version": "8.8.0",
5358
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.8.0.tgz",
5359
- "integrity": "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA==",
5357
+ "version": "8.9.0",
5358
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.0.tgz",
5359
+ "integrity": "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==",
5360
5360
  "license": "MIT",
5361
5361
  "optional": true,
5362
5362
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.355",
3
+ "version": "1.0.357",
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",
@@ -45,6 +45,9 @@ Known memory topics:
45
45
  Available capabilities:
46
46
  {{capabilities}}
47
47
 
48
+ Concrete project opportunities (grounded in THIS repo — open plans, TODO/FIXME, recently-changed files):
49
+ {{projectOpportunities}}
50
+
48
51
  ═══════════════════════════════════════════════════════════════════════════
49
52
 
50
53
  PHASE 2: REFLECT, CONSOLIDATE & REASON (Generative Agents + Reflexion)
@@ -75,11 +78,23 @@ propose tasks at the FRONTIER of current capabilities — neither too easy
75
78
  (already mastered) nor too hard (no chance of success). Check the competence
76
79
  tracker to calibrate difficulty.
77
80
 
81
+ PREFER DEVELOPMENT. You are a self-directed developer of THIS project. When
82
+ concrete project opportunities exist above, PREFER a `capability` task that
83
+ implements or hardens one of them (a small, shippable, testable improvement to
84
+ this codebase) over abstract exploration/infra. Quote the specific
85
+ file/plan/TODO in `provenance`.
86
+
87
+ Curriculum progression (Voyager Goldilocks, driven by the competence tracker):
88
+ - If a category's success rate is HIGH (≥70% over ≥3 attempts), propose a
89
+ LARGER / harder task in it — escalate scope.
90
+ - If LOW (<40%), pick a SMALLER-scoped task or a different category — back off.
91
+ - NEVER repeat a task that recently failed (see the Reflexion buffer above).
92
+
78
93
  For each candidate, specify:
79
94
  - The task description (specific, actionable, measurable)
80
95
  - Rationale (why this task, what led you to it)
81
96
  - Provenance (which memories, directives, or signals informed this)
82
- - Category: directive | exploration | capability | maintenance | social
97
+ - Category: directive | exploration | capability | maintenance | social | autoresearch
83
98
  - Confidence (0-1, calibrated against competence data)
84
99
 
85
100
  ═══════════════════════════════════════════════════════════════════════════