omnius 1.0.356 → 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();
@@ -647510,9 +647510,9 @@ async function handleUpdate(subcommand, ctx3) {
647510
647510
  }
647511
647511
  };
647512
647512
  }
647513
- 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");
647514
647514
  const execA = (cmd, opts) => new Promise(
647515
- (res, rej) => exec6(
647515
+ (res, rej) => exec7(
647516
647516
  cmd,
647517
647517
  {
647518
647518
  encoding: "utf8",
@@ -648259,7 +648259,7 @@ async function handleUpdate(subcommand, ctx3) {
648259
648259
  installOverlay.setPhase("Native Modules");
648260
648260
  installOverlay.setStatus("Rebuilding native modules...");
648261
648261
  await new Promise((resolve71) => {
648262
- const child = exec6(
648262
+ const child = exec7(
648263
648263
  `${sudoPrefix}npm rebuild -g omnius 2>/dev/null || true`,
648264
648264
  { timeout: 12e4 },
648265
648265
  () => resolve71(true)
@@ -648323,7 +648323,7 @@ async function handleUpdate(subcommand, ctx3) {
648323
648323
  if (fsExists(venvPip2)) {
648324
648324
  installOverlay.setStatus("Upgrading Python packages...");
648325
648325
  await new Promise((resolve71) => {
648326
- const child = exec6(
648326
+ const child = exec7(
648327
648327
  `"${venvPip2}" install --upgrade moondream-station pytesseract Pillow opencv-python-headless numpy 2>/dev/null || true`,
648328
648328
  { timeout: 3e5 },
648329
648329
  (err) => resolve71(!err)
@@ -656183,7 +656183,9 @@ var init_bless_engine = __esm({
656183
656183
  // packages/cli/src/tui/dmn-engine.ts
656184
656184
  import { existsSync as existsSync142, readFileSync as readFileSync115, writeFileSync as writeFileSync74, mkdirSync as mkdirSync86, readdirSync as readdirSync51, unlinkSync as unlinkSync29 } from "node:fs";
656185
656185
  import { join as join154, basename as basename33 } from "node:path";
656186
- 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 = []) {
656187
656189
  const competenceReport = competence.length > 0 ? competence.map((c8) => {
656188
656190
  const rate = c8.attempts > 0 ? Math.round(c8.successes / c8.attempts * 100) : 0;
656189
656191
  return ` - ${c8.taskType}: ${c8.attempts} attempts, ${rate}% success`;
@@ -656197,11 +656199,16 @@ function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems,
656197
656199
  dueReminders: dueReminders.length > 0 ? dueReminders.map((r2) => ` - ${r2}`).join("\n") : " (none)",
656198
656200
  attentionItems: attentionItems.length > 0 ? attentionItems.map((a2) => ` - ${a2}`).join("\n") : " (none)",
656199
656201
  memoryTopics: memoryTopics.length > 0 ? memoryTopics.map((t2) => ` - ${t2}`).join("\n") : " (none — fresh start)",
656200
- 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)"
656201
656204
  });
656202
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")}` : "";
656203
656210
  return `Gather observations about the agent's recent work:
656204
- ${competenceReport}
656211
+ ${competenceReport}${oppText}
656205
656212
 
656206
656213
  Reflect on what went well and what could improve.`;
656207
656214
  }
@@ -656265,7 +656272,7 @@ function renderDMNResting(consecutiveNulls) {
656265
656272
  process.stdout.write(` ${c3.dim("◌")} DMN resting — ${consecutiveNulls} cycles with nothing to do. Waiting for stimulus...
656266
656273
  `);
656267
656274
  }
656268
- var DMNEngine;
656275
+ var execAsync3, DMNEngine;
656269
656276
  var init_dmn_engine = __esm({
656270
656277
  "packages/cli/src/tui/dmn-engine.ts"() {
656271
656278
  "use strict";
@@ -656276,6 +656283,7 @@ var init_dmn_engine = __esm({
656276
656283
  init_render();
656277
656284
  init_promptLoader3();
656278
656285
  init_tool_adapter();
656286
+ execAsync3 = promisify8(exec6);
656279
656287
  DMNEngine = class {
656280
656288
  constructor(config, repoRoot) {
656281
656289
  this.config = config;
@@ -656293,12 +656301,17 @@ var init_dmn_engine = __esm({
656293
656301
  tasksGenerated: 0,
656294
656302
  consecutiveNulls: 0,
656295
656303
  competence: [],
656296
- reflectionBuffer: []
656304
+ reflectionBuffer: [],
656305
+ recentProposalFingerprints: [],
656306
+ tasksGeneratedToday: 0,
656307
+ dayStamp: ""
656297
656308
  };
656298
656309
  stateDir;
656299
656310
  historyDir;
656300
656311
  /** Recent task summaries — appended by the bless engine after each task */
656301
656312
  recentTaskSummaries = [];
656313
+ /** A: the proposal returned by the most recent runCycle, awaiting its outcome. */
656314
+ _lastProposal = null;
656302
656315
  /** Maximum consecutive null cycles before entering extended rest */
656303
656316
  maxConsecutiveNulls = 3;
656304
656317
  /** TUI writeContent callback — wraps stdout writes in scroll buffer management.
@@ -656326,9 +656339,11 @@ var init_dmn_engine = __esm({
656326
656339
  if (this.recentTaskSummaries.length > 10) {
656327
656340
  this.recentTaskSummaries.shift();
656328
656341
  }
656329
- if (category) {
656330
- this.updateCompetence(category, true);
656342
+ const cat2 = category ?? this._lastProposal?.category;
656343
+ if (cat2) {
656344
+ this.updateCompetence(cat2, true);
656331
656345
  }
656346
+ this._lastProposal = null;
656332
656347
  this.saveState();
656333
656348
  }
656334
656349
  /** Record a task failure for Reflexion-style learning */
@@ -656340,9 +656355,11 @@ var init_dmn_engine = __esm({
656340
656355
  if (this.state.reflectionBuffer.length > 5) {
656341
656356
  this.state.reflectionBuffer.shift();
656342
656357
  }
656343
- if (category) {
656344
- this.updateCompetence(category, false);
656358
+ const cat2 = category ?? this._lastProposal?.category;
656359
+ if (cat2) {
656360
+ this.updateCompetence(cat2, false);
656345
656361
  }
656362
+ this._lastProposal = null;
656346
656363
  this.saveState();
656347
656364
  }
656348
656365
  /** Update competence tracker for a task category */
@@ -656373,10 +656390,11 @@ var init_dmn_engine = __esm({
656373
656390
  this.state.totalCycles++;
656374
656391
  const cycleNum = this.state.totalCycles;
656375
656392
  const startMs = Date.now();
656376
- const [reminders, attention, memoryTopics] = await Promise.all([
656393
+ const [reminders, attention, memoryTopics, projectOpportunities] = await Promise.all([
656377
656394
  this.gatherReminders(),
656378
656395
  this.gatherAttentionItems(),
656379
- this.gatherMemoryTopics()
656396
+ this.gatherMemoryTopics(),
656397
+ this.gatherProjectOpportunities()
656380
656398
  ]);
656381
656399
  const capabilities = [
656382
656400
  "file_read — read files",
@@ -656393,13 +656411,15 @@ var init_dmn_engine = __esm({
656393
656411
  memoryTopics,
656394
656412
  capabilities,
656395
656413
  this.state.competence,
656396
- this.state.reflectionBuffer
656414
+ this.state.reflectionBuffer,
656415
+ projectOpportunities
656397
656416
  );
656398
656417
  const modelTier2 = getModelTier(this.config.model);
656399
656418
  this.write(() => renderDMNCycleStart(cycleNum, modelTier2 === "large"));
656400
656419
  const useDeliberation = modelTier2 === "large";
656401
656420
  let result;
656402
656421
  let deliberationMeta = null;
656422
+ let deliberationFallbackProposals = [];
656403
656423
  if (useDeliberation) {
656404
656424
  onEvent?.({
656405
656425
  type: "status",
@@ -656408,6 +656428,7 @@ var init_dmn_engine = __esm({
656408
656428
  });
656409
656429
  const delib = await this.runDeliberationCycle(prompt, memoryTopics, onEvent);
656410
656430
  result = { summary: delib.summary };
656431
+ deliberationFallbackProposals = delib.workspace.proposals ?? [];
656411
656432
  deliberationMeta = {
656412
656433
  rounds: delib.workspace.rounds,
656413
656434
  evidence: delib.workspace.evidenceAccumulated,
@@ -656418,7 +656439,40 @@ var init_dmn_engine = __esm({
656418
656439
  result = { summary: mono.summary };
656419
656440
  }
656420
656441
  const durationMs = Date.now() - startMs;
656421
- 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
+ }
656422
656476
  const cycleResult = {
656423
656477
  cycleNumber: cycleNum,
656424
656478
  startedAt: new Date(startMs).toISOString(),
@@ -656442,6 +656496,7 @@ var init_dmn_engine = __esm({
656442
656496
  }
656443
656497
  this.state.lastCycleAt = (/* @__PURE__ */ new Date()).toISOString();
656444
656498
  this.saveState();
656499
+ this._lastProposal = proposal;
656445
656500
  return proposal;
656446
656501
  }
656447
656502
  /** Run the DMN's internal LLM agent */
@@ -656705,14 +656760,15 @@ If the Amygdala flagged high urgency (due reminders, critical attention items),
656705
656760
  LOWER the threshold to 0.5 — urgent situations demand faster action even with
656706
656761
  less certainty (urgency-gating model, Cisek et al. 2009).
656707
656762
 
656708
- OUTPUT: Call task_complete with JSON:
656763
+ OUTPUT: Call task_complete with JSON (the JSON object MUST be the FINAL thing you output):
656709
656764
  {
656710
656765
  "decision": "GO" | "NOGO",
656711
656766
  "evidenceScore": 0.X,
656712
656767
  "selectedTask": { task, rationale, provenance, category, confidence, challengeResult } | null,
656713
656768
  "reasoning": "how evidence from each region contributed to the decision",
656714
656769
  "innerVoice": "final self-talk summary — what the agent 'says to itself' before acting"
656715
- }`, 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);
656716
656772
  if (gateResult) {
656717
656773
  workspace.findings.push({
656718
656774
  from: "basal_ganglia",
@@ -656862,39 +656918,108 @@ OUTPUT: Call task_complete with JSON:
656862
656918
  };
656863
656919
  }
656864
656920
  /** Parse the DMN agent's output into a structured proposal */
656865
- parseProposal(summary) {
656866
- try {
656867
- const jsonMatch = summary.match(/\{[\s\S]*"selectedTask"[\s\S]*\}/);
656868
- if (!jsonMatch) return null;
656869
- const parsed = JSON.parse(jsonMatch[0]);
656870
- if (!parsed.selectedTask || parsed.selectedTask === null) return null;
656871
- const t2 = parsed.selectedTask;
656872
- return {
656873
- task: String(t2.task ?? ""),
656874
- rationale: String(t2.rationale ?? ""),
656875
- provenance: Array.isArray(t2.provenance) ? t2.provenance.map(String) : [],
656876
- category: ["directive", "exploration", "capability", "maintenance", "social", "autoresearch"].includes(t2.category) ? t2.category : "exploration",
656877
- confidence: typeof t2.confidence === "number" ? Math.min(1, Math.max(0, t2.confidence)) : 0.5,
656878
- challengeResult: t2.challengeResult ? String(t2.challengeResult) : void 0
656879
- };
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
+ }
656880
656950
  } catch {
656881
- if (summary.includes("NO_TASK") || summary.toLowerCase().includes("no task")) {
656882
- 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
+ });
656883
656970
  }
656884
- const lines = summary.split("\n").filter((l2) => l2.trim().length > 20);
656885
- if (lines.length > 0) {
656886
- return {
656887
- task: lines[0].trim().slice(0, 500),
656888
- rationale: "Extracted from DMN natural language output",
656889
- provenance: ["dmn:natural-language-extraction"],
656890
- category: "exploration",
656891
- confidence: 0.3
656892
- };
656971
+ if (fallbackProposals.length > 0) {
656972
+ return [...fallbackProposals].sort((a2, b) => b.confidence - a2.confidence)[0];
656893
656973
  }
656894
- return null;
656895
656974
  }
656975
+ return null;
656896
656976
  }
656897
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
+ }
656898
657023
  async gatherReminders() {
656899
657024
  try {
656900
657025
  const reminders = await getDueReminders(this.repoRoot);
@@ -656997,6 +657122,16 @@ OUTPUT: Call task_complete with JSON:
656997
657122
  }
656998
657123
  }
656999
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
+ }
657000
657135
  fingerprintCycle(result) {
657001
657136
  const selected = result.selectedTask ? [
657002
657137
  result.selectedTask.category,
@@ -712169,10 +712304,10 @@ ${incompleteList}${more}
712169
712304
  const scripts = pkg.scripts || {};
712170
712305
  const checkScript = scripts["typecheck"] ? "typecheck" : scripts["build"] ? "build" : null;
712171
712306
  if (checkScript) {
712172
- const { exec: exec6 } = await import("node:child_process");
712173
- const { promisify: promisify8 } = await import("node:util");
712307
+ const { exec: exec7 } = await import("node:child_process");
712308
+ const { promisify: promisify9 } = await import("node:util");
712174
712309
  try {
712175
- await promisify8(exec6)(`npm run ${checkScript} --silent 2>&1`, {
712310
+ await promisify9(exec7)(`npm run ${checkScript} --silent 2>&1`, {
712176
712311
  cwd: cwd4,
712177
712312
  timeout: 12e4,
712178
712313
  encoding: "utf-8",
@@ -713374,6 +713509,10 @@ Meta-critique: quality ${meta.quality}/5, thorough: ${meta.thorough}`;
713374
713509
  }
713375
713510
  };
713376
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
+ }
713377
713516
  function gatherMemorySnippets(root) {
713378
713517
  const snippets = [];
713379
713518
  const dirs = [
@@ -713482,36 +713621,28 @@ function buildNewTaskIntakePacket(ingress, interpretation, previousPrompt, previ
713482
713621
  function createDMNEventHandler(verbose, writeContent) {
713483
713622
  return (event) => {
713484
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.
713485
713628
  case "tool_call":
713486
- if (verbose) {
713487
- writeContent(() => {
713488
- const args = event.toolArgs ?? {};
713489
- renderToolCallStart(event.toolName ?? "unknown", args, true);
713490
- });
713491
- } else {
713492
- const argSummary = formatDMNToolArgs(
713493
- event.toolName ?? "",
713494
- event.toolArgs ?? {}
713495
- );
713496
- writeContent(
713497
- () => process.stdout.write(
713498
- ` ${c3.dim(`DMN → ${event.toolName}`)}${argSummary ? c3.dim(`: ${argSummary}`) : ""}
713499
- `
713500
- )
713629
+ writeContent(() => {
713630
+ renderToolCallStart(
713631
+ event.toolName ?? "unknown",
713632
+ event.toolArgs ?? {},
713633
+ verbose
713501
713634
  );
713502
- }
713635
+ });
713503
713636
  break;
713504
713637
  case "tool_result":
713505
- if (verbose) {
713506
- writeContent(() => {
713507
- renderToolResult(
713508
- event.toolName ?? "unknown",
713509
- event.success ?? false,
713510
- event.content ?? "",
713511
- true
713512
- );
713513
- });
713514
- }
713638
+ writeContent(() => {
713639
+ renderToolResult(
713640
+ event.toolName ?? "unknown",
713641
+ event.success ?? false,
713642
+ event.content ?? "",
713643
+ verbose
713644
+ );
713645
+ });
713515
713646
  break;
713516
713647
  case "model_response":
713517
713648
  if (verbose && event.content) {
@@ -713548,27 +713679,6 @@ ${event.content}`
713548
713679
  }
713549
713680
  };
713550
713681
  }
713551
- function formatDMNToolArgs(toolName, args) {
713552
- switch (toolName) {
713553
- case "memory_read":
713554
- case "memory_search":
713555
- return String(args["topic"] ?? args["query"] ?? "").slice(0, 60);
713556
- case "file_read":
713557
- return String(args["path"] ?? "").slice(0, 80);
713558
- case "list_directory":
713559
- return String(args["path"] ?? ".").slice(0, 80);
713560
- case "shell":
713561
- return String(args["command"] ?? "").slice(0, 80);
713562
- case "memory_write":
713563
- return `${args["topic"] ?? ""}.${args["key"] ?? ""}`;
713564
- case "grep_search":
713565
- return String(args["pattern"] ?? "").slice(0, 60);
713566
- case "find_files":
713567
- return String(args["pattern"] ?? "").slice(0, 60);
713568
- default:
713569
- return "";
713570
- }
713571
- }
713572
713682
  function truncateByLines(text2, maxLines, maxChars = 600) {
713573
713683
  const lines = text2.split("\n");
713574
713684
  if (lines.length <= maxLines && text2.length <= maxChars) return text2;
@@ -715499,7 +715609,8 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
715499
715609
  turns: result.turns,
715500
715610
  toolCalls: result.toolCalls,
715501
715611
  durationMs: result.durationMs,
715502
- model: config.model
715612
+ model: config.model,
715613
+ verified: result.completed === true
715503
715614
  });
715504
715615
  _tasksSinceImprove++;
715505
715616
  if (_tasksSinceImprove >= SELF_IMPROVE_INTERVAL) {
@@ -716760,6 +716871,7 @@ ${result.summary}`
716760
716871
  let dreamEngine = null;
716761
716872
  let blessEngine = null;
716762
716873
  let dmnEngine = null;
716874
+ let dmnOriginatedTask = false;
716763
716875
  const snrEngine = new SNREngine(currentConfig, repoRoot);
716764
716876
  const emotionEngine = new EmotionEngine({
716765
716877
  backendUrl: currentConfig.backendUrl,
@@ -716853,7 +716965,8 @@ Respond concisely and safely.`;
716853
716965
 
716854
716966
  ${proposal.task}
716855
716967
 
716856
- Rationale: ${proposal.rationale}${provenanceNote}`;
716968
+ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.category)}`;
716969
+ dmnOriginatedTask = true;
716857
716970
  writeContent(
716858
716971
  () => renderInfo(
716859
716972
  `DMN auto-cycle: ${proposal.category} task (${Math.round(proposal.confidence * 100)}% confidence)`
@@ -721407,8 +721520,8 @@ ${taskInput}`;
721407
721520
  const updateInfo = await checkForUpdate(version4);
721408
721521
  if (updateInfo) {
721409
721522
  _autoUpdatedThisSession = true;
721410
- const { exec: exec6 } = await import("node:child_process");
721411
- exec6(
721523
+ const { exec: exec7 } = await import("node:child_process");
721524
+ exec7(
721412
721525
  `npm install -g omnius@latest --prefer-online`,
721413
721526
  { timeout: 18e4 },
721414
721527
  (err) => {
@@ -721465,7 +721578,18 @@ ${emotion.emoji} ${emotion.label}`);
721465
721578
  }
721466
721579
  if (blessEngine?.isActive) {
721467
721580
  blessEngine.recordTaskComplete();
721468
- 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) {
721469
721593
  dmnEngine.recordTaskCompletion(lastCompletedSummary);
721470
721594
  }
721471
721595
  const nextTask = await blessEngine.getNextTask();
@@ -721510,7 +721634,8 @@ Respond concisely and safely.`;
721510
721634
 
721511
721635
  ${proposal.task}
721512
721636
 
721513
- Rationale: ${proposal.rationale}${provenanceNote}`;
721637
+ Rationale: ${proposal.rationale}${provenanceNote}${dmnDevDiscipline(proposal.category)}`;
721638
+ dmnOriginatedTask = true;
721514
721639
  writeContent(
721515
721640
  () => renderInfo(
721516
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.356",
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.356",
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.356",
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
  ═══════════════════════════════════════════════════════════════════════════