omnius 1.0.459 → 1.0.461

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
@@ -566100,6 +566100,11 @@ var init_personality = __esm({
566100
566100
  // packages/orchestrator/dist/critic.js
566101
566101
  function buildCriticGuidanceMessage(call, hits, opts = {}) {
566102
566102
  const argPreview = JSON.stringify(call.args ?? {}).slice(0, 200);
566103
+ const actionReason = call.actionReason ? [
566104
+ call.actionReason.scope ? `scope=${call.actionReason.scope}` : "",
566105
+ call.actionReason.intent ? `intent=${call.actionReason.intent}` : "",
566106
+ call.actionReason.evidence ? `evidence=${call.actionReason.evidence}` : ""
566107
+ ].filter(Boolean).join("; ").slice(0, 500) : "";
566103
566108
  const cached = opts.cachedResult ? `
566104
566109
  Prior evidence preview:
566105
566110
  ${opts.cachedResult.slice(0, 700)}` : "";
@@ -566107,7 +566112,8 @@ ${opts.cachedResult.slice(0, 700)}` : "";
566107
566112
  return `[RUNTIME EVIDENCE CACHE — non-blocking]
566108
566113
  Observation: ${source}
566109
566114
  Call: ${call.tool}(${argPreview})
566110
- State: exact prior evidence exists for these arguments in the current state version.
566115
+ ` + (actionReason ? `Action reason: ${actionReason}
566116
+ ` : "") + `State: exact prior evidence exists for these arguments in the current state version.
566111
566117
  Next action contract: let this result inform the next step once, then pivot to a concrete action.
566112
566118
  Suggested next actions: edit/write the implicated file, run verification, read a different specific file, or complete with evidence. Prefer not to repeat this exact call again unless the filesystem, browser, or page state changed.${cached}`;
566113
566119
  }
@@ -573220,6 +573226,7 @@ var init_context_fabric = __esm({
573220
573226
  GOAL: 100,
573221
573227
  ACTION_CONTRACT: 98,
573222
573228
  USER_STEERING: 95,
573229
+ RUNTIME_GUIDANCE: 94,
573223
573230
  TASK_STATE: 80,
573224
573231
  KNOWN_FILES: 70,
573225
573232
  RECENT_FAILURE: 65,
@@ -573236,6 +573243,7 @@ var init_context_fabric = __esm({
573236
573243
  KIND_ORDER = [
573237
573244
  "goal",
573238
573245
  "user_steering",
573246
+ "runtime_guidance",
573239
573247
  "action_contract",
573240
573248
  "task_state",
573241
573249
  "known_files",
@@ -573253,6 +573261,7 @@ var init_context_fabric = __esm({
573253
573261
  KIND_LABELS = {
573254
573262
  goal: "Goal",
573255
573263
  user_steering: "User Steering",
573264
+ runtime_guidance: "Runtime Guidance",
573256
573265
  action_contract: "Next Action Contract",
573257
573266
  task_state: "Task State",
573258
573267
  known_files: "Known Files",
@@ -573270,6 +573279,7 @@ var init_context_fabric = __esm({
573270
573279
  DEFAULT_KIND_CHAR_BUDGET = {
573271
573280
  goal: 900,
573272
573281
  user_steering: 1400,
573282
+ runtime_guidance: 1800,
573273
573283
  action_contract: 1800,
573274
573284
  task_state: 1800,
573275
573285
  known_files: 2400,
@@ -573983,6 +573993,8 @@ ${obs.stateDigest.slice(0, 1800)}
573983
573993
  `This is a loop. Reason about WHY — for THIS specific call, not generically.`,
573984
573994
  ls2.alreadyHave ? `Evidence the agent ALREADY obtained from a prior identical call:
573985
573995
  ${ls2.alreadyHave.slice(0, 900)}` : `(No cached result available for the repeated call.)`,
573996
+ ls2.actionReason ? `Agent's stated action reason:
573997
+ scope=${ls2.actionReason.scope ?? ""}; intent=${ls2.actionReason.intent ?? ""}; evidence=${ls2.actionReason.evidence ?? ""}; expected=${ls2.actionReason.expected_result ?? ""}`.slice(0, 700) : "",
573986
573998
  "",
573987
573999
  stateDigest.trim(),
573988
574000
  "",
@@ -574384,6 +574396,7 @@ function recordDebugToolEvent(input) {
574384
574396
  },
574385
574397
  ...input.focusSupervisor ? { focusSupervisor: input.focusSupervisor } : {},
574386
574398
  ...input.focusDecision ? { focusDecision: input.focusDecision } : {},
574399
+ ...input.actionReason ? { actionReason: compactDebugValue(input.actionReason) } : {},
574387
574400
  ...input.repeatShortCircuit !== void 0 ? { repeatShortCircuit: input.repeatShortCircuit } : {},
574388
574401
  ...input.runtimeAuthored !== void 0 ? { runtimeAuthored: input.runtimeAuthored } : {},
574389
574402
  ...input.runtimeGuidance ? { runtimeGuidancePreview: trim(input.runtimeGuidance, 1200) } : {},
@@ -575053,6 +575066,30 @@ function trim(value2, max) {
575053
575066
  return `${value2.slice(0, max)}
575054
575067
  [truncated ${value2.length - max} chars]`;
575055
575068
  }
575069
+ function compactDebugValue(value2, depth = 0) {
575070
+ if (value2 == null)
575071
+ return value2;
575072
+ if (typeof value2 === "string") {
575073
+ return value2.length > 500 ? `${value2.slice(0, 497)}...` : value2;
575074
+ }
575075
+ if (typeof value2 === "number" || typeof value2 === "boolean")
575076
+ return value2;
575077
+ if (Array.isArray(value2)) {
575078
+ if (depth >= 2)
575079
+ return `[array:${value2.length}]`;
575080
+ return value2.slice(0, 8).map((entry) => compactDebugValue(entry, depth + 1));
575081
+ }
575082
+ if (typeof value2 === "object") {
575083
+ if (depth >= 2)
575084
+ return "[object]";
575085
+ const out = {};
575086
+ for (const [key, entry] of Object.entries(value2).slice(0, 12)) {
575087
+ out[key] = compactDebugValue(entry, depth + 1);
575088
+ }
575089
+ return out;
575090
+ }
575091
+ return String(value2);
575092
+ }
575056
575093
  function safeId2(value2) {
575057
575094
  return String(value2 || "unknown").replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 160);
575058
575095
  }
@@ -575094,6 +575131,9 @@ function violatesDirective(directive, input) {
575094
575131
  }
575095
575132
  if (input.toolName === "ask_user")
575096
575133
  return false;
575134
+ if (directive.requiredNextAction === "read_authoritative_target" && input.usesAuthoritativeTargetEvidence && isEditTool(input.toolName)) {
575135
+ return false;
575136
+ }
575097
575137
  const family = actionFamily(input.toolName, input.args);
575098
575138
  if (directive.requiredNextAction !== "update_todos") {
575099
575139
  if (directive.forbiddenActionFamilies.includes(family))
@@ -575181,6 +575221,17 @@ function compactCachedEvidence(text2) {
575181
575221
  "Use the cached evidence now, or request a distinct offset/limit/query if genuinely needed."
575182
575222
  ].filter(Boolean).join("\n");
575183
575223
  }
575224
+ function actionReasonSummary(actionReason) {
575225
+ if (!actionReason)
575226
+ return "";
575227
+ const parts = [
575228
+ actionReason.scope ? `scope=${actionReason.scope}` : "",
575229
+ actionReason.intent ? `intent=${actionReason.intent}` : "",
575230
+ actionReason.evidence ? `evidence=${actionReason.evidence}` : "",
575231
+ actionReason.expected_result ? `expected=${actionReason.expected_result}` : ""
575232
+ ].filter(Boolean);
575233
+ return parts.length > 0 ? `Action reason observed: ${parts.join("; ").slice(0, 700)}` : "";
575234
+ }
575184
575235
  function actionFamily(toolName, args) {
575185
575236
  if (isEditTool(toolName)) {
575186
575237
  const path12 = primaryPath(args);
@@ -575404,8 +575455,9 @@ var init_focusSupervisor = __esm({
575404
575455
  `${reason}.`,
575405
575456
  `Required next action: ${prior.requiredNextAction}.`,
575406
575457
  `Blocked action family: ${family}.`,
575458
+ actionReasonSummary(input.actionReason),
575407
575459
  "This block is not counted as a new ignored directive."
575408
- ].join("\n"), false);
575460
+ ].filter(Boolean).join("\n"), false);
575409
575461
  }
575410
575462
  return this.pass();
575411
575463
  }
@@ -575446,8 +575498,9 @@ var init_focusSupervisor = __esm({
575446
575498
  `[FOCUS SUPERVISOR BLOCK] The previous directive was ignored: ${prior.reason}`,
575447
575499
  `Required next action: ${directive.requiredNextAction}.`,
575448
575500
  `Blocked action family: ${family}.`,
575501
+ actionReasonSummary(input.actionReason),
575449
575502
  directive.requiredNextAction === "report_incomplete" ? "Stop trying tool variants. Report incomplete/blocked with the concrete evidence." : "Take the required next action before trying another variant."
575450
- ].join("\n"), false);
575503
+ ].filter(Boolean).join("\n"), false);
575451
575504
  }
575452
575505
  }
575453
575506
  if (input.stalePreflightMessage) {
@@ -575458,9 +575511,12 @@ var init_focusSupervisor = __esm({
575458
575511
  requiredNextAction: "read_authoritative_target",
575459
575512
  forbiddenActionFamilies: [family]
575460
575513
  });
575461
- return this.block(directive, `${input.stalePreflightMessage}
575462
-
575463
- [FOCUS SUPERVISOR] Required next action: file_read the authoritative current target once, then build a new edit from current evidence or report incomplete.`, false);
575514
+ return this.block(directive, [
575515
+ input.stalePreflightMessage,
575516
+ "",
575517
+ actionReasonSummary(input.actionReason),
575518
+ "[FOCUS SUPERVISOR] Required next action: file_read the authoritative current target once, then build a new edit from current evidence or report incomplete."
575519
+ ].filter(Boolean).join("\n"), false);
575464
575520
  }
575465
575521
  const duplicateHitCount = input.duplicateHitCount ?? 0;
575466
575522
  if (input.cachedResult && duplicateHitCount > 0) {
@@ -575480,9 +575536,10 @@ var init_focusSupervisor = __esm({
575480
575536
  input.cachedResultFailed ? "[FOCUS SUPERVISOR: CACHED FAILURE]" : "[FOCUS SUPERVISOR: CACHED EVIDENCE]",
575481
575537
  `This ${input.toolName} action family has already produced evidence and should not be re-executed unchanged.`,
575482
575538
  `Required next action: ${directive.requiredNextAction}.`,
575539
+ actionReasonSummary(input.actionReason),
575483
575540
  "",
575484
575541
  compactCachedEvidence(input.cachedResult)
575485
- ].join("\n"), !input.cachedResultFailed);
575542
+ ].filter(Boolean).join("\n"), !input.cachedResultFailed);
575486
575543
  }
575487
575544
  return this.inject(directive, `[FOCUS SUPERVISOR] ${directive.reason}. Required next action: ${directive.requiredNextAction}.`);
575488
575545
  }
@@ -575536,6 +575593,10 @@ var init_focusSupervisor = __esm({
575536
575593
  this.lastReason = "cached/no-op tool result did not perform the required recovery action";
575537
575594
  return;
575538
575595
  }
575596
+ if (input.success && input.usedAuthoritativeTargetEvidence && isEditTool(input.toolName) && this.directive?.requiredNextAction === "read_authoritative_target") {
575597
+ this.clearSatisfiedDirective("verified edit used authoritative target evidence", input.turn);
575598
+ return;
575599
+ }
575539
575600
  if (input.toolName === "file_read" && input.success) {
575540
575601
  if (this.directive?.requiredNextAction === "read_authoritative_target") {
575541
575602
  this.clearSatisfiedDirective("authoritative evidence refreshed", input.turn);
@@ -578219,7 +578280,9 @@ function sanitizeHistoryThink(messages2) {
578219
578280
  const stripped = messages2.map((m2) => {
578220
578281
  if (m2.role !== "assistant" || typeof m2.content !== "string")
578221
578282
  return m2;
578222
- return { ...m2, content: stripThinkBlocks(m2.content) };
578283
+ const withoutThink = stripThinkBlocks(m2.content);
578284
+ const collapsed = collapseDegenerateAssistantRepetition(withoutThink);
578285
+ return { ...m2, content: collapsed ?? withoutThink };
578223
578286
  });
578224
578287
  const sanitized = [];
578225
578288
  for (let i2 = 0; i2 < stripped.length; i2++) {
@@ -578231,6 +578294,35 @@ function sanitizeHistoryThink(messages2) {
578231
578294
  }
578232
578295
  return sanitized;
578233
578296
  }
578297
+ function collapseDegenerateAssistantRepetition(content) {
578298
+ const trimmed = content.trim();
578299
+ if (trimmed.length < 160)
578300
+ return null;
578301
+ const lineUnits = trimmed.split(/\n+/).map((line) => line.trim()).filter((line) => line.length >= 12);
578302
+ const sentenceUnits = lineUnits.length >= 3 ? lineUnits : trimmed.split(/(?<=[.!?])\s+/).map((part) => part.trim()).filter((part) => part.length >= 12);
578303
+ if (sentenceUnits.length < 3)
578304
+ return null;
578305
+ const counts = /* @__PURE__ */ new Map();
578306
+ for (const unit of sentenceUnits) {
578307
+ const key = unit.toLowerCase().replace(/\s+/g, " ").slice(0, 220);
578308
+ const current = counts.get(key) ?? { count: 0, chars: 0, sample: unit };
578309
+ current.count++;
578310
+ current.chars += unit.length;
578311
+ counts.set(key, current);
578312
+ }
578313
+ let best = null;
578314
+ for (const value2 of counts.values()) {
578315
+ if (!best || value2.count > best.count || value2.count === best.count && value2.chars > best.chars) {
578316
+ best = value2;
578317
+ }
578318
+ }
578319
+ if (!best || best.count < 3)
578320
+ return null;
578321
+ const repeatedRatio = best.chars / Math.max(1, trimmed.length);
578322
+ if (repeatedRatio < 0.55)
578323
+ return null;
578324
+ return `[assistant repetitive text suppressed: repeated ${best.count}x "${best.sample.slice(0, 180)}"]`;
578325
+ }
578234
578326
  function isOrphanDuplicateAssistantToolAnchor(messages2, index, referencedToolCallIds) {
578235
578327
  const current = messages2[index];
578236
578328
  if (!current)
@@ -578385,7 +578477,7 @@ function classifyThinkOutcome(raw) {
578385
578477
  }
578386
578478
  return null;
578387
578479
  }
578388
- var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
578480
+ var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, TOOL_ACTION_REASON_KEYS, TOOL_ACTION_REASON_SCOPES, TOOL_ACTION_REASON_SCHEMA, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
578389
578481
  var init_agenticRunner = __esm({
578390
578482
  "packages/orchestrator/dist/agenticRunner.js"() {
578391
578483
  "use strict";
@@ -578474,6 +578566,56 @@ var init_agenticRunner = __esm({
578474
578566
  skill: ["skill_list", "skill_extract", "skill_execute", "skill_search"]
578475
578567
  };
578476
578568
  TOOL_AUTO_DEMOTE_TURNS = 10;
578569
+ TOOL_ACTION_REASON_KEYS = /* @__PURE__ */ new Set([
578570
+ "action_reason",
578571
+ "actionReason",
578572
+ "justification"
578573
+ ]);
578574
+ TOOL_ACTION_REASON_SCOPES = /* @__PURE__ */ new Set([
578575
+ "discovery",
578576
+ "targeted_patch",
578577
+ "new_file",
578578
+ "full_file_rewrite",
578579
+ "verification",
578580
+ "state_update",
578581
+ "delegation",
578582
+ "completion",
578583
+ "other"
578584
+ ]);
578585
+ TOOL_ACTION_REASON_SCHEMA = {
578586
+ type: "object",
578587
+ description: "Compact public action contract for this exact tool call. Do not include hidden chain-of-thought; use concise task-state facts.",
578588
+ properties: {
578589
+ task_anchor: {
578590
+ type: "string",
578591
+ description: "One compact phrase tying this call to the active user goal/current focus."
578592
+ },
578593
+ intent: {
578594
+ type: "string",
578595
+ description: "One sentence explaining why this tool is the next action."
578596
+ },
578597
+ evidence: {
578598
+ type: "string",
578599
+ description: "Fresh evidence or contract authorizing the action: file hash, focus directive, cached evidence, user request, or explicit absence."
578600
+ },
578601
+ expected_result: {
578602
+ type: "string",
578603
+ description: "The concrete new state or information expected from this call."
578604
+ },
578605
+ scope: {
578606
+ type: "string",
578607
+ enum: Array.from(TOOL_ACTION_REASON_SCOPES),
578608
+ description: "Classify the action scope. Use targeted_patch for constrained edits; full_file_rewrite only for deliberate whole-file replacement."
578609
+ }
578610
+ },
578611
+ required: [
578612
+ "task_anchor",
578613
+ "intent",
578614
+ "evidence",
578615
+ "expected_result",
578616
+ "scope"
578617
+ ]
578618
+ };
578477
578619
  SYSTEM_PROMPT = loadPrompt("agentic/system-large.md");
578478
578620
  SYSTEM_PROMPT_MEDIUM = loadPrompt("agentic/system-medium.md");
578479
578621
  SYSTEM_PROMPT_SMALL = loadPrompt("agentic/system-small.md");
@@ -578529,6 +578671,7 @@ var init_agenticRunner = __esm({
578529
578671
  _onTypedEvent;
578530
578672
  handlers = [];
578531
578673
  pendingUserMessages = [];
578674
+ pendingRuntimeGuidanceMessages = [];
578532
578675
  aborted = false;
578533
578676
  _abortController = new AbortController();
578534
578677
  _paused = false;
@@ -578908,6 +579051,7 @@ var init_agenticRunner = __esm({
578908
579051
  //
578909
579052
  // Kill switch: OMNIUS_DISABLE_REG61_COERCE=1 disables BOTH set and enforce.
578910
579053
  _reg61PerpetualGateActive = false;
579054
+ _reg61DeferredFocusDirectiveId = null;
578911
579055
  // DECOMP-2 (root-cause from batch531-midi-decomp, 2026-05-03): compelling
578912
579056
  // sub_agent delegation. DECOMP-1's informational directive was ignored
578913
579057
  // (0 sub_agent calls in 466 tool-call run despite directive at turn 1).
@@ -579027,28 +579171,58 @@ var init_agenticRunner = __esm({
579027
579171
  return this.options.workboardDir || this._workingDirectory || process.cwd();
579028
579172
  }
579029
579173
  getOrCreateWorkboard() {
579030
- if (this._workboard)
579174
+ const goal = this._taskState.originalGoal || this._taskState.goal || "";
579175
+ if (this._workboard) {
579176
+ this._workboard = this._seedWorkboardCardsIfNeeded(this._workboard, goal);
579031
579177
  return this._workboard;
579178
+ }
579032
579179
  const dir = this._workboardDir();
579033
579180
  const runId = this._sessionId;
579034
579181
  const existing = loadWorkboardSnapshot(dir, runId);
579035
579182
  if (existing) {
579036
- this._workboard = existing;
579037
- return existing;
579183
+ this._workboard = this._seedWorkboardCardsIfNeeded(existing, goal);
579184
+ return this._workboard;
579038
579185
  }
579039
579186
  try {
579040
579187
  this._workboard = createWorkboard(dir, {
579041
579188
  runId,
579042
579189
  owner: this.options.subAgent ? "sub-agent" : "agent",
579043
- goal: this._taskState.originalGoal || this._taskState.goal || void 0,
579190
+ goal: goal || void 0,
579044
579191
  title: (this._taskState.goal || "").slice(0, 120) || void 0,
579045
- cards: this._initialWorkboardCardsForGoal(this._taskState.originalGoal || this._taskState.goal || "")
579192
+ cards: this._initialWorkboardCardsForGoal(goal)
579046
579193
  });
579047
579194
  } catch {
579048
579195
  this._workboard = loadWorkboardSnapshot(dir, runId);
579049
579196
  }
579197
+ if (this._workboard) {
579198
+ this._workboard = this._seedWorkboardCardsIfNeeded(this._workboard, goal);
579199
+ }
579050
579200
  return this._workboard;
579051
579201
  }
579202
+ _seedWorkboardCardsIfNeeded(snapshot, goal) {
579203
+ if (snapshot.cards.length > 0)
579204
+ return snapshot;
579205
+ const cards = this._initialWorkboardCardsForGoal(goal);
579206
+ if (cards.length === 0)
579207
+ return snapshot;
579208
+ let current = snapshot;
579209
+ const dir = this._workboardDir();
579210
+ for (const card of cards) {
579211
+ try {
579212
+ current = addWorkboardCard(dir, {
579213
+ ...card,
579214
+ runId: snapshot.runId,
579215
+ actor: this.options.subAgent ? "sub-agent" : "agent",
579216
+ decisionReason: "Seeded missing workboard card after an empty persisted board was detected for an active user task."
579217
+ });
579218
+ } catch {
579219
+ const refreshed = readActiveWorkboardSnapshot(dir, snapshot.runId);
579220
+ if (refreshed)
579221
+ current = refreshed;
579222
+ }
579223
+ }
579224
+ return readActiveWorkboardSnapshot(dir, snapshot.runId) ?? current;
579225
+ }
579052
579226
  _initialWorkboardCardsForGoal(goal) {
579053
579227
  if (!this.writesUserTaskArtifacts())
579054
579228
  return [];
@@ -579166,9 +579340,11 @@ var init_agenticRunner = __esm({
579166
579340
  injectWorkboardContext() {
579167
579341
  if (this._workboard || this.options.subAgent) {
579168
579342
  const dir = this._workboardDir();
579169
- const snapshot = readActiveWorkboardSnapshot(dir, this._sessionId);
579343
+ let snapshot = readActiveWorkboardSnapshot(dir, this._sessionId);
579170
579344
  if (!snapshot)
579171
579345
  return null;
579346
+ snapshot = this._seedWorkboardCardsIfNeeded(snapshot, this._taskState.originalGoal || this._taskState.goal || "");
579347
+ this._workboard = snapshot;
579172
579348
  const activeCards = snapshot.cards.filter((c9) => c9.status === "in_progress" || c9.status === "open" || c9.status === "needs_changes");
579173
579349
  if (activeCards.length === 0 && snapshot.cards.every((c9) => c9.status === "verified" || c9.status === "completed"))
579174
579350
  return null;
@@ -579198,11 +579374,13 @@ ${parts.join("\n")}
579198
579374
  const dir = this._workboardDir();
579199
579375
  const fromDisk = readActiveWorkboardSnapshot(dir, this._sessionId);
579200
579376
  if (fromDisk) {
579201
- this._workboard = fromDisk;
579202
- return fromDisk;
579377
+ this._workboard = this._seedWorkboardCardsIfNeeded(fromDisk, this._taskState.originalGoal || this._taskState.goal || "");
579378
+ return this._workboard;
579203
579379
  }
579204
- if (this._workboard)
579380
+ if (this._workboard) {
579381
+ this._workboard = this._seedWorkboardCardsIfNeeded(this._workboard, this._taskState.originalGoal || this._taskState.goal || "");
579205
579382
  return this._workboard;
579383
+ }
579206
579384
  const goal = this._taskState.originalGoal || this._taskState.goal || "";
579207
579385
  if (this._initialWorkboardCardsForGoal(goal).length === 0)
579208
579386
  return null;
@@ -579263,7 +579441,7 @@ ${parts.join("\n")}
579263
579441
  cardId: implementation2.id,
579264
579442
  title: implementation2.title,
579265
579443
  reason: "discovery evidence is already present; land the smallest concrete repair",
579266
- preferredTools: ["file_write", "file_edit", "batch_edit", "file_patch"]
579444
+ preferredTools: ["file_patch", "batch_edit", "file_edit", "file_write"]
579267
579445
  };
579268
579446
  }
579269
579447
  const active = snapshot.cards.find((card) => card.status === "in_progress") ?? snapshot.cards.find((card) => card.status === "needs_changes") ?? snapshot.cards.find((card) => card.status === "open") ?? discovery;
@@ -579273,7 +579451,7 @@ ${parts.join("\n")}
579273
579451
  cardId: active.id,
579274
579452
  title: active.title,
579275
579453
  reason: active.id === "discover-current-state" ? "no authoritative target evidence is recorded yet" : "workboard card is the next unresolved card",
579276
- preferredTools: active.id === "discover-current-state" ? ["file_read", "grep_search", "list_directory", "shell"] : ["file_write", "file_edit", "batch_edit", "file_patch", "shell"]
579454
+ preferredTools: active.id === "discover-current-state" ? ["file_read", "grep_search", "list_directory", "shell"] : ["file_patch", "batch_edit", "file_edit", "file_write", "shell"]
579277
579455
  };
579278
579456
  }
579279
579457
  _focusToolsForRequiredAction(action) {
@@ -579287,7 +579465,7 @@ ${parts.join("\n")}
579287
579465
  case "disambiguate_edit_match":
579288
579466
  return "file_edit or batch_edit with replace_all=true for identical global changes, or with a more unique old_string copied from current file context";
579289
579467
  case "edit_different_target":
579290
- return "file_write, file_edit, batch_edit, or file_patch on a different supported target";
579468
+ return "file_patch, batch_edit, file_edit, or hash-guarded file_write on a different supported target";
579291
579469
  case "run_verification":
579292
579470
  return "shell running the verification/runtime command";
579293
579471
  case "report_blocked":
@@ -579299,6 +579477,25 @@ ${parts.join("\n")}
579299
579477
  }
579300
579478
  _renderNextActionContract(turn) {
579301
579479
  const lines = ["[NEXT ACTION CONTRACT]", `turn=${turn}`];
579480
+ const ts = this._taskState;
579481
+ const goal = (ts.originalGoal || ts.goal || "").replace(/\s+/g, " ").trim();
579482
+ if (goal)
579483
+ lines.push(`goal_anchor=${goal.slice(0, 360)}`);
579484
+ if (ts.currentStep) {
579485
+ lines.push(`current_focus=${ts.currentStep.replace(/\s+/g, " ").slice(0, 220)}`);
579486
+ }
579487
+ if (ts.nextAction) {
579488
+ lines.push(`declared_next_action=${ts.nextAction.replace(/\s+/g, " ").slice(0, 220)}`);
579489
+ }
579490
+ lines.push(`progress_anchor=completed_steps:${ts.completedSteps.length} failed_approaches:${ts.failedApproaches.length} modified_files:${ts.modifiedFiles.size} tool_calls:${ts.toolCallCount}`);
579491
+ if (ts.completedSteps.length > 0) {
579492
+ lines.push(`recent_completed=${ts.completedSteps.slice(-3).join(" | ").replace(/\s+/g, " ").slice(0, 420)}`);
579493
+ }
579494
+ if (ts.failedApproaches.length > 0) {
579495
+ lines.push(`recent_failed_approaches=${ts.failedApproaches.slice(-3).join(" | ").replace(/\s+/g, " ").slice(0, 420)}`);
579496
+ }
579497
+ lines.push("tool_call_contract=Each tool call must include action_reason {task_anchor,intent,evidence,expected_result,scope}; compact public facts only, no hidden chain-of-thought.");
579498
+ lines.push(`scope_contract=${Array.from(TOOL_ACTION_REASON_SCOPES).join("|")}; use targeted_patch for constrained code edits and full_file_rewrite only for deliberate whole-file replacement.`);
579302
579499
  const focus = this._focusSupervisor?.snapshot().directive ?? null;
579303
579500
  if (focus) {
579304
579501
  lines.push(`focus_required_next_action=${focus.requiredNextAction}`);
@@ -580551,6 +580748,25 @@ Your hypotheses MUST address this specific error, not generic causes.
580551
580748
  const fa = this._detectFailingApproachSignal(turn);
580552
580749
  return fa ? this._failingApproachDirective(fa) : null;
580553
580750
  }
580751
+ _focusDirectiveSuppressesEditPressure() {
580752
+ return this._focusSupervisor?.currentDirective ?? null;
580753
+ }
580754
+ _deferReg61ToFocusDirective(input) {
580755
+ if (this._reg61PerpetualGateActive) {
580756
+ this._reg61PerpetualGateActive = false;
580757
+ }
580758
+ if (this._reg61DeferredFocusDirectiveId === input.directive.id)
580759
+ return;
580760
+ this._reg61DeferredFocusDirectiveId = input.directive.id;
580761
+ const _cyclePart = input.cycleLabel ? ` (${input.cycleLabel})` : "";
580762
+ const _detail = input.detail ? `; ${input.detail}` : "";
580763
+ this.emit({
580764
+ type: "status",
580765
+ content: `REG-61 deferred to focus supervisor at turn ${input.turn}${_cyclePart}; source=${input.source}; directive_id=${input.directive.id}; required_next_action=${input.directive.requiredNextAction}${_detail}`,
580766
+ turn: input.turn,
580767
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
580768
+ });
580769
+ }
580554
580770
  /**
580555
580771
  * REG-61 sliding-window first-edit / sustained-edit nudge.
580556
580772
  *
@@ -580642,11 +580858,23 @@ Your hypotheses MUST address this specific error, not generic causes.
580642
580858
  }
580643
580859
  if (_readsInWindow < REG61_MIN_READS)
580644
580860
  return;
580861
+ const _gapDesc = this._lastFileWriteTurn < 0 ? `no creative edits yet this run` : `${turn - this._lastFileWriteTurn} turns since last creative edit (turn ${this._lastFileWriteTurn})`;
580862
+ const focusDirective = this._focusDirectiveSuppressesEditPressure();
580863
+ if (focusDirective) {
580864
+ this._deferReg61ToFocusDirective({
580865
+ directive: focusDirective,
580866
+ turn,
580867
+ cycleLabel,
580868
+ source: "trigger",
580869
+ detail: `reads_in_window=${_readsInWindow}; ${_gapDesc}`
580870
+ });
580871
+ return;
580872
+ }
580873
+ this._reg61DeferredFocusDirectiveId = null;
580645
580874
  this._reg61CooldownUntilTurn = turn + REG61_COOLDOWN;
580646
580875
  if (process.env["OMNIUS_DISABLE_REG61_COERCE"] !== "1") {
580647
580876
  this._reg61PerpetualGateActive = true;
580648
580877
  }
580649
- const _gapDesc = this._lastFileWriteTurn < 0 ? `no creative edits yet this run` : `${turn - this._lastFileWriteTurn} turns since last creative edit (turn ${this._lastFileWriteTurn})`;
580650
580878
  const reg61Msg = `[FIRST-EDIT NUDGE — REG-61]
580651
580879
  You have made ${_readsInWindow} read/exploration calls in the trailing window — ${_gapDesc}. Reading is preparation; writing is progress. Runs that stay in pure-read mode produce zero deliverables.
580652
580880
 
@@ -581569,7 +581797,7 @@ ${context2 ?? ""}`;
581569
581797
  }
581570
581798
  if (critique2) {
581571
581799
  this._visualNudgesEmitted.add(nudgeKey);
581572
- this.pendingUserMessages.push(critique2);
581800
+ this.enqueueRuntimeGuidance(critique2);
581573
581801
  }
581574
581802
  }
581575
581803
  }
@@ -584418,7 +584646,7 @@ ${blob}
584418
584646
  });
584419
584647
  if (!this._microcompactHintEmitted && this.tools.has("memory_write")) {
584420
584648
  this._microcompactHintEmitted = true;
584421
- this.pendingUserMessages.push(`[SYSTEM] Older tool results have been cleared to save context. If you discovered important patterns or facts, use memory_write to persist them before they are lost from context.`);
584649
+ this.enqueueRuntimeGuidance(`[SYSTEM] Older tool results have been cleared to save context. If you discovered important patterns or facts, use memory_write to persist them before they are lost from context.`);
584422
584650
  }
584423
584651
  }
584424
584652
  }
@@ -584738,7 +584966,7 @@ ${blob}
584738
584966
  if (seen.has(value2))
584739
584967
  return "#cycle";
584740
584968
  seen.add(value2);
584741
- const entries = Object.entries(value2).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${this._canonicalArgValue(v, seen)}`);
584969
+ const entries = Object.entries(value2).filter(([key]) => !TOOL_ACTION_REASON_KEYS.has(key)).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${this._canonicalArgValue(v, seen)}`);
584742
584970
  return `#object:{${entries.join(",")}}`;
584743
584971
  }
584744
584972
  return String(value2);
@@ -585151,6 +585379,11 @@ ${notice}`;
585151
585379
  injectUserMessage(content) {
585152
585380
  this.pendingUserMessages.push(this.scrubUserInput(content, "injected-user-message"));
585153
585381
  }
585382
+ enqueueRuntimeGuidance(content) {
585383
+ const cleaned = this.scrubUserInput(content, "runtime-guidance").trim();
585384
+ if (cleaned)
585385
+ this.pendingRuntimeGuidanceMessages.push(cleaned);
585386
+ }
585154
585387
  /** Retract the last pending user message (Esc cancel).
585155
585388
  * Returns the retracted text, or null if queue is empty or already consumed. */
585156
585389
  retractLastPendingMessage() {
@@ -585998,7 +586231,7 @@ TASK: ${scrubbedTask}` : scrubbedTask;
585998
586231
  onCritique: (critique2, sourceTurn) => {
585999
586232
  const runClosed = completed || this._completionIncompleteVerification || this.aborted;
586000
586233
  if (!runClosed && (this._adversaryMode === "skillcoach" || this._adversaryMode === "both")) {
586001
- this.pendingUserMessages.push(AdversaryStream.formatInjection(critique2));
586234
+ this.enqueueRuntimeGuidance(AdversaryStream.formatInjection(critique2));
586002
586235
  }
586003
586236
  this.emit({
586004
586237
  type: "adversary_reaction",
@@ -587203,7 +587436,7 @@ ${_staleSamples.join("\n")}` : ``,
587203
587436
  ``,
587204
587437
  ` (c) REPLACE_ALL: If you want a global rename, set replace_all=true and the uniqueness check is bypassed.`,
587205
587438
  ``,
587206
- ` (d) FILE_WRITE: If the changes are extensive, rewrite the whole file with file_write instead of patching it. Faster than 6 failed edits.`,
587439
+ ` (d) CONSTRAINED PATCH: If the exact replacement is too brittle, use file_patch with expected_old_content or batch_edit/file_edit against fresh current text. Use file_write only for new files, dry-run proposals, or deliberate hash-guarded full_file_rewrite actions.`,
587207
587440
  ``,
587208
587441
  `Do NOT in your next response: call file_edit or batch_edit on ${_efWorstPath} again with another guess at old_string.`
587209
587442
  ].join("\n")
@@ -587398,7 +587631,7 @@ ${_staleSamples.join("\n")}` : ``,
587398
587631
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
587399
587632
  });
587400
587633
  messages2.push({
587401
- role: "user",
587634
+ role: "system",
587402
587635
  content: this.buildHealthCheckPrompt(elapsed, toolCallCount, repetitionScore, selfEvalCount)
587403
587636
  });
587404
587637
  nextSelfEval = now2 + selfEvalInterval;
@@ -587433,6 +587666,7 @@ ${_staleSamples.join("\n")}` : ``,
587433
587666
  });
587434
587667
  }
587435
587668
  }
587669
+ this.drainPendingRuntimeGuidance(turn);
587436
587670
  while (this.pendingUserMessages.length > 0) {
587437
587671
  const userMsg = this.pendingUserMessages.shift();
587438
587672
  await this.appendInjectedUserMessage(userMsg, messages2, turn);
@@ -587440,7 +587674,7 @@ ${_staleSamples.join("\n")}` : ``,
587440
587674
  if (!this.options.disableTodoPlanningNudges) {
587441
587675
  const maybeReminder = this.getTodoReminderContent(turn);
587442
587676
  if (maybeReminder) {
587443
- messages2.push({ role: "user", content: maybeReminder });
587677
+ messages2.push({ role: "system", content: maybeReminder });
587444
587678
  this.emit({
587445
587679
  type: "status",
587446
587680
  content: `todo_reminder injected (turn ${turn}, last todo_write turn ${this._lastTodoWriteTurn})`,
@@ -587459,7 +587693,7 @@ ${_staleSamples.join("\n")}` : ``,
587459
587693
  const isComplex = !explicitSingleTool && (wordCount2 > 40 || hasMultipleActions || hasMultipleFiles);
587460
587694
  if (isComplex) {
587461
587695
  messages2.push({
587462
- role: "user",
587696
+ role: "system",
587463
587697
  content: `[TASK DECOMPOSITION — Multi-step task detected]
587464
587698
 
587465
587699
  MANDATORY FIRST ACTION: Call todo_write NOW with the complete plan.
@@ -587485,7 +587719,7 @@ Call todo_write FIRST, then start with step 1.`
587485
587719
  const isGreenfield = /\bcreate\b.*\bnew\b|\bimplement\b.*\bclass\b|\bwrite\b.*\bfrom scratch\b|\bbuild\b.*\bmodule\b/i.test(goal);
587486
587720
  if (isGreenfield && !isComplex) {
587487
587721
  messages2.push({
587488
- role: "user",
587722
+ role: "system",
587489
587723
  content: `[GREENFIELD TASK — Write code immediately]
587490
587724
 
587491
587725
  MANDATORY: Your FIRST tool call MUST be file_write.
@@ -587611,7 +587845,7 @@ ${hints.join("\n")}`);
587611
587845
  const isLooping = repetitionRatio > 0.4;
587612
587846
  const highArousal = errorRatio > 0.5 || isLooping;
587613
587847
  if (highArousal && !isLooping && !noProgress) {
587614
- this.pendingUserMessages.push(`[COGNITIVE STATE: HIGH AROUSAL] Recent error rate is ${Math.round(errorRatio * 100)}%. Your current approach may be hitting a wall. Consider:
587848
+ this.enqueueRuntimeGuidance(`[COGNITIVE STATE: HIGH AROUSAL] Recent error rate is ${Math.round(errorRatio * 100)}%. Your current approach may be hitting a wall. Consider:
587615
587849
  1. Read related files you haven't examined yet
587616
587850
  2. Search for similar patterns in the codebase
587617
587851
  3. Check if your assumptions about the API/framework are correct
@@ -587621,7 +587855,7 @@ State what you've learned from the errors before trying again.`);
587621
587855
  const progress = this._taskState.completedSteps.slice(-3).join("; ");
587622
587856
  const failures = this._taskState.failedApproaches.slice(-2).join("; ");
587623
587857
  messages2.push({
587624
- role: "user",
587858
+ role: "system",
587625
587859
  content: `[PROGRESS CHECK — Turn ${turn}]
587626
587860
  **Goal:** ${this._taskState.goal}
587627
587861
  ` + (progress ? `**Done so far:** ${progress}
@@ -588032,7 +588266,7 @@ ${memoryLines.join("\n")}`
588032
588266
  const result = this.applyRegisteredToolResultTriage(rawResult, resolvedParsedTool.name, tool);
588033
588267
  messages2.push({ role: "assistant", content });
588034
588268
  messages2.push({
588035
- role: "user",
588269
+ role: "system",
588036
588270
  content: `Tool result (${parsed.tool}): ${result.output.slice(0, 2e3)}`
588037
588271
  });
588038
588272
  if (resolvedParsedTool.name === "task_complete") {
@@ -588221,7 +588455,7 @@ ${memoryLines.join("\n")}`
588221
588455
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
588222
588456
  });
588223
588457
  messages2.push({
588224
- role: "user",
588458
+ role: "system",
588225
588459
  content: `[SYSTEM] You produced ${consecutiveEmptyResponses} empty responses in a row. You MUST take action now. Your task: ${this._taskState.goal}
588226
588460
 
588227
588461
  ` + (this._taskState.nextAction ? `**DO THIS NOW:** ${this._taskState.nextAction}
@@ -588306,6 +588540,7 @@ ${memoryLines.join("\n")}`
588306
588540
  const executeSingle = async (tc) => {
588307
588541
  if (this.aborted)
588308
588542
  return null;
588543
+ const actionReasonForAdvisory = this._extractToolActionReason(tc.arguments ?? {});
588309
588544
  const cohortKey = this.buildArgCohortKey(tc.name, tc.arguments);
588310
588545
  const cohort = this._argCohorts.get(cohortKey);
588311
588546
  if (cohort && cohort.failure >= 3 && cohort.success === 0) {
@@ -588319,7 +588554,7 @@ ${memoryLines.join("\n")}`
588319
588554
  }
588320
588555
  });
588321
588556
  if (this._adversaryMode === "skillcoach" || this._adversaryMode === "both") {
588322
- this.pendingUserMessages.push(`[ADVERSARY CRITIQUE — non-blocking]
588557
+ this.enqueueRuntimeGuidance(`[ADVERSARY CRITIQUE — non-blocking]
588323
588558
  Evidence: ${tc.name} with similar arguments has failed ${cohort.failure}× recently.
588324
588559
  Root cause hypothesis: the argument family may be wrong, a prerequisite may be missing, or the tool is being used before enough state is known.
588325
588560
  Corrective action: try a different approach first: read relevant files, adjust arguments, or verify prerequisites.`);
@@ -588332,7 +588567,7 @@ Corrective action: try a different approach first: read relevant files, adjust a
588332
588567
  const _injKey = `resfix:${_sug.fix.from.join(" ")}=>${_sug.fix.to.join(" ")}`;
588333
588568
  if (!this._errorGuidanceInjected.has(_injKey)) {
588334
588569
  this._errorGuidanceInjected.add(_injKey);
588335
- this.pendingUserMessages.push(_sug.message);
588570
+ this.enqueueRuntimeGuidance(_sug.message);
588336
588571
  this.emit({
588337
588572
  type: "status",
588338
588573
  content: `Resolution memory: suggested \`${_sug.fix.from.join(" ")}\`→\`${_sug.fix.to.join(" ")}\` before shell call`,
@@ -588555,7 +588790,16 @@ Corrective action: try a different approach first: read relevant files, adjust a
588555
588790
  "priority_delegate",
588556
588791
  "background_run"
588557
588792
  ]);
588558
- if (this._reg61PerpetualGateActive && !REG61_BYPASS_TOOLS.has(tc.name) && process.env["OMNIUS_DISABLE_REG61_COERCE"] !== "1") {
588793
+ const focusDirectiveForEditPressure = this._focusDirectiveSuppressesEditPressure();
588794
+ if (this._reg61PerpetualGateActive && focusDirectiveForEditPressure) {
588795
+ this._deferReg61ToFocusDirective({
588796
+ directive: focusDirectiveForEditPressure,
588797
+ turn,
588798
+ source: "latched_steer",
588799
+ detail: `attempted_tool=${tc.name}`
588800
+ });
588801
+ }
588802
+ if (this._reg61PerpetualGateActive && !focusDirectiveForEditPressure && !REG61_BYPASS_TOOLS.has(tc.name) && process.env["OMNIUS_DISABLE_REG61_COERCE"] !== "1") {
588559
588803
  const _dbgLoop = this._detectDebugLoop([
588560
588804
  ...toolCallLog,
588561
588805
  { name: tc.name, argsKey }
@@ -588688,6 +588932,7 @@ Read the current file if needed, make a different concrete edit, or move to veri
588688
588932
  turn,
588689
588933
  toolName: tc.name,
588690
588934
  args: tc.arguments ?? {},
588935
+ actionReason: actionReasonForAdvisory,
588691
588936
  fingerprint: toolFingerprint,
588692
588937
  isReadLike: false,
588693
588938
  stalePreflightMessage: staleEditBlock,
@@ -588753,6 +588998,7 @@ Read the current file if needed, make a different concrete edit, or move to veri
588753
588998
  turn,
588754
588999
  toolName: tc.name,
588755
589000
  args: tc.arguments ?? {},
589001
+ actionReason: actionReasonForAdvisory,
588756
589002
  fingerprint: toolFingerprint,
588757
589003
  isReadLike: false,
588758
589004
  stalePreflightMessage: staleRewriteBlock,
@@ -588818,6 +589064,7 @@ Read the current file if needed, make a different concrete edit, or move to veri
588818
589064
  turn,
588819
589065
  toolName: tc.name,
588820
589066
  args: tc.arguments ?? {},
589067
+ actionReason: actionReasonForAdvisory,
588821
589068
  fingerprint: toolFingerprint,
588822
589069
  isReadLike: false,
588823
589070
  stalePreflightMessage: editReversalBlock,
@@ -588949,7 +589196,11 @@ Read the current file if needed, make a different concrete edit, or move to veri
588949
589196
  decision: "pass",
588950
589197
  reason: "adversary critic disabled for isolated evaluation"
588951
589198
  } : evaluate2({
588952
- proposedCall: { tool: tc.name, args: tc.arguments ?? {} },
589199
+ proposedCall: {
589200
+ tool: tc.name,
589201
+ args: tc.arguments ?? {},
589202
+ actionReason: actionReasonForAdvisory ?? void 0
589203
+ },
588953
589204
  fingerprint: toolFingerprint,
588954
589205
  isReadLike,
588955
589206
  recentToolResults,
@@ -588998,6 +589249,7 @@ Read the current file if needed, make a different concrete edit, or move to veri
588998
589249
  tool: tc.name,
588999
589250
  target: _target,
589000
589251
  count: criticDecision.hitNumber,
589252
+ actionReason: actionReasonForAdvisory ?? void 0,
589001
589253
  alreadyHave: _existingFp?.result
589002
589254
  }
589003
589255
  });
@@ -589079,10 +589331,12 @@ ${cachedResult}`,
589079
589331
  }
589080
589332
  const focusCachedEntry = recentToolResults.get(toolFingerprint);
589081
589333
  const focusDuplicateHits = criticDecision.decision === "guidance" ? criticDecision.hitNumber : dedupHitCount.get(toolFingerprint) ?? 0;
589334
+ const usesAuthoritativeTargetEvidence = this._toolCallUsesFreshFileReadEvidence(tc.name, tc.arguments ?? {});
589082
589335
  const focusDecision = this._focusSupervisor?.evaluateProposedCall({
589083
589336
  turn,
589084
589337
  toolName: tc.name,
589085
589338
  args: tc.arguments ?? {},
589339
+ actionReason: actionReasonForAdvisory,
589086
589340
  completionStatus: completionStatusFromTaskCompleteArgs(tc.arguments),
589087
589341
  fingerprint: toolFingerprint,
589088
589342
  isReadLike,
@@ -589090,7 +589344,8 @@ ${cachedResult}`,
589090
589344
  cachedResult: focusCachedEntry?.result,
589091
589345
  cachedResultFailed: tc.name === "shell" && typeof focusCachedEntry?.result === "string" && focusCachedEntry.result.includes("status: failure"),
589092
589346
  duplicateHitCount: focusDuplicateHits,
589093
- context: this._buildFocusContextSnapshot()
589347
+ context: this._buildFocusContextSnapshot(),
589348
+ usesAuthoritativeTargetEvidence
589094
589349
  });
589095
589350
  if (focusDecision && focusDecision.kind !== "pass") {
589096
589351
  this._emitFocusSupervisorEvent({
@@ -589130,6 +589385,7 @@ ${cachedResult}`,
589130
589385
  const tool = resolvedTool?.tool;
589131
589386
  let result;
589132
589387
  let runtimeSystemGuidance = null;
589388
+ let toolActionReason = actionReasonForAdvisory;
589133
589389
  if (repeatShortCircuit) {
589134
589390
  result = repeatShortCircuit;
589135
589391
  } else if (tc.arguments && "_raw" in tc.arguments) {
@@ -589145,7 +589401,14 @@ ${cachedResult}`,
589145
589401
  error: this.unknownToolError(tc.name)
589146
589402
  };
589147
589403
  } else {
589148
- let validationError = this._validateFreshExpectedHashesForEdit(resolvedTool?.name ?? tc.name, tc.arguments ?? {});
589404
+ let validationError = this._validateToolActionReason(resolvedTool?.name ?? tc.name, tc.arguments ?? {});
589405
+ if (!validationError) {
589406
+ tc.arguments = this._stripToolActionReasonArgs(tc.arguments ?? {});
589407
+ validationError = this._validateFileWriteOverwriteContract(resolvedTool?.name ?? tc.name, tc.arguments ?? {}, toolActionReason);
589408
+ }
589409
+ if (!validationError) {
589410
+ validationError = this._validateFreshExpectedHashesForEdit(resolvedTool?.name ?? tc.name, tc.arguments ?? {});
589411
+ }
589149
589412
  if (!validationError && tool.inputSchema) {
589150
589413
  const parseResult = tool.inputSchema.safeParse(tc.arguments);
589151
589414
  if (!parseResult.success) {
@@ -589213,7 +589476,7 @@ ${cachedResult}`,
589213
589476
  });
589214
589477
  if (this._hookDenyHintCount < 3 && this.tools.has("memory_write")) {
589215
589478
  this._hookDenyHintCount++;
589216
- this.pendingUserMessages.push(`[SYSTEM] Tool "${tc.name}" was blocked: ${hookCheck.reason}. If this constraint should persist across sessions, use memory_write to save it (e.g., memory_write(topic="constraints", key="${tc.name}_blocked", value="${(hookCheck.reason ?? "hook").slice(0, 80)}")).`);
589479
+ this.enqueueRuntimeGuidance(`[SYSTEM] Tool "${tc.name}" was blocked: ${hookCheck.reason}. If this constraint should persist across sessions, use memory_write to save it (e.g., memory_write(topic="constraints", key="${tc.name}_blocked", value="${(hookCheck.reason ?? "hook").slice(0, 80)}")).`);
589217
589480
  }
589218
589481
  } else {
589219
589482
  const finalArgs = hookCheck.modifiedArgs ?? tc.arguments;
@@ -590451,7 +590714,7 @@ ${bookkeepingGuidance}` : bookkeepingGuidance;
590451
590714
  }
590452
590715
  if (this._isAtomicBatchEditAbort(tc.name, result)) {
590453
590716
  editFeedbackRequiredBeforeMoreEdits = this._buildBatchEditAtomicAbortGuidance(tc.arguments);
590454
- this.pendingUserMessages.push(editFeedbackRequiredBeforeMoreEdits);
590717
+ this.enqueueRuntimeGuidance(editFeedbackRequiredBeforeMoreEdits);
590455
590718
  }
590456
590719
  const currentLogEntry = toolCallLog[_toolLogTailIdx];
590457
590720
  if (currentLogEntry) {
@@ -590616,6 +590879,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
590616
590879
  turn,
590617
590880
  toolName: tc.name,
590618
590881
  args: tc.arguments ?? {},
590882
+ actionReason: toolActionReason,
590619
590883
  fingerprint: toolFingerprint,
590620
590884
  success: result.success,
590621
590885
  output: result.output ?? result.llmContent ?? "",
@@ -590624,7 +590888,8 @@ Evidence: ${evidencePreview}`.slice(0, 500);
590624
590888
  isReadLike,
590625
590889
  noop: tc.name === "todo_write" ? this._isTodoWriteNoopResult(result) : result.noop,
590626
590890
  alreadyApplied: result.alreadyApplied === true,
590627
- runtimeAuthored: result.runtimeAuthored === true
590891
+ runtimeAuthored: result.runtimeAuthored === true,
590892
+ usedAuthoritativeTargetEvidence: usesAuthoritativeTargetEvidence
590628
590893
  });
590629
590894
  const focusSnapshotAfter = this._focusSupervisor?.snapshot();
590630
590895
  focusAfterToolResult = focusSnapshotAfter ?? void 0;
@@ -590667,6 +590932,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
590667
590932
  reason: focusDecision.directive.reason,
590668
590933
  requiredNextAction: focusDecision.directive.requiredNextAction
590669
590934
  },
590935
+ actionReason: toolActionReason ?? void 0,
590670
590936
  repeatShortCircuit: Boolean(repeatShortCircuit),
590671
590937
  runtimeAuthored: result.runtimeAuthored === true,
590672
590938
  runtimeGuidance: runtimeSystemGuidance
@@ -590700,7 +590966,7 @@ Evidence: ${evidencePreview}`.slice(0, 500);
590700
590966
  }
590701
590967
  const consecutiveSameTool = Math.max(sameToolFailStreak, this._taskState.failedApproaches.slice(-2).filter((f2) => f2.startsWith(`${tc.name}(`)).length);
590702
590968
  if (sameToolFailStreak >= 5 && (this.options.modelTier === "small" || this.options.modelTier === "medium")) {
590703
- this.pendingUserMessages.push(`[BRANCH — evaluate alternatives before acting]
590969
+ this.enqueueRuntimeGuidance(`[BRANCH — evaluate alternatives before acting]
590704
590970
  Tool "${tc.name}" has failed ${sameToolFailStreak} times. STOP and enumerate:
590705
590971
  Option A: [describe a completely different approach]
590706
590972
  Option B: [describe another alternative]
@@ -590710,7 +590976,7 @@ Pick the BEST option and explain why, then execute it. Do NOT retry ${tc.name} w
590710
590976
  sameToolFailName = null;
590711
590977
  }
590712
590978
  if (consecutiveSameTool >= 2 && (this.options.modelTier === "small" || this.options.modelTier === "medium")) {
590713
- this.pendingUserMessages.push(`[PIVOT REQUIRED] You have failed ${consecutiveSameTool + 1} times in a row with ${tc.name}. Your current approach is not working. You MUST try something fundamentally different:
590979
+ this.enqueueRuntimeGuidance(`[PIVOT REQUIRED] You have failed ${consecutiveSameTool + 1} times in a row with ${tc.name}. Your current approach is not working. You MUST try something fundamentally different:
590714
590980
  - If file_edit keeps failing: re-read the file first, then use the EXACT text from the file
590715
590981
  - If shell keeps failing: try a different command or check prerequisites
590716
590982
  - If grep_search finds nothing: try broader patterns or list_directory
@@ -590723,7 +590989,7 @@ Do NOT retry ${tc.name} with similar arguments.`);
590723
590989
  this._taskState.failedApproaches.shift();
590724
590990
  }
590725
590991
  if (this._taskState.failedApproaches.length === 3 && this.tools.has("memory_write")) {
590726
- this.pendingUserMessages.push(`[SYSTEM] You have ${this._taskState.failedApproaches.length} failed approaches this session. Consider using memory_write to save these failure patterns so you avoid them in future sessions:
590992
+ this.enqueueRuntimeGuidance(`[SYSTEM] You have ${this._taskState.failedApproaches.length} failed approaches this session. Consider using memory_write to save these failure patterns so you avoid them in future sessions:
590727
590993
  ` + this._taskState.failedApproaches.map((f2) => `- ${f2}`).join("\n"));
590728
590994
  }
590729
590995
  }
@@ -590731,7 +590997,7 @@ Do NOT retry ${tc.name} with similar arguments.`);
590731
590997
  sameToolFailStreak = 0;
590732
590998
  sameToolFailName = null;
590733
590999
  if (isGenerationArtifactSuccess(tc.name, result.output ?? "")) {
590734
- this.pendingUserMessages.push(`[GENERATION COMPLETE] ${tc.name} succeeded. Do not call the same generation tool again for the same request. Use the artifact/path from the tool result; if delivery is needed, send it, otherwise call task_complete.`);
591000
+ this.enqueueRuntimeGuidance(`[GENERATION COMPLETE] ${tc.name} succeeded. Do not call the same generation tool again for the same request. Use the artifact/path from the tool result; if delivery is needed, send it, otherwise call task_complete.`);
590735
591001
  }
590736
591002
  }
590737
591003
  if (filePath && (tc.name === "file_read" || tc.name === "file_write" || tc.name === "file_edit" || tc.name === "batch_edit" || tc.name === "file_patch")) {
@@ -590759,7 +591025,7 @@ Do NOT retry ${tc.name} with similar arguments.`);
590759
591025
  if (isModify && (turnTier === "small" || turnTier === "medium")) {
590760
591026
  const modCount = this._taskState.modifiedFiles.size;
590761
591027
  if (modCount >= 2 && modCount % 2 === 0) {
590762
- this.pendingUserMessages.push(`[Test reminder] You've modified ${modCount} files. Run relevant tests NOW to verify: shell(command="npm test") or the project's test command. Fix any failures before continuing.`);
591028
+ this.enqueueRuntimeGuidance(`[Test reminder] You've modified ${modCount} files. Run relevant tests NOW to verify: shell(command="npm test") or the project's test command. Fix any failures before continuing.`);
590763
591029
  }
590764
591030
  }
590765
591031
  }
@@ -590803,7 +591069,7 @@ Do NOT retry ${tc.name} with similar arguments.`);
590803
591069
  }
590804
591070
  if (isEisdir) {
590805
591071
  const failedPath = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
590806
- this.pendingUserMessages.push(`[SYSTEM] "${failedPath}" is a DIRECTORY, not a file. You CANNOT use file_read on directories.
591072
+ this.enqueueRuntimeGuidance(`[SYSTEM] "${failedPath}" is a DIRECTORY, not a file. You CANNOT use file_read on directories.
590807
591073
  Use list_directory("${failedPath}") to see its contents instead.
590808
591074
  Then use file_read on individual FILES inside it.`);
590809
591075
  this._taskState.failedApproaches.push(`file_read("${failedPath}"): EISDIR — this is a directory, use list_directory`);
@@ -590814,7 +591080,7 @@ Then use file_read on individual FILES inside it.`);
590814
591080
  if (this._consecutiveEnoent >= 2 || windowEnoents >= 4) {
590815
591081
  const failedPath = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
590816
591082
  const triggerReason = this._consecutiveEnoent >= 2 ? `${this._consecutiveEnoent} consecutive file-not-found errors` : `${windowEnoents}/8 recent file operations failed`;
590817
- this.pendingUserMessages.push(`[SYSTEM] ${triggerReason} (last: "${failedPath}"). You are repeating failed paths. CHANGE YOUR APPROACH:
591083
+ this.enqueueRuntimeGuidance(`[SYSTEM] ${triggerReason} (last: "${failedPath}"). You are repeating failed paths. CHANGE YOUR APPROACH:
590818
591084
  1. Call list_directory(".") to see what exists at the project root
590819
591085
  2. Entries in a directory listing are RELATIVE to that directory. If you listed "parent/" and saw "child", the full path is "parent/child" — NOT ".child" or just "child"
590820
591086
  3. If an entry is a directory (marked "d"), use list_directory on it, NOT file_read
@@ -591439,7 +591705,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
591439
591705
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591440
591706
  });
591441
591707
  messages2.push({
591442
- role: "user",
591708
+ role: "system",
591443
591709
  content: "You have been reasoning internally for several turns without producing visible output or tool calls. Please take action now — call a tool or produce a visible response." + successHint
591444
591710
  });
591445
591711
  }
@@ -591459,7 +591725,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
591459
591725
  if (shellCommands.length > 0 && shellCommands.length < 2e3) {
591460
591726
  if (looksLikeShellFileWrite(shellCommands)) {
591461
591727
  messages2.push({
591462
- role: "user",
591728
+ role: "system",
591463
591729
  content: `You wrote a shell code block that appears to create or overwrite files. I did NOT auto-execute it because shell redirection bypasses file_write/file_edit/file_patch/batch_edit tracking and version checks. Use file_write/content_base64 for full-file writes, file_edit or batch_edit for exact replacements, or file_patch/new_content_base64 for line-range patches.`
591464
591730
  });
591465
591731
  this.emit({
@@ -591519,7 +591785,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
591519
591785
  } else {
591520
591786
  correction = `CRITICAL: You cannot use ${toolName} by writing text. After ${narratedToolCallCount} attempts, it is clear this approach will not work. Try a COMPLETELY DIFFERENT tool or approach. For example: if trying skill_execute, try shell("cat .omnius/skills/*/SKILL.md") instead. If trying file_read, try shell("cat path/to/file") instead.`;
591521
591787
  }
591522
- messages2.push({ role: "user", content: correction });
591788
+ messages2.push({ role: "system", content: correction });
591523
591789
  this.emit({
591524
591790
  type: "status",
591525
591791
  content: `Narrated tool call #${narratedToolCallCount}: model wrote ${toolName} as text`,
@@ -591528,11 +591794,12 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
591528
591794
  } else {
591529
591795
  narratedToolCallCount = 0;
591530
591796
  messages2.push({
591531
- role: "user",
591797
+ role: "system",
591532
591798
  content: "Continue working. Use tools to read files, make changes, and run validation. Call task_complete when done."
591533
591799
  });
591534
591800
  }
591535
591801
  if (adversaryAddedGuidance) {
591802
+ this.drainPendingRuntimeGuidance(turn);
591536
591803
  while (this.pendingUserMessages.length > 0) {
591537
591804
  const userMsg = this.pendingUserMessages.shift();
591538
591805
  await this.appendInjectedUserMessage(userMsg, messages2, turn);
@@ -591602,7 +591869,7 @@ Your most recent tool calls SUCCEEDED. If the task is complete, call task_comple
591602
591869
  });
591603
591870
  this._reg61CooldownUntilTurn = -1;
591604
591871
  messages2.push({
591605
- role: "user",
591872
+ role: "system",
591606
591873
  content: `[CONTINUATION — Cycle ${bruteForceCycle}]
591607
591874
 
591608
591875
  You have used ${totalTurns} turns and ${toolCallCount} tool calls so far. The task is NOT yet complete. DO NOT give up — re-evaluate and continue:
@@ -591693,11 +591960,12 @@ ${this.options.maxTurns && this.options.maxTurns > 0 ? `You have ${this.options.
591693
591960
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
591694
591961
  });
591695
591962
  messages2.push({
591696
- role: "user",
591963
+ role: "system",
591697
591964
  content: this.buildHealthCheckPrompt(elapsed, toolCallCount, repetitionScore, selfEvalCount)
591698
591965
  });
591699
591966
  nextSelfEval = bfNow + selfEvalInterval;
591700
591967
  }
591968
+ this.drainPendingRuntimeGuidance(turn);
591701
591969
  while (this.pendingUserMessages.length > 0) {
591702
591970
  const userMsg = this.pendingUserMessages.shift();
591703
591971
  await this.appendInjectedUserMessage(userMsg, messages2, turn);
@@ -592039,7 +592307,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592039
592307
  this._consecutiveEnoent++;
592040
592308
  if (this._consecutiveEnoent >= 2) {
592041
592309
  const failedPath2 = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
592042
- this.pendingUserMessages.push(`[SYSTEM] ${this._consecutiveEnoent} consecutive file-not-found errors (last: "${failedPath2}"). You are repeating failed paths. CHANGE YOUR APPROACH:
592310
+ this.enqueueRuntimeGuidance(`[SYSTEM] ${this._consecutiveEnoent} consecutive file-not-found errors (last: "${failedPath2}"). You are repeating failed paths. CHANGE YOUR APPROACH:
592043
592311
  1. Call list_directory(".") to see what exists at the project root
592044
592312
  2. Entries in a directory listing are RELATIVE to that directory. If you listed "parent/" and saw "child", the full path is "parent/child" — NOT ".child" or just "child"
592045
592313
  3. If an entry is a directory (marked "d"), use list_directory on it, NOT file_read
@@ -592176,7 +592444,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592176
592444
  const recentSucc = this._adversaryToolOutcomes.slice(-3).filter((o2) => o2.succeeded);
592177
592445
  const succHint = recentSucc.length > 0 ? "\n\nYour most recent tool calls SUCCEEDED. If the task is complete, call task_complete now with a summary." : "";
592178
592446
  messages2.push({
592179
- role: "user",
592447
+ role: "system",
592180
592448
  content: "You have been reasoning internally for several turns without producing visible output or tool calls. Please take action now — call a tool or produce a visible response." + succHint
592181
592449
  });
592182
592450
  }
@@ -592191,7 +592459,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
592191
592459
  break;
592192
592460
  }
592193
592461
  messages2.push({
592194
- role: "user",
592462
+ role: "system",
592195
592463
  content: "Continue working. Use tools to read files, make changes, and run validation. Call task_complete when done."
592196
592464
  });
592197
592465
  }
@@ -593371,6 +593639,16 @@ ${marker}` : marker);
593371
593639
  return { args, patchedCount: 0 };
593372
593640
  return { args: { ...args, expected_hash: fresh.hash }, patchedCount: 1 };
593373
593641
  }
593642
+ if (toolName === "file_write" || toolName === "file_patch") {
593643
+ if (this._normalizeExpectedHashValue(this._rawExpectedHashValue(args))) {
593644
+ return { args, patchedCount: 0 };
593645
+ }
593646
+ const path12 = this.extractPrimaryToolPath(args);
593647
+ const fresh = path12 ? this._freshReadHashForEditPath(path12) : null;
593648
+ if (!fresh)
593649
+ return { args, patchedCount: 0 };
593650
+ return { args: { ...args, expected_hash: fresh.hash }, patchedCount: 1 };
593651
+ }
593374
593652
  if (toolName !== "batch_edit" || !Array.isArray(args["edits"])) {
593375
593653
  return { args, patchedCount: 0 };
593376
593654
  }
@@ -593391,6 +593669,151 @@ ${marker}` : marker);
593391
593669
  });
593392
593670
  return patchedCount > 0 ? { args: { ...args, edits }, patchedCount } : { args, patchedCount: 0 };
593393
593671
  }
593672
+ _toolCallUsesFreshFileReadEvidence(toolName, args) {
593673
+ if (toolName !== "file_write" && toolName !== "file_edit" && toolName !== "file_patch" && toolName !== "batch_edit") {
593674
+ return false;
593675
+ }
593676
+ const matchesFresh = (rec) => {
593677
+ const path12 = typeof rec["path"] === "string" ? rec["path"] : typeof rec["file"] === "string" ? rec["file"] : "";
593678
+ if (!path12)
593679
+ return false;
593680
+ const expectedHash = this._normalizeExpectedHashValue(this._rawExpectedHashValue(rec));
593681
+ if (!expectedHash)
593682
+ return false;
593683
+ const fresh = this._freshReadHashForEditPath(path12);
593684
+ return fresh?.hash === expectedHash;
593685
+ };
593686
+ if (toolName === "batch_edit") {
593687
+ const edits = args["edits"];
593688
+ return Array.isArray(edits) && edits.length > 0 && edits.every((edit) => !!edit && typeof edit === "object" && !Array.isArray(edit) && matchesFresh(edit));
593689
+ }
593690
+ return matchesFresh(args);
593691
+ }
593692
+ _requiresToolActionReason(toolName) {
593693
+ if (process.env["OMNIUS_DISABLE_TOOL_ACTION_REASON"] === "1") {
593694
+ return false;
593695
+ }
593696
+ if ((process.env["VITEST"] || process.env["NODE_ENV"] === "test") && process.env["OMNIUS_REQUIRE_TOOL_ACTION_REASON"] !== "1") {
593697
+ return false;
593698
+ }
593699
+ if (this.options.artifactMode === "internal")
593700
+ return false;
593701
+ if (toolName.startsWith("__"))
593702
+ return false;
593703
+ return true;
593704
+ }
593705
+ _withToolActionReasonSchema(toolName, parameters) {
593706
+ if (!this._requiresToolActionReason(toolName)) {
593707
+ return parameters ?? { type: "object", properties: {} };
593708
+ }
593709
+ const base3 = parameters && typeof parameters === "object" && !Array.isArray(parameters) ? { ...parameters } : { type: "object" };
593710
+ const properties = base3["properties"] && typeof base3["properties"] === "object" && !Array.isArray(base3["properties"]) ? { ...base3["properties"] } : {};
593711
+ properties["action_reason"] = TOOL_ACTION_REASON_SCHEMA;
593712
+ const required = Array.isArray(base3["required"]) ? base3["required"].map(String) : [];
593713
+ return {
593714
+ ...base3,
593715
+ type: "object",
593716
+ properties,
593717
+ required: [.../* @__PURE__ */ new Set([...required, "action_reason"])]
593718
+ };
593719
+ }
593720
+ _extractToolActionReason(args) {
593721
+ const raw = args["action_reason"] ?? args["actionReason"] ?? args["justification"];
593722
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
593723
+ return null;
593724
+ const rec = raw;
593725
+ const reason = {
593726
+ task_anchor: String(rec["task_anchor"] ?? rec["taskAnchor"] ?? "").trim(),
593727
+ intent: String(rec["intent"] ?? "").trim(),
593728
+ evidence: String(rec["evidence"] ?? "").trim(),
593729
+ expected_result: String(rec["expected_result"] ?? rec["expectedResult"] ?? "").trim(),
593730
+ scope: String(rec["scope"] ?? "").trim()
593731
+ };
593732
+ return reason;
593733
+ }
593734
+ _validateToolActionReason(toolName, args) {
593735
+ if (!this._requiresToolActionReason(toolName))
593736
+ return null;
593737
+ const raw = args["action_reason"] ?? args["actionReason"] ?? args["justification"];
593738
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
593739
+ return [
593740
+ "[TOOL ACTION REASON CONTRACT]",
593741
+ `Every tool call must include action_reason: { task_anchor, intent, evidence, expected_result, scope }.`,
593742
+ `Use compact public facts only; do not include hidden chain-of-thought.`,
593743
+ `scope must be one of: ${Array.from(TOOL_ACTION_REASON_SCOPES).join(", ")}.`
593744
+ ].join("\n");
593745
+ }
593746
+ const reason = this._extractToolActionReason(args);
593747
+ if (!reason) {
593748
+ return "[TOOL ACTION REASON CONTRACT] action_reason must be an object with compact public task-state fields.";
593749
+ }
593750
+ const missing = [
593751
+ "task_anchor",
593752
+ "intent",
593753
+ "evidence",
593754
+ "expected_result",
593755
+ "scope"
593756
+ ].filter((key) => reason[key].length < 3);
593757
+ if (missing.length > 0) {
593758
+ return `[TOOL ACTION REASON CONTRACT] action_reason missing non-empty field(s): ${missing.join(", ")}.`;
593759
+ }
593760
+ if (!TOOL_ACTION_REASON_SCOPES.has(reason.scope)) {
593761
+ return `[TOOL ACTION REASON CONTRACT] action_reason.scope="${reason.scope}" is invalid. Use one of: ${Array.from(TOOL_ACTION_REASON_SCOPES).join(", ")}.`;
593762
+ }
593763
+ return null;
593764
+ }
593765
+ _stripToolActionReasonArgs(args) {
593766
+ let changed = false;
593767
+ const next = {};
593768
+ for (const [key, value2] of Object.entries(args)) {
593769
+ if (TOOL_ACTION_REASON_KEYS.has(key)) {
593770
+ changed = true;
593771
+ continue;
593772
+ }
593773
+ next[key] = value2;
593774
+ }
593775
+ return changed ? next : args;
593776
+ }
593777
+ _validateFileWriteOverwriteContract(toolName, args, actionReason) {
593778
+ if (toolName !== "file_write")
593779
+ return null;
593780
+ if (process.env["OMNIUS_ALLOW_UNVERIFIED_FILE_WRITE_OVERWRITE"] === "1") {
593781
+ return null;
593782
+ }
593783
+ if (args["dry_run"] === true || args["dryRun"] === true)
593784
+ return null;
593785
+ const path12 = this.extractPrimaryToolPath(args);
593786
+ if (!path12)
593787
+ return null;
593788
+ let exists2 = false;
593789
+ try {
593790
+ const st = _fsStatSync(this._absoluteToolPath(path12), {
593791
+ throwIfNoEntry: false
593792
+ });
593793
+ exists2 = !!st && st.isFile();
593794
+ } catch {
593795
+ exists2 = false;
593796
+ }
593797
+ if (!exists2)
593798
+ return null;
593799
+ if (actionReason && actionReason.scope !== "full_file_rewrite") {
593800
+ return [
593801
+ "[FULL FILE REWRITE CONTRACT]",
593802
+ `file_write targets an existing file: ${path12}.`,
593803
+ `For constrained repairs, use file_patch, batch_edit, or file_edit instead of rewriting the full file.`,
593804
+ `If a whole-file replacement is truly intended, set action_reason.scope="full_file_rewrite" and use expected_hash from a fresh file_read.`
593805
+ ].join("\n");
593806
+ }
593807
+ if (!this._toolCallUsesFreshFileReadEvidence(toolName, args)) {
593808
+ const fresh = this._freshReadHashForEditPath(path12);
593809
+ return [
593810
+ "[FULL FILE REWRITE CONTRACT]",
593811
+ `file_write would overwrite existing file ${path12}, but it is not anchored to fresh file_read evidence.`,
593812
+ fresh ? `Use expected_hash=${fresh.hash} from the current active evidence, or prefer file_patch/batch_edit for a constrained change.` : `First file_read ${path12}, then prefer file_patch/batch_edit; only retry file_write with expected_hash if a whole-file rewrite is necessary.`
593813
+ ].join("\n");
593814
+ }
593815
+ return null;
593816
+ }
593394
593817
  _validateFreshExpectedHashesForEdit(toolName, args) {
593395
593818
  if (process.env["OMNIUS_ALLOW_UNVERIFIED_OLD_STRING_EDIT"] === "1") {
593396
593819
  return null;
@@ -593747,11 +594170,20 @@ ${result.output}`, "utf-8");
593747
594170
  const folded = this.foldOutput(modelContent, maxLen);
593748
594171
  return this.wrapToolOutputForModel(toolName, `[Tool output truncated — ${result.output.length} chars, ${lineCount} lines]
593749
594172
  Full output saved to: ${savedPath}
593750
- To read the complete output, use file_read with path="${savedPath}".
594173
+ ${this.truncatedToolOutputAccessHint(savedPath)}
593751
594174
 
593752
594175
  Truncated preview (beginning + end):
593753
594176
  ${folded}`);
593754
594177
  }
594178
+ truncatedToolOutputAccessHint(savedPath) {
594179
+ if (this.tools.has("file_read")) {
594180
+ return `To read the complete output, use file_read with path="${savedPath}".`;
594181
+ }
594182
+ if (this.tools.has("shell")) {
594183
+ return `To inspect the complete output, use shell with a read-only command for ${JSON.stringify(savedPath)}.`;
594184
+ }
594185
+ return `No file-reading tool is available in this agent role. Do not call file_read for this saved path; use the truncated preview, or report that the full payload is archived at ${savedPath}.`;
594186
+ }
593755
594187
  wrapToolOutputForModel(toolName, output) {
593756
594188
  if (toolName === "task_complete" || output.startsWith("[trust_tier:")) {
593757
594189
  return output;
@@ -594221,6 +594653,50 @@ ${tail}`;
594221
594653
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
594222
594654
  });
594223
594655
  }
594656
+ drainPendingRuntimeGuidance(turn) {
594657
+ while (this.pendingRuntimeGuidanceMessages.length > 0) {
594658
+ const guidance = this.pendingRuntimeGuidanceMessages.shift();
594659
+ this.appendRuntimeGuidance(guidance, turn);
594660
+ }
594661
+ }
594662
+ appendRuntimeGuidance(guidance, turn) {
594663
+ const packet = this.formatRuntimeGuidance(guidance);
594664
+ const guidanceHash = _createHash("sha256").update(packet).digest("hex").slice(0, 16);
594665
+ const signal = signalFromBlock("runtime_guidance", "runtime.guidance", packet, {
594666
+ id: `runtime-guidance-${turn}-${guidanceHash}`,
594667
+ dedupeKey: `runtime.guidance:${guidanceHash}`,
594668
+ priority: 94,
594669
+ createdTurn: turn,
594670
+ ttlTurns: 8
594671
+ });
594672
+ if (signal)
594673
+ this._contextLedger.upsert(signal);
594674
+ this.emit({
594675
+ type: "status",
594676
+ content: `Runtime guidance queued: ${guidance.replace(/\s+/g, " ").slice(0, 140)}`,
594677
+ turn,
594678
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
594679
+ });
594680
+ }
594681
+ formatRuntimeGuidance(guidance) {
594682
+ if (guidance.trim().startsWith("[RUNTIME_GUIDANCE_INTAKE")) {
594683
+ return guidance;
594684
+ }
594685
+ return [
594686
+ "[RUNTIME_GUIDANCE_INTAKE v1]",
594687
+ "Source: Omnius runtime control/advisory signal, not an external user message.",
594688
+ "",
594689
+ "<<<RUNTIME_GUIDANCE>>>",
594690
+ guidance,
594691
+ "<<<END_RUNTIME_GUIDANCE>>>",
594692
+ "",
594693
+ "Contract:",
594694
+ "- Treat this as diagnostic/control-plane guidance for the next action.",
594695
+ "- Reconcile it with the user goal and fresh tool evidence.",
594696
+ "- Do not quote or summarize this wrapper to the user.",
594697
+ "[/RUNTIME_GUIDANCE_INTAKE]"
594698
+ ].join("\n");
594699
+ }
594224
594700
  formatInjectedUserMessage(userMsg) {
594225
594701
  if (userMsg.trim().startsWith("[MID_TASK_STEERING_INTAKE")) {
594226
594702
  return userMsg;
@@ -595306,6 +595782,7 @@ ${trimmedNew}`;
595306
595782
  args = JSON.parse(tc.function.arguments);
595307
595783
  } catch {
595308
595784
  }
595785
+ const actionReason = this._extractToolActionReason(args);
595309
595786
  const argsKey = this._buildExactArgsKey(args);
595310
595787
  const fingerprint = this._buildToolFingerprint(name10, args);
595311
595788
  const prior = this._adversaryToolOutcomes.find((o2) => o2.succeeded && o2.tool === name10 && o2.fingerprint === fingerprint && o2.stateVersion === this._adversaryStateVersion && o2.turn < turn);
@@ -595319,6 +595796,7 @@ ${trimmedNew}`;
595319
595796
  tool: name10,
595320
595797
  target: argsKey.slice(0, 180),
595321
595798
  count: repeatCount,
595799
+ actionReason: actionReason ?? void 0,
595322
595800
  alreadyHave: prior.preview
595323
595801
  }
595324
595802
  }, `possible repeated action ${name10} after prior same-state success`);
@@ -596552,7 +597030,7 @@ Example: ${tool.name}(${JSON.stringify(meta.examples[0].args ?? {})})` : "";
596552
597030
  function: {
596553
597031
  name: tool.name,
596554
597032
  description: compressDesc ? (desc.split(/\.\s/)[0]?.slice(0, 120) ?? desc.slice(0, 120)) + "." : desc,
596555
- parameters: tool.parameters
597033
+ parameters: this._withToolActionReasonSchema(tool.name, tool.parameters)
596556
597034
  }
596557
597035
  };
596558
597036
  });
@@ -596574,7 +597052,7 @@ Example: ${tool.name}(${JSON.stringify(meta.examples[0].args ?? {})})` : "";
596574
597052
 
596575
597053
  Available tools (${deferred.length}):
596576
597054
  ${catalog}`,
596577
- parameters: {
597055
+ parameters: this._withToolActionReasonSchema("tool_search", {
596578
597056
  type: "object",
596579
597057
  properties: {
596580
597058
  query: {
@@ -596583,7 +597061,7 @@ ${catalog}`,
596583
597061
  }
596584
597062
  },
