open-agents-ai 0.187.499 → 0.187.501

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
@@ -3986,6 +3986,91 @@ ${JSON.stringify(extracted, null, 2)}`);
3986
3986
  }
3987
3987
  });
3988
3988
 
3989
+ // packages/execution/dist/tools/edit-snippet-finder.js
3990
+ function findClosestSnippet(content, oldString, contextLines = 5) {
3991
+ const lines = content.split("\n");
3992
+ if (lines.length === 0)
3993
+ return null;
3994
+ const oldLines = oldString.split("\n").map((l2) => l2.trim()).filter((l2) => l2.length > 0);
3995
+ if (oldLines.length === 0)
3996
+ return null;
3997
+ const anchor = oldLines.reduce((best, l2) => l2.length > best.length ? l2 : best, oldLines[0]);
3998
+ let bestIdx = 0;
3999
+ let bestScore = -1;
4000
+ for (let i2 = 0; i2 < lines.length; i2++) {
4001
+ const score = diceSimilarity(anchor, lines[i2].trim());
4002
+ if (score > bestScore) {
4003
+ bestScore = score;
4004
+ bestIdx = i2;
4005
+ }
4006
+ }
4007
+ const startIdx = Math.max(0, bestIdx - contextLines);
4008
+ const endIdx = Math.min(lines.length - 1, bestIdx + contextLines);
4009
+ const snippetLines = [];
4010
+ for (let i2 = startIdx; i2 <= endIdx; i2++) {
4011
+ const lineNum = i2 + 1;
4012
+ const marker = i2 === bestIdx ? ">" : " ";
4013
+ snippetLines.push(`${marker} ${lineNum.toString().padStart(4)} | ${lines[i2]}`);
4014
+ }
4015
+ return {
4016
+ startLine: startIdx + 1,
4017
+ endLine: endIdx + 1,
4018
+ content: snippetLines.join("\n"),
4019
+ similarity: Math.max(0, bestScore),
4020
+ totalLines: lines.length
4021
+ };
4022
+ }
4023
+ function snippetAtOffset(content, charOffset, contextLines = 3) {
4024
+ const lines = content.split("\n");
4025
+ const before = content.slice(0, charOffset);
4026
+ const lineNumber = before.split("\n").length;
4027
+ const idx = lineNumber - 1;
4028
+ const startIdx = Math.max(0, idx - contextLines);
4029
+ const endIdx = Math.min(lines.length - 1, idx + contextLines);
4030
+ const out = [];
4031
+ for (let i2 = startIdx; i2 <= endIdx; i2++) {
4032
+ const marker = i2 === idx ? ">" : " ";
4033
+ out.push(`${marker} ${(i2 + 1).toString().padStart(4)} | ${lines[i2]}`);
4034
+ }
4035
+ return { lineNumber, content: out.join("\n") };
4036
+ }
4037
+ function diceSimilarity(a2, b) {
4038
+ if (!a2.length && !b.length)
4039
+ return 1;
4040
+ if (!a2.length || !b.length)
4041
+ return 0;
4042
+ if (a2 === b)
4043
+ return 1;
4044
+ if (a2.length < 2 || b.length < 2) {
4045
+ return a2 === b ? 1 : 0;
4046
+ }
4047
+ const bigramsA = bigrams(a2);
4048
+ const bigramsB = bigrams(b);
4049
+ let intersection = 0;
4050
+ for (const bg of bigramsB.keys()) {
4051
+ const count = Math.min(bigramsA.get(bg) ?? 0, bigramsB.get(bg));
4052
+ if (count > 0) {
4053
+ intersection += count;
4054
+ bigramsA.set(bg, (bigramsA.get(bg) ?? 0) - count);
4055
+ }
4056
+ }
4057
+ const total = a2.length - 1 + (b.length - 1);
4058
+ return total > 0 ? 2 * intersection / total : 0;
4059
+ }
4060
+ function bigrams(s2) {
4061
+ const map2 = /* @__PURE__ */ new Map();
4062
+ for (let i2 = 0; i2 < s2.length - 1; i2++) {
4063
+ const bg = s2.slice(i2, i2 + 2);
4064
+ map2.set(bg, (map2.get(bg) ?? 0) + 1);
4065
+ }
4066
+ return map2;
4067
+ }
4068
+ var init_edit_snippet_finder = __esm({
4069
+ "packages/execution/dist/tools/edit-snippet-finder.js"() {
4070
+ "use strict";
4071
+ }
4072
+ });
4073
+
3989
4074
  // packages/execution/dist/tools/file-edit.js
3990
4075
  import { readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
3991
4076
  import { resolve as resolve6 } from "node:path";
@@ -4016,6 +4101,15 @@ function findMatchLines(haystack, needle) {
4016
4101
  }
4017
4102
  return lines;
4018
4103
  }
4104
+ function findMatchOffsets(haystack, needle) {
4105
+ const offsets = [];
4106
+ let pos = 0;
4107
+ while ((pos = haystack.indexOf(needle, pos)) !== -1) {
4108
+ offsets.push(pos);
4109
+ pos += needle.length;
4110
+ }
4111
+ return offsets;
4112
+ }
4019
4113
  function replaceAllOccurrences(haystack, needle, replacement) {
4020
4114
  return haystack.split(needle).join(replacement);
4021
4115
  }
@@ -4024,6 +4118,7 @@ var init_file_edit = __esm({
4024
4118
  "packages/execution/dist/tools/file-edit.js"() {
4025
4119
  "use strict";
4026
4120
  init_change_log();
4121
+ init_edit_snippet_finder();
4027
4122
  FileEditTool = class {
4028
4123
  name = "file_edit";
4029
4124
  description = "Make a precise edit to a file by replacing an exact string match. The old_string must be unique in the file unless replace_all is true. Use replace_all to rename variables or change repeated patterns throughout the file.";
@@ -4085,19 +4180,42 @@ var init_file_edit = __esm({
4085
4180
  const content = await readFile3(fullPath, "utf-8");
4086
4181
  const occurrences = countOccurrences(content, oldString);
4087
4182
  if (occurrences === 0) {
4183
+ const snippet = findClosestSnippet(content, oldString, 5);
4184
+ let errorMsg = `old_string not found in ${filePath}.`;
4185
+ if (snippet) {
4186
+ const pct = Math.round(snippet.similarity * 100);
4187
+ errorMsg += `
4188
+
4189
+ Current file content (closest match, ${pct}% similarity, lines ${snippet.startLine}–${snippet.endLine} of ${snippet.totalLines}):
4190
+ ${snippet.content}
4191
+
4192
+ Use the EXACT current content above to construct a working old_string. Do not retry with a different guess — the file on disk has changed since you last read it.`;
4193
+ } else {
4194
+ errorMsg += ` The file is empty or binary. Use file_read to inspect.`;
4195
+ }
4088
4196
  return {
4089
4197
  success: false,
4090
4198
  output: "",
4091
- error: `old_string not found in ${filePath}. Read the file first to verify the exact content.`,
4199
+ error: errorMsg,
4092
4200
  durationMs: performance.now() - start2
4093
4201
  };
4094
4202
  }
4095
4203
  if (!replaceAll && occurrences > 1) {
4096
4204
  const matchLines = findMatchLines(content, oldString);
4205
+ const offsets = findMatchOffsets(content, oldString);
4206
+ const snippets = offsets.slice(0, 4).map((off) => {
4207
+ const s2 = snippetAtOffset(content, off, 3);
4208
+ return `
4209
+ --- match at line ${s2.lineNumber} ---
4210
+ ${s2.content}`;
4211
+ }).join("\n");
4097
4212
  return {
4098
4213
  success: false,
4099
4214
  output: "",
4100
- error: `old_string is not unique — found ${occurrences} occurrences at lines ${matchLines.join(", ")}. Either include more surrounding context to make old_string unique, or set replace_all=true to replace all ${occurrences} occurrences.`,
4215
+ error: `old_string is not unique — found ${occurrences} occurrences at lines ${matchLines.join(", ")}.
4216
+ ${snippets}
4217
+
4218
+ Add UNIQUE surrounding context (a function name, distinctive comment, or unique line above/below) to disambiguate. Or set replace_all=true if you want all ${occurrences} occurrences replaced identically.`,
4101
4219
  durationMs: performance.now() - start2
4102
4220
  };
4103
4221
  }
@@ -5595,6 +5713,15 @@ var init_aiwg_workflow = __esm({
5595
5713
  // packages/execution/dist/tools/batch-edit.js
5596
5714
  import { readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
5597
5715
  import { resolve as resolve11 } from "node:path";
5716
+ function findMatchOffsetsBatch(haystack, needle) {
5717
+ const offsets = [];
5718
+ let pos = 0;
5719
+ while ((pos = haystack.indexOf(needle, pos)) !== -1) {
5720
+ offsets.push(pos);
5721
+ pos += needle.length;
5722
+ }
5723
+ return offsets;
5724
+ }
5598
5725
  function countOccurrences2(haystack, needle) {
5599
5726
  let count = 0;
5600
5727
  let pos = 0;
@@ -5609,6 +5736,7 @@ var init_batch_edit = __esm({
5609
5736
  "packages/execution/dist/tools/batch-edit.js"() {
5610
5737
  "use strict";
5611
5738
  init_change_log();
5739
+ init_edit_snippet_finder();
5612
5740
  BatchEditTool = class {
5613
5741
  name = "batch_edit";
5614
5742
  description = "Make multiple precise edits across one or more files in a single call. More efficient than calling file_edit repeatedly. Each edit replaces an exact string match with uniqueness validation. Edits are applied in order within each file. Set replace_all on individual edits for bulk renames.";
@@ -5680,12 +5808,29 @@ var init_batch_edit = __esm({
5680
5808
  for (const edit of fileEdits) {
5681
5809
  const occurrences = countOccurrences2(content, edit.old_string);
5682
5810
  if (occurrences === 0) {
5683
- results.push(`SKIP: old_string not found in ${edit.relPath}`);
5811
+ const snippet = findClosestSnippet(content, edit.old_string, 5);
5812
+ if (snippet) {
5813
+ const pct = Math.round(snippet.similarity * 100);
5814
+ results.push(`SKIP: old_string not found in ${edit.relPath}.
5815
+ Closest match in current file (${pct}% similar, lines ${snippet.startLine}–${snippet.endLine} of ${snippet.totalLines}):
5816
+ ${snippet.content}
5817
+ Use this exact content for old_string in your next attempt.`);
5818
+ } else {
5819
+ results.push(`SKIP: old_string not found in ${edit.relPath} (file empty or binary)`);
5820
+ }
5684
5821
  failCount++;
5685
5822
  continue;
5686
5823
  }
5687
5824
  if (!edit.replace_all && occurrences > 1) {
5688
- results.push(`AMBIGUOUS: old_string has ${occurrences} matches in ${edit.relPath} — add more context or use replace_all`);
5825
+ const offsets = findMatchOffsetsBatch(content, edit.old_string).slice(0, 4);
5826
+ const snippets = offsets.map((off) => {
5827
+ const s2 = snippetAtOffset(content, off, 3);
5828
+ return `
5829
+ --- match at line ${s2.lineNumber} ---
5830
+ ${s2.content}`;
5831
+ }).join("\n");
5832
+ results.push(`AMBIGUOUS: old_string has ${occurrences} matches in ${edit.relPath}.${snippets}
5833
+ Add UNIQUE surrounding context, or use replace_all=true.`);
5689
5834
  failCount++;
5690
5835
  continue;
5691
5836
  }
@@ -515154,6 +515299,257 @@ var init_stuck_meta_analyzer = __esm({
515154
515299
  }
515155
515300
  });
515156
515301
 
515302
+ // packages/orchestrator/dist/problem-frame-validator.js
515303
+ function renderFrameValidatorPrompt(inputs) {
515304
+ const lines = [];
515305
+ lines.push(`# PROBLEM-FRAME VALIDATION`);
515306
+ lines.push(``);
515307
+ lines.push(`You are a META-STRATEGY VALIDATOR. Another agent (the implementer)`);
515308
+ lines.push(`is working on a task. You are NOT here to suggest a next tool call.`);
515309
+ lines.push(`You are here to ask the deeper question:`);
515310
+ lines.push(``);
515311
+ lines.push(` > "Is the implementer solving the right problem?"`);
515312
+ lines.push(``);
515313
+ lines.push(`Specifically, evaluate three things:`);
515314
+ lines.push(``);
515315
+ lines.push(` 1. **Goal-vs-subtask alignment**: does the current sub-task`);
515316
+ lines.push(` genuinely contribute to the original goal, or has the agent`);
515317
+ lines.push(` drifted into a side-quest that doesn't matter?`);
515318
+ lines.push(``);
515319
+ lines.push(` 2. **Frame ambition**: is the current frame too ambitious for`);
515320
+ lines.push(` the implementer's context? Is there a simpler version of`);
515321
+ lines.push(` the same sub-task that would still satisfy the goal?`);
515322
+ lines.push(``);
515323
+ lines.push(` 3. **Productive blockedness**: if the agent has been stuck on`);
515324
+ lines.push(` this sub-task for a while, is there a fundamentally`);
515325
+ lines.push(` different approach that would unblock progress, OR is`);
515326
+ lines.push(` the right move to declare this specific sub-task blocked`);
515327
+ lines.push(` and move on / escalate to the user?`);
515328
+ lines.push(``);
515329
+ lines.push(`## Goal (the immutable anchor)`);
515330
+ lines.push(inputs.goal.slice(0, 800));
515331
+ lines.push(``);
515332
+ lines.push(`## Working directory`);
515333
+ lines.push(inputs.workingDir);
515334
+ lines.push(``);
515335
+ lines.push(`## Current sub-task`);
515336
+ if (inputs.currentSubtask.trim().length > 0) {
515337
+ lines.push(inputs.currentSubtask.slice(0, 400));
515338
+ } else {
515339
+ lines.push(`(no in-progress sub-task identified — agent may be drifting)`);
515340
+ }
515341
+ lines.push(``);
515342
+ lines.push(`## Plan status`);
515343
+ if (inputs.planStatus.length === 0) {
515344
+ lines.push(`(no plan items declared)`);
515345
+ } else {
515346
+ for (const t2 of inputs.planStatus.slice(0, 12)) {
515347
+ lines.push(` [${t2.status}] ${t2.content.slice(0, 100)}`);
515348
+ }
515349
+ }
515350
+ lines.push(``);
515351
+ lines.push(`## Recent activity summary`);
515352
+ lines.push(inputs.recentActivitySummary.slice(0, 2e3));
515353
+ lines.push(``);
515354
+ lines.push(`## Triggers`);
515355
+ lines.push(` - Trigger reason: ${inputs.triggerReason}`);
515356
+ lines.push(` - Stuck-event count this run: ${inputs.recentStuckCount}`);
515357
+ lines.push(` - Turn: ${inputs.turn}`);
515358
+ lines.push(``);
515359
+ lines.push(`## Your verdict — pick ONE`);
515360
+ lines.push(``);
515361
+ lines.push(`Choose the verdict that best matches the situation:`);
515362
+ lines.push(``);
515363
+ lines.push(` - **continue**: the current frame is right. The agent should`);
515364
+ lines.push(` keep going. Use this when goal-subtask alignment is good`);
515365
+ lines.push(` and progress is reasonable for the difficulty.`);
515366
+ lines.push(``);
515367
+ lines.push(` - **simplify**: the current frame is correct in direction but`);
515368
+ lines.push(` too ambitious. Suggest a SMALLER version of the same`);
515369
+ lines.push(` sub-task that still satisfies the goal. Common case:`);
515370
+ lines.push(` agent is over-engineering a config when defaults would`);
515371
+ lines.push(` work, or writing comprehensive tests when 1-2 smoke tests`);
515372
+ lines.push(` would unblock.`);
515373
+ lines.push(``);
515374
+ lines.push(` - **pivot**: the current frame is WRONG. The sub-task the`);
515375
+ lines.push(` agent is on does not contribute to the goal, or there is`);
515376
+ lines.push(` a fundamentally better approach. Common case: agent is`);
515377
+ lines.push(` fixing a custom config that should be deleted; agent is`);
515378
+ lines.push(` writing tests against an API that needs changing, not`);
515379
+ lines.push(` accommodating.`);
515380
+ lines.push(``);
515381
+ lines.push(` - **declare-blocked**: there is no productive frame from this`);
515382
+ lines.push(` state. The current sub-task genuinely cannot be completed`);
515383
+ lines.push(` with the current information; the agent should call`);
515384
+ lines.push(` task_complete with the specific blocker named (NOT a`);
515385
+ lines.push(` polite "looks done"). Reserve for genuine dead-ends.`);
515386
+ lines.push(``);
515387
+ lines.push(`Do NOT default to continue out of politeness. If the recent`);
515388
+ lines.push(`activity shows the agent spinning on a sub-problem (especially`);
515389
+ lines.push(`if stuck-event count > 1), seriously consider whether simplify`);
515390
+ lines.push(`or pivot would help.`);
515391
+ lines.push(``);
515392
+ lines.push(`## Output format`);
515393
+ lines.push(``);
515394
+ lines.push(`Reason briefly (1-3 sentences) about goal alignment, then emit`);
515395
+ lines.push(`a SINGLE JSON code block with this exact shape:`);
515396
+ lines.push(``);
515397
+ lines.push("```json");
515398
+ lines.push(`{`);
515399
+ lines.push(` "verdict": "continue" | "simplify" | "pivot" | "declare-blocked",`);
515400
+ lines.push(` "rationale": "<1-2 sentences>",`);
515401
+ lines.push(` "recommended_frame": { /* OMIT for continue and declare-blocked */`);
515402
+ lines.push(` "new_subtask": "<concrete description of the better sub-task>",`);
515403
+ lines.push(` "why_better": "<why this frame is more tractable / aligned>",`);
515404
+ lines.push(` "success_criterion": "<what 'done' looks like for the new frame>"`);
515405
+ lines.push(` },`);
515406
+ lines.push(` "blocker_summary": "<for declare-blocked only — name the specific blocker>"`);
515407
+ lines.push(`}`);
515408
+ lines.push("```");
515409
+ lines.push(``);
515410
+ lines.push(`Be SPECIFIC. Vague verdicts are useless.`);
515411
+ return lines.join("\n");
515412
+ }
515413
+ function parseFrameVerdict(rawResponse) {
515414
+ const fallback = (msg) => ({
515415
+ verdict: "continue",
515416
+ // Safe default: don't disrupt agent on parser error
515417
+ rationale: `(frame-validator parse failed: ${msg})`,
515418
+ raw: rawResponse,
515419
+ parseFallback: true
515420
+ });
515421
+ if (!rawResponse || typeof rawResponse !== "string" || rawResponse.trim().length === 0) {
515422
+ return fallback("empty response");
515423
+ }
515424
+ const fenceMatch = rawResponse.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
515425
+ let jsonText = null;
515426
+ if (fenceMatch) {
515427
+ jsonText = fenceMatch[1].trim();
515428
+ } else {
515429
+ const first2 = rawResponse.indexOf("{");
515430
+ const last2 = rawResponse.lastIndexOf("}");
515431
+ if (first2 !== -1 && last2 > first2)
515432
+ jsonText = rawResponse.slice(first2, last2 + 1);
515433
+ }
515434
+ if (!jsonText)
515435
+ return fallback("no JSON block found");
515436
+ let parsed;
515437
+ try {
515438
+ parsed = JSON.parse(jsonText);
515439
+ } catch (e2) {
515440
+ return fallback(`JSON parse: ${e2 instanceof Error ? e2.message : String(e2)}`);
515441
+ }
515442
+ if (!parsed || typeof parsed !== "object")
515443
+ return fallback("not an object");
515444
+ const rawVerdict = String(parsed.verdict ?? "").toLowerCase().trim();
515445
+ let verdict;
515446
+ if (rawVerdict === "continue" || rawVerdict === "ok" || rawVerdict === "stay")
515447
+ verdict = "continue";
515448
+ else if (rawVerdict === "simplify" || rawVerdict === "shrink" || rawVerdict === "minimize")
515449
+ verdict = "simplify";
515450
+ else if (rawVerdict === "pivot" || rawVerdict === "change" || rawVerdict === "redirect")
515451
+ verdict = "pivot";
515452
+ else if (rawVerdict === "declare-blocked" || rawVerdict === "declare_blocked" || rawVerdict === "blocked" || rawVerdict === "escalate")
515453
+ verdict = "declare-blocked";
515454
+ else
515455
+ return fallback(`unrecognized verdict: ${rawVerdict}`);
515456
+ const rationale = typeof parsed.rationale === "string" && parsed.rationale.length > 0 ? parsed.rationale.slice(0, 600) : "(no rationale)";
515457
+ let recommended_frame;
515458
+ if (verdict === "simplify" || verdict === "pivot") {
515459
+ const rf = parsed.recommended_frame;
515460
+ if (rf && typeof rf === "object") {
515461
+ const newSub = typeof rf.new_subtask === "string" ? rf.new_subtask.slice(0, 400) : "";
515462
+ const whyBetter = typeof rf.why_better === "string" ? rf.why_better.slice(0, 400) : "";
515463
+ const success = typeof rf.success_criterion === "string" ? rf.success_criterion.slice(0, 400) : "";
515464
+ if (newSub.length > 0) {
515465
+ recommended_frame = { new_subtask: newSub, why_better: whyBetter, success_criterion: success };
515466
+ }
515467
+ }
515468
+ if (!recommended_frame) {
515469
+ return fallback(`${verdict} verdict missing required recommended_frame`);
515470
+ }
515471
+ }
515472
+ let blocker_summary;
515473
+ if (verdict === "declare-blocked") {
515474
+ blocker_summary = typeof parsed.blocker_summary === "string" && parsed.blocker_summary.length > 0 ? parsed.blocker_summary.slice(0, 600) : void 0;
515475
+ if (!blocker_summary) {
515476
+ return fallback(`declare-blocked verdict missing required blocker_summary`);
515477
+ }
515478
+ }
515479
+ return {
515480
+ verdict,
515481
+ rationale,
515482
+ recommended_frame,
515483
+ blocker_summary,
515484
+ raw: rawResponse
515485
+ };
515486
+ }
515487
+ function renderVerdictAsMessage(v) {
515488
+ if (v.parseFallback)
515489
+ return "";
515490
+ if (v.verdict === "continue")
515491
+ return "";
515492
+ const lines = [];
515493
+ lines.push(`[PROBLEM-FRAME VALIDATION — REG-51 — ${v.verdict.toUpperCase()}]`);
515494
+ lines.push(``);
515495
+ lines.push(`A meta-strategy validator reviewed your goal, current sub-task, and recent`);
515496
+ lines.push(`activity. Verdict:`);
515497
+ lines.push(``);
515498
+ lines.push(` ${v.rationale}`);
515499
+ lines.push(``);
515500
+ if (v.verdict === "simplify" || v.verdict === "pivot") {
515501
+ const rf = v.recommended_frame;
515502
+ const verb = v.verdict === "simplify" ? "SHRINK YOUR CURRENT FRAME" : "PIVOT TO A DIFFERENT FRAME";
515503
+ lines.push(`${verb}:`);
515504
+ lines.push(``);
515505
+ lines.push(` New sub-task: ${rf.new_subtask}`);
515506
+ lines.push(` Why this is better: ${rf.why_better}`);
515507
+ lines.push(` Done when: ${rf.success_criterion}`);
515508
+ lines.push(``);
515509
+ lines.push(`Update your todo list to reflect this new framing on your next response.`);
515510
+ lines.push(`Then take ONE concrete action toward the new sub-task. Do NOT continue`);
515511
+ lines.push(`the previous approach — it has been judged ${v.verdict === "pivot" ? "wrong" : "too ambitious"} for`);
515512
+ lines.push(`your current state.`);
515513
+ } else if (v.verdict === "declare-blocked") {
515514
+ lines.push(`DECLARE BLOCKED:`);
515515
+ lines.push(``);
515516
+ lines.push(` ${v.blocker_summary}`);
515517
+ lines.push(``);
515518
+ lines.push(`Call task_complete with a summary that names this blocker EXPLICITLY. Do`);
515519
+ lines.push(`not pretend the work is done; do not keep iterating. The validator has`);
515520
+ lines.push(`determined no productive frame exists from this state. Surface the`);
515521
+ lines.push(`blocker and let the user / next agent intervene.`);
515522
+ }
515523
+ return lines.join("\n");
515524
+ }
515525
+ async function runFrameValidator(opts) {
515526
+ const startMs = Date.now();
515527
+ const prompt = renderFrameValidatorPrompt(opts.inputs);
515528
+ const promptBytes = Buffer.byteLength(prompt, "utf-8");
515529
+ let raw = "";
515530
+ try {
515531
+ raw = await opts.callable(prompt);
515532
+ } catch (e2) {
515533
+ raw = "";
515534
+ void e2;
515535
+ }
515536
+ const responseBytes = Buffer.byteLength(raw, "utf-8");
515537
+ const verdict = parseFrameVerdict(raw);
515538
+ const injection = renderVerdictAsMessage(verdict);
515539
+ return {
515540
+ verdict,
515541
+ injection,
515542
+ promptBytes,
515543
+ responseBytes,
515544
+ durationMs: Date.now() - startMs
515545
+ };
515546
+ }
515547
+ var init_problem_frame_validator = __esm({
515548
+ "packages/orchestrator/dist/problem-frame-validator.js"() {
515549
+ "use strict";
515550
+ }
515551
+ });
515552
+
515157
515553
  // packages/orchestrator/dist/pressure-gate.js
