open-agents-ai 0.187.479 → 0.187.481

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
@@ -512229,6 +512229,139 @@ var init_critic = __esm({
512229
512229
  }
512230
512230
  });
512231
512231
 
512232
+ // packages/orchestrator/dist/reflection.js
512233
+ function extractSubject(errorText) {
512234
+ if (!errorText)
512235
+ return null;
512236
+ const PATTERNS = [
512237
+ // Quoted module / type / symbol after recognizable phrases
512238
+ /cannot find (?:module|name|type|symbol|reference|file|namespace)\s+['"`]([^'"`\n]{1,80})['"`]/i,
512239
+ /(?:undefined|unresolved)\s+(?:reference|import|symbol)\s+(?:to\s+)?['"`]([^'"`\n]{1,80})['"`]/i,
512240
+ /['"`]([^'"`\n]{1,80})['"`]\s+is not (?:a function|defined|assignable)/i,
512241
+ /is not assignable to (?:type|parameter)\s+['"`]([^'"`\n]{1,80})['"`]/i,
512242
+ /\btype\s+['"`]([^'"`\n]{1,80})['"`]\s+is not assignable/i,
512243
+ /\benoent\b[^'"`\n]*['"`]([^'"`\n]{1,200})['"`]/i,
512244
+ /\b(?:permission denied|eacces)\b[^'"`\n]*['"`]([^'"`\n]{1,200})['"`]/i,
512245
+ /no such file or directory[^'"`\n]*['"`]([^'"`\n]{1,200})['"`]/i,
512246
+ /\b([a-z_][a-z0-9_]*)\s+is not defined\b/i,
512247
+ /\b(?:property|method|attribute)\s+['"`]([^'"`\n]{1,80})['"`]\s+(?:does not exist|not found)/i,
512248
+ /\bcannot resolve\s+['"`]?([^'"`\n\s]{1,120})['"`]?/i,
512249
+ /\bmodule not found:?\s+['"`]?([^'"`\n\s]{1,120})['"`]?/i
512250
+ ];
512251
+ for (const re of PATTERNS) {
512252
+ const m2 = errorText.match(re);
512253
+ if (m2 && m2[1]) {
512254
+ const subj = m2[1].trim();
512255
+ if (subj.length > 0 && subj.length <= 200)
512256
+ return subj;
512257
+ }
512258
+ }
512259
+ return null;
512260
+ }
512261
+ function errorSignature(errorText) {
512262
+ if (!errorText)
512263
+ return "";
512264
+ const norm = errorText.replace(/\r?\n/g, " ").replace(/\s+/g, " ").replace(/^\s*error:\s*/i, "").trim().toLowerCase();
512265
+ return norm.slice(0, 50);
512266
+ }
512267
+ function categorizeError(errorText) {
512268
+ if (!errorText)
512269
+ return "unknown";
512270
+ for (const { category, re } of CATEGORY_PATTERNS) {
512271
+ if (re.test(errorText))
512272
+ return category;
512273
+ }
512274
+ return "unknown";
512275
+ }
512276
+ function buildStem(toolName, args) {
512277
+ if (!args || Object.keys(args).length === 0)
512278
+ return toolName;
512279
+ const entries = Object.entries(args).sort(([a2], [b]) => a2.localeCompare(b));
512280
+ const first2 = entries[0];
512281
+ const v = typeof first2[1] === "string" ? first2[1] : JSON.stringify(first2[1]);
512282
+ return `${toolName}:${first2[0]}=${v.slice(0, 60)}`;
512283
+ }
512284
+ function firstSignalLine(errorText) {
512285
+ if (!errorText)
512286
+ return "";
512287
+ const lines = errorText.split(/\r?\n/);
512288
+ for (const raw of lines) {
512289
+ const line = raw.trim();
512290
+ if (!line)
512291
+ continue;
512292
+ if (line === "Error:" || line === "error:")
512293
+ continue;
512294
+ return line.slice(0, 200);
512295
+ }
512296
+ return errorText.slice(0, 200);
512297
+ }
512298
+ function synthesizeReflection(input) {
512299
+ const category = categorizeError(input.errorText);
512300
+ const stem = buildStem(input.toolName, input.args);
512301
+ const argPreview = JSON.stringify(input.args ?? {}).slice(0, 120);
512302
+ const subject = extractSubject(input.errorText);
512303
+ const sigs = new Set(input.priorErrorSignatures ?? []);
512304
+ const sig = errorSignature(input.errorText);
512305
+ if (sig)
512306
+ sigs.add(sig);
512307
+ return {
512308
+ stem,
512309
+ attempted: `${input.toolName}(${argPreview})`,
512310
+ wentWrong: firstSignalLine(input.errorText),
512311
+ hypothesis: HYPOTHESES[category],
512312
+ turn: input.turn,
512313
+ attempts: (input.priorAttempts ?? 0) + 1,
512314
+ subject,
512315
+ errorSignatures: sigs
512316
+ };
512317
+ }
512318
+ function renderReflectionMessage(r2) {
512319
+ const lines = [];
512320
+ const distinctErrors = r2.errorSignatures?.size ?? 0;
512321
+ if (distinctErrors >= 3) {
512322
+ lines.push(`[ERROR-SHIFT DETECTED — ${distinctErrors} DIFFERENT errors have emerged for \`${r2.attempted}\` across ${r2.attempts} attempts.`, `Each "fix" you've made is moving the bug somewhere new instead of resolving it. Your understanding of the failure is wrong.`, `STOP fixing. Re-read the FIRST error you saw on this call and trace exactly what each subsequent fix changed. Do NOT make another change until you can explain why the next change addresses the root.]`, ``);
512323
+ }
512324
+ lines.push(`[REFLECTION — your last attempt of \`${r2.attempted}\` failed (turn ${r2.turn}, ${r2.attempts} attempt${r2.attempts === 1 ? "" : "s"} so far).`);
512325
+ lines.push(`Last error: "${r2.wentWrong}"`);
512326
+ lines.push(`Hypothesis: ${r2.hypothesis}`);
512327
+ if (r2.subject) {
512328
+ lines.push(`Specifically: verify \`${r2.subject}\` exists at the expected location with the smallest possible read command before retrying.`);
512329
+ }
512330
+ if (r2.attempts >= 3) {
512331
+ lines.push(``);
512332
+ lines.push(`[FORCED — your intrinsic knowledge has not resolved this in ${r2.attempts} attempts. Your NEXT call MUST be \`web_search("${r2.wentWrong.replace(/"/g, '\\"').slice(0, 120)}")\` (or close-equivalent). Read the top result before making another fix attempt.]`);
512333
+ }
512334
+ lines.push(`VERIFY this hypothesis with a single small command BEFORE retrying the same tool. If you retry without verifying, you will likely fail the same way.]`);
512335
+ return lines.join("\n");
512336
+ }
512337
+ var CATEGORY_PATTERNS, HYPOTHESES;
512338
+ var init_reflection = __esm({
512339
+ "packages/orchestrator/dist/reflection.js"() {
512340
+ "use strict";
512341
+ CATEGORY_PATTERNS = [
512342
+ { category: "permission_denied", re: /\b(permission denied|eacces|access denied|operation not permitted|forbidden)\b/i },
512343
+ { category: "type_or_reference_error", re: /\b(type error|cannot find module|cannot find name|is not (a function|defined|assignable)|undefined reference|unresolved (import|reference)|missing required)\b/i },
512344
+ { category: "connection_refused", re: /\b(connection refused|econnrefused|connection reset|econnreset|host unreachable|getaddrinfo|enotfound)\b/i },
512345
+ { category: "timeout", re: /\b(timeout|timed out|etimedout|deadline exceeded)\b/i },
512346
+ { category: "syntax_error", re: /\b(syntax error|parse error|unexpected token|unexpected end of (input|json)|malformed)\b/i },
512347
+ { category: "not_found", re: /\b(not found|enoent|no such file|cannot find|does not exist|404)\b/i },
512348
+ // Use [1-9]\d* so multi-digit non-zero codes (e.g. "return code 127") match —
512349
+ // the prior [^0] only matched a single character and failed on multi-digit.
512350
+ { category: "nonzero_exit", re: /\b(exit code [1-9]\d*|exit status [1-9]\d*|command failed|exit code: ?[1-9]\d*|return code [1-9]\d*)\b/i }
512351
+ ];
512352
+ HYPOTHESES = {
512353
+ permission_denied: "permissions issue — check ownership and mode of the target; you may need to operate on a writeable location",
512354
+ not_found: "the named resource doesn't exist at the expected location — verify the path/name with a single-line list before retrying",
512355
+ connection_refused: "remote service is unreachable — verify it's running and reachable before retrying with the same address",
512356
+ timeout: "operation took too long — reduce scope (smaller batch, fewer items) or verify the service is healthy",
512357
+ syntax_error: "malformed input — re-read the surrounding context; the input you produced doesn't match what the consumer expects",
512358
+ type_or_reference_error: "a name, type, or import doesn't resolve — verify the reference matches what's defined; do not guess at the symbol",
512359
+ nonzero_exit: "the command exited with a failure code — read the FULL error output and verify args + prerequisites before retrying",
512360
+ unknown: "re-read the full error message and identify the most likely cause; verify your assumption with a single small command before retrying"
512361
+ };
512362
+ }
512363
+ });
512364
+
512232
512365
  // packages/orchestrator/dist/pressure-gate.js
512233
512366
  function detectPressure(message2) {
512234
512367
  const hasProfanity = PRESSURE_SIGNALS.test(message2);
@@ -518460,6 +518593,7 @@ var init_agenticRunner = __esm({
518460
518593
  init_personality();
518461
518594
  init_promptLoader();
518462
518595
  init_critic();
518596
+ init_reflection();
518463
518597
  init_pressure_gate();
518464
518598
  init_dist5();
518465
518599
  init_dist7();
@@ -518586,6 +518720,22 @@ var init_agenticRunner = __esm({
518586
518720
  _errorPatterns = /* @__PURE__ */ new Map();
518587
518721
  _errorGuidanceInjected = /* @__PURE__ */ new Set();
518588
518722
  // prevent duplicate injection per turn
518723
+ // REG-26 (Patch C): Reflexion-style structured failure memory. Indexed by
518724
+ // fingerprint stem (tool + first arg, truncated). When the agent retries a
518725
+ // tool with a stem matching a stored reflection, surface "what was tried,
518726
+ // what went wrong, hypothesis to verify" as a system message before the
518727
+ // dispatch — generic across all stacks. See packages/orchestrator/src/reflection.ts.
518728
+ _failureReflections = /* @__PURE__ */ new Map();
518729
+ _reflectionsInjectedThisTurn = /* @__PURE__ */ new Set();
518730
+ // prevent duplicate inject per turn
518731
+ // REG-30: one-shot per-turn typecheck-vs-build hint
518732
+ _typecheckHintInjectedThisTurn = false;
518733
+ // REG-31: track most recent successful build-shaped shell so the turn-start
518734
+ // positive-completion check knows when the agent has validated its work.
518735
+ _lastBuildSuccessTurn = -1;
518736
+ _lastBuildSuccessCommand = "";
518737
+ // REG-31: prevent duplicate completion suggestion per turn
518738
+ _completionPromptInjectedThisTurn = false;
518589
518739
  // ── WO-AM-01/04/10: Associative memory stores ──
518590
518740
  // Episode store: every tool call → persistent episode with importance + decay
518591
518741
  // Temporal KG: entities + relations with temporal validity (valid_from/valid_until)
@@ -520729,6 +520879,37 @@ TASK: ${task}` : task;
520729
520879
  break;
520730
520880
  }
520731
520881
  injectionsThisTurn = 0;
520882
+ this._reflectionsInjectedThisTurn.clear();
520883
+ this._typecheckHintInjectedThisTurn = false;
520884
+ this._completionPromptInjectedThisTurn = false;
520885
+ try {
520886
+ const _todos = this.readSessionTodos() || [];
520887
+ if (_todos.length > 0 && _todos.every((t2) => t2.status === "completed") && this._lastBuildSuccessTurn >= 0 && turn - this._lastBuildSuccessTurn <= 8 && !this._completionPromptInjectedThisTurn) {
520888
+ this._completionPromptInjectedThisTurn = true;
520889
+ messages2.push({
520890
+ role: "system",
520891
+ content: [
520892
+ `[ALL TODOS COMPLETED + LAST VALIDATION PASSED — TIME TO DECLARE DONE]`,
520893
+ ``,
520894
+ `Status:`,
520895
+ ` • Todos: ${_todos.length}/${_todos.length} completed`,
520896
+ ` • Last successful validation: \`${this._lastBuildSuccessCommand.slice(0, 120)}\` (turn ${this._lastBuildSuccessTurn}, ${turn - this._lastBuildSuccessTurn} turn(s) ago)`,
520897
+ ``,
520898
+ `Your work is done. Call task_complete now with a concise summary of what was implemented:`,
520899
+ ``,
520900
+ ` task_complete({ summary: "<one-paragraph description of what was built and verified>" })`,
520901
+ ``,
520902
+ `Do NOT add more polish, more files, or more validation. The plan is complete; the validation passed; the spec is implemented. Calling task_complete is the correct next action.`
520903
+ ].join("\n")
520904
+ });
520905
+ this.emit({
520906
+ type: "status",
520907
+ content: `REG-31: positive completion signal injected (todos all done, last build success ${turn - this._lastBuildSuccessTurn}t ago)`,
520908
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
520909
+ });
520910
+ }
520911
+ } catch {
520912
+ }
520732
520913
  while (deferredSoftInjections.length > 0 && injectionsThisTurn < INJECTION_BUDGET_SOFT) {
520733
520914
  const next = deferredSoftInjections.shift();
520734
520915
  messages2.push({ role: next.role, content: next.content });
@@ -521598,6 +521779,21 @@ ${memoryLines.join("\n")}`
521598
521779
  if (observerRedundantBlock) {
521599
521780
  this._littlemanRedundantBlocks.delete(toolFingerprint);
521600
521781
  }
521782
+ {
521783
+ const _reflStem = buildStem(tc.name, tc.arguments ?? {});
521784
+ if (!this._reflectionsInjectedThisTurn.has(_reflStem)) {
521785
+ const _reflEntry = this._failureReflections.get(_reflStem);
521786
+ if (_reflEntry) {
521787
+ this._reflectionsInjectedThisTurn.add(_reflStem);
521788
+ const _isEscalation = _reflEntry.attempts >= 3 || (_reflEntry.errorSignatures?.size ?? 0) >= 3;
521789
+ if (_isEscalation) {
521790
+ messages2.push({ role: "system", content: renderReflectionMessage(_reflEntry) });
521791
+ } else {
521792
+ pushSoftInjection("system", renderReflectionMessage(_reflEntry));
521793
+ }
521794
+ }
521795
+ }
521796
+ }
521601
521797
  const criticDecision = evaluate({
521602
521798
  proposedCall: { tool: tc.name, args: tc.arguments ?? {} },
521603
521799
  fingerprint: toolFingerprint,
@@ -521624,6 +521820,11 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
521624
521820
  }
521625
521821
  if (criticDecision.decision === "force_progress_block") {
521626
521822
  dedupHitCount.set(toolFingerprint, criticDecision.hitNumber);
521823
+ const _existingFp = recentToolResults.get(toolFingerprint);
521824
+ if (_existingFp !== void 0) {
521825
+ recentToolResults.delete(toolFingerprint);
521826
+ recentToolResults.set(toolFingerprint, _existingFp);
521827
+ }
521627
521828
  this.emit({ type: "tool_call", toolName: tc.name, toolArgs: tc.arguments, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
521628
521829
  this.emit({
521629
521830
  type: "tool_result",
@@ -521637,6 +521838,11 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
521637
521838
  }
521638
521839
  if (criticDecision.decision === "serve_cached") {
521639
521840
  dedupHitCount.set(toolFingerprint, criticDecision.hitNumber);
521841
+ const _existingFp = recentToolResults.get(toolFingerprint);
521842
+ if (_existingFp !== void 0) {
521843
+ recentToolResults.delete(toolFingerprint);
521844
+ recentToolResults.set(toolFingerprint, _existingFp);
521845
+ }
521640
521846
  this.emit({
521641
521847
  type: "tool_call",
521642
521848
  toolName: tc.name,
@@ -522026,6 +522232,21 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
522026
522232
  const lastLog = toolCallLog[toolCallLog.length - 1];
522027
522233
  if (lastLog)
522028
522234
  lastLog.success = true;
522235
+ if (tc.name === "shell") {
522236
+ const _shellCmd = String(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? "");
522237
+ const _typecheckOnly = /\b(--noEmit|--dry-run|--check\b|\bmypy\b|\bruff check\b|\bcargo check\b|\bstylelint --check\b|\bpylint\b(?!.*--exit-zero))\b/i.test(_shellCmd);
522238
+ if (_typecheckOnly && !this._typecheckHintInjectedThisTurn) {
522239
+ this._typecheckHintInjectedThisTurn = true;
522240
+ pushSoftInjection("system", `[Typecheck PASSED but does NOT mean the project builds or runs. Typecheck only validates declarations, not runtime behavior, plugin pipelines, or build-time transformations. Before declaring this work complete, run the actual build/run command for the project (the verb is typically "build", "run", "start", or "compile" — context-specific to your stack).]`);
522241
+ }
522242
+ }
522243
+ if (tc.name === "shell") {
522244
+ const _shellCmd2 = String(tc.arguments?.["command"] ?? tc.arguments?.["cmd"] ?? "");
522245
+ if (/\b(build|test|run\b|start\b|serve\b|verify|check)\b/i.test(_shellCmd2)) {
522246
+ this._lastBuildSuccessTurn = turn;
522247
+ this._lastBuildSuccessCommand = _shellCmd2.slice(0, 200);
522248
+ }
522249
+ }
522029
522250
  if (["file_write", "file_edit", "file_patch", "batch_edit"].includes(tc.name) && this._patchHistoryStore) {
522030
522251
  try {
522031
522252
  const filePath2 = tc.arguments?.path || tc.arguments?.file_path;
@@ -522062,6 +522283,8 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
522062
522283
  }
522063
522284
  if (result.success) {
522064
522285
  this._recentFailures = this._recentFailures.filter((f2) => f2.fingerprint !== toolFingerprint);
522286
+ const _stem = buildStem(tc.name, tc.arguments ?? {});
522287
+ this._failureReflections.delete(_stem);
522065
522288
  }
522066
522289
  if (!result.success) {
522067
522290
  this._recentFailures.push({
@@ -522075,6 +522298,25 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
522075
522298
  if (this._recentFailures.length > 8) {
522076
522299
  this._recentFailures = this._recentFailures.slice(-8);
522077
522300
  }
522301
+ const _refStem = buildStem(tc.name, tc.arguments ?? {});
522302
+ const _prior = this._failureReflections.get(_refStem);
522303
+ const _refErr = (result.error ?? result.output ?? "").toString();
522304
+ const _entry = synthesizeReflection({
522305
+ toolName: tc.name,
522306
+ args: tc.arguments ?? {},
522307
+ errorText: _refErr,
522308
+ turn,
522309
+ priorAttempts: _prior?.attempts ?? 0,
522310
+ // REG-27: carry forward distinct error-signature set so the
522311
+ // agent's renderer can detect error-shift (3+ different errors)
522312
+ priorErrorSignatures: _prior?.errorSignatures
522313
+ });
522314
+ this._failureReflections.set(_refStem, _entry);
522315
+ if (this._failureReflections.size > 32) {
522316
+ const oldestKey = this._failureReflections.keys().next().value;
522317
+ if (oldestKey !== void 0)
522318
+ this._failureReflections.delete(oldestKey);
522319
+ }
522078
522320
  }
522079
522321
  if (!result.success && tc.name === "shell" && /\[PERMISSION_ERROR\]/.test(result.error ?? "")) {
522080
522322
  this.emit({
@@ -522340,9 +522582,35 @@ ${sr.result.output}`;
522340
522582
  for (const batch2 of batches) {
522341
522583
  if (this.aborted)
522342
522584
  break;
522585
+ const batchFingerprintFirstId = /* @__PURE__ */ new Map();
522586
+ const batchInFlight = /* @__PURE__ */ new Map();
522587
+ const buildBatchFp = (call) => {
522588
+ const args = call.args ?? {};
522589
+ const argsKey = Object.entries(args).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${k}=${typeof v === "string" ? v.slice(0, 160) : JSON.stringify(v).slice(0, 160)}`).join(",");
522590
+ return `${call.name}:${argsKey}`;
522591
+ };
522592
+ for (const call of batch2.calls) {
522593
+ const fp = buildBatchFp(call);
522594
+ if (!batchFingerprintFirstId.has(fp)) {
522595
+ batchFingerprintFirstId.set(fp, call.id);
522596
+ }
522597
+ }
522343
522598
  const results = await executeBatch(batch2, async (call) => {
522344
522599
  const originalTc = rawToolCalls.find((tc) => tc.id === call.id);
522345
- return executeSingle(originalTc);
522600
+ const fp = buildBatchFp(call);
522601
+ const firstId = batchFingerprintFirstId.get(fp);
522602
+ if (firstId !== void 0 && call.id !== void 0 && firstId !== call.id) {
522603
+ const inflight = batchInFlight.get(fp);
522604
+ if (inflight) {
522605
+ const cloned = await inflight;
522606
+ if (!cloned)
522607
+ return null;
522608
+ return { tc: { ...cloned.tc, id: call.id }, output: cloned.output };
522609
+ }
522610
+ }
522611
+ const promise = executeSingle(originalTc);
522612
+ batchInFlight.set(fp, promise);
522613
+ return promise;
522346
522614
  }, 5);
522347
522615
  for (const r2 of results) {
522348
522616
  if (r2) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.479",
3
+ "version": "0.187.481",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "open-agents-ai",
9
- "version": "0.187.479",
9
+ "version": "0.187.481",
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.479",
3
+ "version": "0.187.481",
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",