596585
597063
  required: ["query"]
596586
- }
597064
+ })
596587
597065
  }
596588
597066
  });
596589
597067
  const activatedToolsRef = this._activatedTools;
@@ -596625,7 +597103,7 @@ ${catalog}`,
596625
597103
  lines.push(`Aliases: ${tool.aliases.join(", ")}`);
596626
597104
  }
596627
597105
  lines.push(`${getDesc(tool)}${customToolDetails(tool)}`);
596628
- lines.push(`Parameters: ${JSON.stringify(tool.parameters)}`);
597106
+ lines.push(`Parameters: ${JSON.stringify(this._withToolActionReasonSchema(tool.name, tool.parameters))}`);
596629
597107
  }
596630
597108
  }
596631
597109
  if (alreadyAvailable.length > 0) {
@@ -596677,7 +597155,7 @@ ${catalog}`,
596677
597155
  for (const t2 of matches)
596678
597156
  activatedToolsRef.add(t2.name);
596679
597157
  const result = matches.map((t2) => {
596680
- const paramsStr = JSON.stringify(t2.parameters, null, 2);
597158
+ const paramsStr = JSON.stringify(this._withToolActionReasonSchema(t2.name, t2.parameters), null, 2);
596681
597159
  const aliases = t2.aliases?.length ? `
596682
597160
  Aliases: ${t2.aliases.join(", ")}` : "";
596683
597161
  return `## ${t2.name}${aliases}
@@ -743079,13 +743557,15 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
743079
743557
  },
743080
743558
  savePendingTaskState() {
743081
743559
  if (lastSubmittedPrompt && activeTask) {
743560
+ const activeToolCallCount = activeTask.toolCallCount;
743561
+ const activeFilesTouched = Array.from(activeTask.filesTouched);
743082
743562
  savePendingTask(repoRoot, {
743083
743563
  prompt: lastSubmittedPrompt,
743084
- progressSummary: `Task paused by user. ${sessionToolCallCount} tool calls completed.`,
743085
- filesModified: sessionFilesTouched,
743564
+ progressSummary: `Task paused by user. ${activeToolCallCount} tool calls completed.`,
743565
+ filesModified: activeFilesTouched,
743086
743566
  bruteForce: bruteForceEnabled,
743087
743567
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
743088
- toolCallCount: sessionToolCallCount
743568
+ toolCallCount: activeToolCallCount
743089
743569
  });
743090
743570
  return true;
743091
743571
  }