515158
515554
  function detectPressure(message2) {
515159
515555
  const hasProfanity = PRESSURE_SIGNALS.test(message2);
@@ -521394,6 +521790,7 @@ var init_agenticRunner = __esm({
521394
521790
  init_backward_pass_runner();
521395
521791
  init_world_state_plan_reconciler();
521396
521792
  init_stuck_meta_analyzer();
521793
+ init_problem_frame_validator();
521397
521794
  init_pressure_gate();
521398
521795
  init_dist5();
521399
521796
  init_dist7();
@@ -521594,6 +521991,33 @@ var init_agenticRunner = __esm({
521594
521991
  // turn until they clear. Track which we've surfaced this run so the
521595
521992
  // signal doesn't fire >1× per turn per stem.
521596
521993
  _stickyEscalationsSurfacedThisTurn = /* @__PURE__ */ new Set();
521994
+ // REG-49b: per-loop-episode dedup flag for SSMA invocation from the
521995
+ // Loop Intervention escalation path. Reset when repetition score
521996
+ // drops below the recovery threshold (0.2).
521997
+ _smaFiredThisLoop = false;
521998
+ // REG-50: same-file write-thrash detector cooldown. Once fired, give
521999
+ // the agent 8 turns to break out before re-firing. Distinct from REG-44
522000
+ // because the failure shape is different (writes >> reads, but stuck
522001
+ // because no verification between writes).
522002
+ _writeThrashCooldownUntilTurn = -1;
522003
+ // REG-51: Problem-Frame Validator state. Periodic checkpoint (every
522004
+ // OA_PFV_INTERVAL turns) + chronic-stuck trigger (when SSMA fire count
522005
+ // hits a threshold). Tracks last-fired turn and cumulative SSMA count
522006
+ // for chronic detection.
522007
+ _lastPfvTurn = -1;
522008
+ _ssmaFiredCount = 0;
522009
+ // REG-52: PFV cooldown. Without this, chronic-stuck (which only ever
522010
+ // increases) keeps PFV firing every turn after the threshold is
522011
+ // crossed, mostly returning continue (waste). Cooldown for OA_PFV_COOLDOWN
522012
+ // turns (default 8) after every fire — same shape as REG-44/REG-50.
522013
+ _pfvCooldownUntilTurn = -1;
522014
+ // REG-53: edit-fail-thrash detector cooldown. Detects the failure
522015
+ // mode REG-50 misses: agent hammers file_edit/batch_edit on the same
522016
+ // file with old_string variants that never match. Each failure
522017
+ // doesn't increment the file's WRITE count (no bytes hit disk), so
522018
+ // REG-50's cooldown stays armed and never re-fires. The agent stays
522019
+ // stuck.
522020
+ _editFailThrashCooldownUntilTurn = -1;
521597
522021
  // REG-46: world-state regeneration. Replaces stream-based context
521598
522022
  // re-derivation (agent re-listing dirs, re-reading specs) with periodic
521599
522023
  // injected snapshots of workdir + plan reconciliation + recent failures.
@@ -524316,6 +524740,7 @@ ${_staleSamples.join("\n")}` : ``,
524316
524740
  preview: (entry.wentWrong || "").slice(0, 200)
524317
524741
  })).sort((a2, b) => b.attempts - a2.attempts).slice(0, 5);
524318
524742
  const _smaTools = Array.from(this.tools.keys());
524743
+ this._ssmaFiredCount++;
524319
524744
  runStuckAnalyzer({
524320
524745
  inputs: {
524321
524746
  goal: this._taskState.originalGoal || this._taskState.goal || "",
@@ -524358,6 +524783,293 @@ ${_staleSamples.join("\n")}` : ``,
524358
524783
  }
524359
524784
  }
524360
524785
  }
524786
+ if (turn > this._writeThrashCooldownUntilTurn && turn >= 12) {
524787
+ const _wtWindow = toolCallLog.slice(-15);
524788
+ if (_wtWindow.length >= 12) {
524789
+ const _wtWriteClass = /* @__PURE__ */ new Set([
524790
+ "file_write",
524791
+ "file_edit",
524792
+ "file_patch",
524793
+ "batch_edit",
524794
+ "notebook_edit"
524795
+ ]);
524796
+ const _wtFileCounts = /* @__PURE__ */ new Map();
524797
+ for (const c9 of _wtWindow) {
524798
+ if (!_wtWriteClass.has(c9.name))
524799
+ continue;
524800
+ const _ak = c9.argsKey || "";
524801
+ const _m = /(?:^|,)path=([^,]+)/.exec(_ak);
524802
+ const _pk = _m ? _m[1].slice(0, 200) : _ak.slice(0, 200);
524803
+ _wtFileCounts.set(_pk, (_wtFileCounts.get(_pk) ?? 0) + 1);
524804
+ }
524805
+ const _wtThreshold = parseInt(process.env["OA_WRITE_THRASH_THRESHOLD"] || "4", 10) || 4;
524806
+ let _wtWorstPath = "";
524807
+ let _wtWorstCount = 0;
524808
+ for (const [_p, _n] of _wtFileCounts.entries()) {
524809
+ if (_n > _wtWorstCount) {
524810
+ _wtWorstCount = _n;
524811
+ _wtWorstPath = _p;
524812
+ }
524813
+ }
524814
+ const _wtHadSuccessfulVerify = _wtWindow.some((c9) => {
524815
+ if (c9.name !== "shell" || c9.success !== true)
524816
+ return false;
524817
+ const _out = c9.outputPreview || "";
524818
+ return !/error|failed?|exit code [1-9]/i.test(_out);
524819
+ });
524820
+ if (_wtWorstCount >= _wtThreshold && !_wtHadSuccessfulVerify) {
524821
+ this._writeThrashCooldownUntilTurn = turn + 8;
524822
+ messages2.push({
524823
+ role: "system",
524824
+ content: [
524825
+ `[WRITE-THRASH HALT — REG-50]`,
524826
+ ``,
524827
+ `In the last ${_wtWindow.length} tool calls you have written the same file ${_wtWorstCount} times:`,
524828
+ ` ${_wtWorstPath}`,
524829
+ ``,
524830
+ `No successful test/build/typecheck command ran between writes — you are iterating the file blind, hoping the next variation works. This is a write-thrash anti-pattern. Repeated edits without verification confirm nothing.`,
524831
+ ``,
524832
+ `Pick ONE of these for your next response:`,
524833
+ ``,
524834
+ ` (a) RUN-AND-READ: Run the EXACT command that fails (test/typecheck/build) and READ THE FULL ERROR MESSAGE LITERALLY. Do not summarize it from memory; paste it back into context. The current write hasn't been validated against any error.`,
524835
+ ``,
524836
+ ` (b) DELETE-AND-RESTART: If the file has been rewritten ${_wtWorstCount} times, the current approach is wrong. Either delete the file and try a fundamentally different design, OR revert to a known-good earlier version (use git, working_notes, or memory_search to find one).`,
524837
+ ``,
524838
+ ` (c) WEB-SEARCH: If the error is framework- or version-specific (config files often are), web_search the EXACT error string + relevant tool name. External knowledge beats blind iteration.`,
524839
+ ``,
524840
+ ` (d) DECLARE BLOCKED: If the file's correct shape genuinely isn't knowable from the spec + this codebase, call task_complete with a summary that names this specific file as the blocker. Don't burn more turns on it.`,
524841
+ ``,
524842
+ `Do NOT in your next response: write to ${_wtWorstPath} again without first running and reading the failing command's output.`
524843
+ ].join("\n")
524844
+ });
524845
+ this.emit({
524846
+ type: "status",
524847
+ content: `REG-50 WRITE-THRASH halt fired at turn ${turn} — file=${_wtWorstPath}, count=${_wtWorstCount}/${_wtThreshold}, no successful verify in window`,
524848
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
524849
+ });
524850
+ try {
524851
+ const _smaRaw50 = (process.env["OA_STUCK_META_ANALYZER"] || "off").toLowerCase();
524852
+ const _smaOn50 = _smaRaw50 === "on" || _smaRaw50 === "1" || _smaRaw50 === "true";
524853
+ if (_smaOn50) {
524854
+ const _smaCallable50 = async (prompt) => {
524855
+ try {
524856
+ const _r = await this.backend.chatCompletion({
524857
+ messages: [
524858
+ { role: "system", content: "You are a META-ANALYSIS sub-agent. Audit the implementer's stuck state and emit a structured JSON directive." },
524859
+ { role: "user", content: prompt }
524860
+ ],
524861
+ tools: [],
524862
+ temperature: 0,
524863
+ maxTokens: parseInt(process.env["OA_STUCK_META_MAX_TOKENS"] || "2048", 10) || 2048,
524864
+ timeoutMs: parseInt(process.env["OA_STUCK_META_TIMEOUT_MS"] || "120000", 10) || 12e4
524865
+ });
524866
+ const _c = _r?.choices?.[0]?.message?.content;
524867
+ return typeof _c === "string" ? _c : "";
524868
+ } catch {
524869
+ return "";
524870
+ }
524871
+ };
524872
+ const _smaCalls50 = _wtWindow.map((c9) => ({
524873
+ name: c9.name,
524874
+ argsKey: c9.argsKey,
524875
+ success: c9.success,
524876
+ outputPreview: (c9.outputPreview || "").split(/\r?\n/)[0]?.slice(0, 120) ?? ""
524877
+ }));
524878
+ const _smaPlan50 = (() => {
524879
+ try {
524880
+ const _todos = this.readSessionTodos() || [];
524881
+ return _todos.slice(0, 12).map((t2) => ({
524882
+ content: t2.content || "",
524883
+ reconciled: t2.status || "pending",
524884
+ rationale: "(structural status only)"
524885
+ }));
524886
+ } catch {
524887
+ return [];
524888
+ }
524889
+ })();
524890
+ const _smaTools50 = Array.from(this.tools.keys());
524891
+ this._ssmaFiredCount++;
524892
+ runStuckAnalyzer({
524893
+ inputs: {
524894
+ goal: this._taskState.originalGoal || this._taskState.goal || "",
524895
+ workingDir: this._workingDirectory || process.cwd(),
524896
+ triggerReason: `reg50-write-thrash-${_wtWorstCount}x`,
524897
+ recentToolCalls: _smaCalls50,
524898
+ planStatus: _smaPlan50,
524899
+ recentFailures: [],
524900
+ workspaceSummary: `WRITE-THRASH: ${_wtWorstPath} written ${_wtWorstCount} times in last ${_wtWindow.length} calls without successful verification.`,
524901
+ availableTools: _smaTools50,
524902
+ turn
524903
+ },
524904
+ callable: _smaCallable50
524905
+ }).then((_smaResult) => {
524906
+ if (_smaResult.injection && !_smaResult.directive.parseFallback) {
524907
+ messages2.push({ role: "system", content: _smaResult.injection });
524908
+ this.emit({
524909
+ type: "status",
524910
+ content: `REG-50 → SSMA fired at turn ${turn} — diagnosis="${_smaResult.directive.diagnosis.slice(0, 80)}", next=${_smaResult.directive.next_action.tool}, ${_smaResult.durationMs}ms`,
524911
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
524912
+ });
524913
+ }
524914
+ }).catch(() => {
524915
+ });
524916
+ }
524917
+ } catch {
524918
+ }
524919
+ }
524920
+ }
524921
+ }
524922
+ if (turn > this._editFailThrashCooldownUntilTurn && turn >= 12) {
524923
+ const _efWindow = toolCallLog.slice(-15);
524924
+ if (_efWindow.length >= 12) {
524925
+ const _efEditClass = /* @__PURE__ */ new Set([
524926
+ "file_edit",
524927
+ "batch_edit",
524928
+ "file_patch"
524929
+ ]);
524930
+ const _efFailCounts = /* @__PURE__ */ new Map();
524931
+ for (const c9 of _efWindow) {
524932
+ if (!_efEditClass.has(c9.name))
524933
+ continue;
524934
+ if (c9.success !== false)
524935
+ continue;
524936
+ const _ak = c9.argsKey || "";
524937
+ const _m = /(?:^|,)path=([^,]+)/.exec(_ak);
524938
+ const _pk = _m ? _m[1].slice(0, 200) : _ak.slice(0, 200);
524939
+ _efFailCounts.set(_pk, (_efFailCounts.get(_pk) ?? 0) + 1);
524940
+ }
524941
+ const _efThreshold = parseInt(process.env["OA_EDIT_FAIL_THRESHOLD"] || "3", 10) || 3;
524942
+ let _efWorstPath = "";
524943
+ let _efWorstCount = 0;
524944
+ for (const [_p, _n] of _efFailCounts.entries()) {
524945
+ if (_n > _efWorstCount) {
524946
+ _efWorstCount = _n;
524947
+ _efWorstPath = _p;
524948
+ }
524949
+ }
524950
+ if (_efWorstCount >= _efThreshold) {
524951
+ this._editFailThrashCooldownUntilTurn = turn + 8;
524952
+ messages2.push({
524953
+ role: "system",
524954
+ content: [
524955
+ `[EDIT-FAIL-THRASH HALT — REG-53]`,
524956
+ ``,
524957
+ `In the last ${_efWindow.length} tool calls you have failed file_edit/batch_edit on the same file ${_efWorstCount} times:`,
524958
+ ` ${_efWorstPath}`,
524959
+ ``,
524960
+ `Each failure means your old_string did not match the file content. Your remembered version of this file has diverged from what's on disk — likely because an earlier edit succeeded and shifted things, or because you guessed at the file's content.`,
524961
+ ``,
524962
+ `STOP guessing variants of old_string. Pick ONE of these for your next response:`,
524963
+ ``,
524964
+ ` (a) FILE_READ + COPY-EXACT: Call file_read on ${_efWorstPath}, then copy the EXACT bytes (whitespace, indentation, punctuation) for old_string from that read. Do not paraphrase or reformat.`,
524965
+ ``,
524966
+ ` (b) USE THE INLINE SNIPPET: Recent edit failures already include a "closest match" snippet showing the actual current content near where you tried to edit. Read that snippet in your last error message and use its content verbatim as old_string.`,
524967
+ ``,
524968
+ ` (c) REPLACE_ALL: If you want a global rename, set replace_all=true and the uniqueness check is bypassed.`,
524969
+ ``,
524970
+ ` (d) FILE_WRITE: If the changes are extensive, rewrite the whole file with file_write instead of patching it. Faster than 6 failed edits.`,
524971
+ ``,
524972
+ `Do NOT in your next response: call file_edit or batch_edit on ${_efWorstPath} again with another guess at old_string.`
524973
+ ].join("\n")
524974
+ });
524975
+ this.emit({
524976
+ type: "status",
524977
+ content: `REG-53 EDIT-FAIL-THRASH halt fired at turn ${turn} — file=${_efWorstPath}, failures=${_efWorstCount}/${_efThreshold}`,
524978
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
524979
+ });
524980
+ }
524981
+ }
524982
+ }
524983
+ try {
524984
+ const _pfvRaw = (process.env["OA_PFV"] || "off").toLowerCase();
524985
+ const _pfvOn = _pfvRaw === "on" || _pfvRaw === "1" || _pfvRaw === "true";
524986
+ if (_pfvOn && this._lastPfvTurn !== turn && turn > this._pfvCooldownUntilTurn) {
524987
+ const _pfvInterval = parseInt(process.env["OA_PFV_INTERVAL"] || "30", 10) || 30;
524988
+ const _pfvChronicThreshold = parseInt(process.env["OA_PFV_CHRONIC_THRESHOLD"] || "2", 10) || 2;
524989
+ const _pfvCooldown = parseInt(process.env["OA_PFV_COOLDOWN"] || "8", 10) || 8;
524990
+ const _pfvPeriodic = turn > 0 && _pfvInterval > 0 && turn % _pfvInterval === 0;
524991
+ const _pfvChronic = this._ssmaFiredCount >= _pfvChronicThreshold;
524992
+ if (_pfvPeriodic || _pfvChronic) {
524993
+ this._lastPfvTurn = turn;
524994
+ this._pfvCooldownUntilTurn = turn + _pfvCooldown;
524995
+ const _pfvTriggerReason = _pfvChronic ? "chronic-stuck" : "periodic";
524996
+ const _pfvCallable = async (prompt) => {
524997
+ try {
524998
+ const _r = await this.backend.chatCompletion({
524999
+ messages: [
525000
+ { role: "system", content: "You are a META-STRATEGY VALIDATOR. Audit the implementer's frame-vs-goal alignment and emit a structured JSON verdict." },
525001
+ { role: "user", content: prompt }
525002
+ ],
525003
+ tools: [],
525004
+ temperature: 0,
525005
+ maxTokens: parseInt(process.env["OA_PFV_MAX_TOKENS"] || "1500", 10) || 1500,
525006
+ timeoutMs: parseInt(process.env["OA_PFV_TIMEOUT_MS"] || "120000", 10) || 12e4
525007
+ });
525008
+ const _c = _r?.choices?.[0]?.message?.content;
525009
+ return typeof _c === "string" ? _c : "";
525010
+ } catch {
525011
+ return "";
525012
+ }
525013
+ };
525014
+ const _pfvTodos = (() => {
525015
+ try {
525016
+ return this.readSessionTodos() || [];
525017
+ } catch {
525018
+ return [];
525019
+ }
525020
+ })();
525021
+ const _pfvCurrentSubtask = _pfvTodos.find((t2) => t2.status === "in_progress")?.content || "";
525022
+ const _pfvPlanStatus = _pfvTodos.slice(0, 12).map((t2) => ({
525023
+ content: t2.content || "",
525024
+ status: t2.status || "pending"
525025
+ }));
525026
+ const _pfvRecentCalls = toolCallLog.slice(-30);
525027
+ const _pfvSummaryParts = [];
525028
+ _pfvSummaryParts.push(`Last ${_pfvRecentCalls.length} tool calls (most recent at bottom):`);
525029
+ for (const _c of _pfvRecentCalls) {
525030
+ const _ak = (_c.argsKey || "").slice(0, 80);
525031
+ const _flag = _c.success === false ? "ERR" : "ok";
525032
+ _pfvSummaryParts.push(` ${_c.name}(${_ak}) ${_flag}`);
525033
+ }
525034
+ const _pfvActivity = _pfvSummaryParts.join("\n");
525035
+ runFrameValidator({
525036
+ inputs: {
525037
+ goal: this._taskState.originalGoal || this._taskState.goal || "",
525038
+ workingDir: this._workingDirectory || process.cwd(),
525039
+ currentSubtask: _pfvCurrentSubtask,
525040
+ recentActivitySummary: _pfvActivity,
525041
+ planStatus: _pfvPlanStatus,
525042
+ recentStuckCount: this._ssmaFiredCount,
525043
+ turn,
525044
+ triggerReason: _pfvTriggerReason
525045
+ },
525046
+ callable: _pfvCallable
525047
+ }).then((_pfvResult) => {
525048
+ if (_pfvResult.injection && !_pfvResult.verdict.parseFallback && _pfvResult.verdict.verdict !== "continue") {
525049
+ messages2.push({ role: "system", content: _pfvResult.injection });
525050
+ this.emit({
525051
+ type: "status",
525052
+ content: `REG-51 PFV fired at turn ${turn} — verdict=${_pfvResult.verdict.verdict}, trigger=${_pfvTriggerReason}, ssmaCount=${this._ssmaFiredCount}, ${_pfvResult.durationMs}ms`,
525053
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
525054
+ });
525055
+ } else {
525056
+ this.emit({
525057
+ type: "status",
525058
+ content: `REG-51 PFV verdict=continue (or parse-fallback) at turn ${turn} — trigger=${_pfvTriggerReason}, ${_pfvResult.durationMs}ms`,
525059
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
525060
+ });
525061
+ }
525062
+ }).catch((_e) => {
525063
+ this.emit({
525064
+ type: "status",
525065
+ content: `REG-51 PFV threw: ${_e instanceof Error ? _e.message : String(_e)} (non-fatal)`,
525066
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
525067
+ });
525068
+ });
525069
+ }
525070
+ }
525071
+ } catch (_e) {
525072
+ }
524361
525073
  try {
524362
525074
  const STICKY_PER_TURN_CAP = 2;
524363
525075
  const _candidates = [];
@@ -526393,7 +527105,90 @@ Call task_complete(summary="...") NOW with whatever you have.`
526393
527105
  content: `Loop intervention ${loopInterventionCount}/${maxInterventions}: ${Math.round(currentRepScore * 100)}% repetitive (${topRepeated})`,
526394
527106
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
526395
527107
  });
527108
+ try {
527109
+ const _smaRaw = (process.env["OA_STUCK_META_ANALYZER"] || "off").toLowerCase();
527110
+ const _smaOn = _smaRaw === "on" || _smaRaw === "1" || _smaRaw === "true";
527111
+ if (_smaOn && loopInterventionCount >= 2 && !this._smaFiredThisLoop) {
527112
+ this._smaFiredThisLoop = true;
527113
+ const _smaCallable = async (prompt) => {
527114
+ try {
527115
+ const _r = await this.backend.chatCompletion({
527116
+ messages: [
527117
+ { role: "system", content: "You are a META-ANALYSIS sub-agent. Audit the implementer's stuck state and emit a structured JSON directive." },
527118
+ { role: "user", content: prompt }
527119
+ ],
527120
+ tools: [],
527121
+ temperature: 0,
527122
+ maxTokens: parseInt(process.env["OA_STUCK_META_MAX_TOKENS"] || "2048", 10) || 2048,
527123
+ timeoutMs: parseInt(process.env["OA_STUCK_META_TIMEOUT_MS"] || "120000", 10) || 12e4
527124
+ });
527125
+ const _c = _r?.choices?.[0]?.message?.content;
527126
+ return typeof _c === "string" ? _c : "";
527127
+ } catch {
527128
+ return "";
527129
+ }
527130
+ };
527131
+ const _smaCalls = toolCallLog.slice(-25).map((c9) => ({
527132
+ name: c9.name,
527133
+ argsKey: c9.argsKey,
527134
+ success: c9.success,
527135
+ outputPreview: (c9.outputPreview || "").split(/\r?\n/)[0]?.slice(0, 120) ?? ""
527136
+ }));
527137
+ const _smaPlan = (() => {
527138
+ try {
527139
+ const _todos = this.readSessionTodos() || [];
527140
+ return _todos.slice(0, 12).map((t2) => ({
527141
+ content: t2.content || "",
527142
+ reconciled: t2.status || "pending",
527143
+ rationale: "(structural status only)"
527144
+ }));
527145
+ } catch {
527146
+ return [];
527147
+ }
527148
+ })();
527149
+ const _smaFailures = Array.from(this._failureReflections.entries()).map(([stem, entry]) => ({ stem, attempts: entry.attempts, preview: (entry.wentWrong || "").slice(0, 200) })).sort((a2, b) => b.attempts - a2.attempts).slice(0, 5);
527150
+ const _smaTools = Array.from(this.tools.keys());
527151
+ this._ssmaFiredCount++;
527152
+ runStuckAnalyzer({
527153
+ inputs: {
527154
+ goal: this._taskState.originalGoal || this._taskState.goal || "",
527155
+ workingDir: this._workingDirectory || process.cwd(),
527156
+ triggerReason: `loop-intervention-${loopInterventionCount}/${maxInterventions}`,
527157
+ recentToolCalls: _smaCalls,
527158
+ planStatus: _smaPlan,
527159
+ recentFailures: _smaFailures,
527160
+ workspaceSummary: void 0,
527161
+ availableTools: _smaTools,
527162
+ turn
527163
+ },
527164
+ callable: _smaCallable
527165
+ }).then((_smaResult) => {
527166
+ if (_smaResult.injection && !_smaResult.directive.parseFallback) {
527167
+ messages2.push({ role: "system", content: _smaResult.injection });
527168
+ this.emit({
527169
+ type: "status",
527170
+ content: `REG-49 stuck-meta-analyzer fired (loop-intervention path) at turn ${turn} — diagnosis="${_smaResult.directive.diagnosis.slice(0, 80)}", next=${_smaResult.directive.next_action.tool}, ${_smaResult.durationMs}ms`,
527171
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
527172
+ });
527173
+ } else {
527174
+ this.emit({
527175
+ type: "status",
527176
+ content: `REG-49 stuck-meta-analyzer parse failed (loop-intervention path) at turn ${turn} — falling back to existing intervention`,
527177
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
527178
+ });
527179
+ }
527180
+ }).catch((_e) => {
527181
+ this.emit({
527182
+ type: "status",
527183
+ content: `REG-49 stuck-meta-analyzer (loop-intervention path) threw: ${_e instanceof Error ? _e.message : String(_e)} (non-fatal)`,
527184
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
527185
+ });
527186
+ });
527187
+ }
527188
+ } catch (_e) {
527189
+ }
526396
527190
  } else if (currentRepScore <= 0.2) {
527191
+ this._smaFiredThisLoop = false;
526397
527192
  }
526398
527193
  } else {
526399
527194
  const content = msg.content || "";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.499",
3
+ "version": "0.187.501",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "open-agents-ai",
9
- "version": "0.187.499",
9
+ "version": "0.187.501",
10
10
  "hasInstallScript": true,
11
11
  "license": "CC-BY-NC-4.0",
12
12
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.499",
3
+ "version": "0.187.501",
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",