open-agents-ai 0.187.508 → 0.187.509

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
@@ -523505,13 +523505,7 @@ function checkMutateMtimeDelta(intent, cwd4, ctx3, _result) {
523505
523505
  bestNewest = auxNewest;
523506
523506
  }
523507
523507
  if (bestNewest <= 0) {
523508
- return {
523509
- trustworthy: false,
523510
- syntheticError: `Command (${intent.verb}) reported success but ${cwd4} contains no files. The action either didn't run or had no effect.`,
523511
- intentBucket: bucket,
523512
- outcomeClass: "broken",
523513
- detail: "no files under cwd after mutate intent"
523514
- };
523508
+ return { trustworthy: true, intentBucket: bucket, outcomeClass: "unknown", detail: "empty cwd — verifier abstaining" };
523515
523509
  }
523516
523510
  if (bestNewest < ctx3.startedAt - MTIME_GRACE_MS) {
523517
523511
  const ageSec = Math.round(((ctx3.now ? ctx3.now() : Date.now()) - bestNewest) / 1e3);
@@ -523918,6 +523912,248 @@ var init_postActionVerifier = __esm({
523918
523912
  }
523919
523913
  });
523920
523914
 
523915
+ // packages/orchestrator/dist/errorClusterTracker.js
523916
+ var errorClusterTracker_exports = {};
523917
+ __export(errorClusterTracker_exports, {
523918
+ ErrorClusterTracker: () => ErrorClusterTracker,
523919
+ parseErrors: () => parseErrors
523920
+ });
523921
+ function parseErrors(output) {
523922
+ if (!output || typeof output !== "string")
523923
+ return [];
523924
+ const seen = /* @__PURE__ */ new Set();
523925
+ const out = [];
523926
+ for (const { re, build } of ERROR_PATTERNS) {
523927
+ re.lastIndex = 0;
523928
+ let m2;
523929
+ while ((m2 = re.exec(output)) !== null) {
523930
+ const err = build(m2);
523931
+ if (!err)
523932
+ continue;
523933
+ const key = `${err.file}|${err.line ?? ""}|${err.col ?? ""}|${err.code}|${err.message.slice(0, 32)}`;
523934
+ if (seen.has(key))
523935
+ continue;
523936
+ seen.add(key);
523937
+ out.push(err);
523938
+ }
523939
+ }
523940
+ return out;
523941
+ }
523942
+ function clusterKey(scope, file, code8) {
523943
+ return `${scope}\0${file}\0${code8}`;
523944
+ }
523945
+ function parseClusterKey(key) {
523946
+ const parts = key.split("\0");
523947
+ return { file: parts[1] ?? "", code: parts[2] ?? "" };
523948
+ }
523949
+ function shortFile(p2) {
523950
+ const parts = p2.split("/");
523951
+ return parts.length <= 2 ? p2 : `…/${parts.slice(-2).join("/")}`;
523952
+ }
523953
+ function formatStagnantLine(c9, critical) {
523954
+ const codeLabel = c9.key.code ? `× ${c9.key.code}` : "× errors";
523955
+ const tag = critical ? "STAGNANT-CRIT" : "STAGNANT";
523956
+ return `${tag}: ${shortFile(c9.key.file)} ${c9.count}${codeLabel} unchanged across ${c9.attemptsSinceCountChange} attempts — per-line edits aren't converging; consider reverting the affected region or rewriting it as a unit`;
523957
+ }
523958
+ var ERROR_PATTERNS, DEFAULTS, ErrorClusterTracker;
523959
+ var init_errorClusterTracker = __esm({
523960
+ "packages/orchestrator/dist/errorClusterTracker.js"() {
523961
+ "use strict";
523962
+ ERROR_PATTERNS = [
523963
+ // Style A: file:line:col: severity[: code]: message
523964
+ // Matches gcc, clang, eslint, golangci-lint, ruby, generic POSIX tools.
523965
+ {
523966
+ re: /^([^\s:()]+):(\d+):(\d+):\s*(error|warning|note)(?::\s*([A-Za-z][\w/-]*))?\s*:?\s*(.*)$/gm,
523967
+ build: (m2) => {
523968
+ const file = m2[1];
523969
+ if (/^https?:\/\//.test(file))
523970
+ return null;
523971
+ return {
523972
+ file,
523973
+ line: parseInt(m2[2], 10),
523974
+ col: parseInt(m2[3], 10),
523975
+ severity: m2[4],
523976
+ code: (m2[5] ?? "").trim(),
523977
+ message: (m2[6] ?? "").trim().slice(0, 240)
523978
+ };
523979
+ }
523980
+ },
523981
+ // Style B: file(line,col): severity CODE: message (TypeScript)
523982
+ {
523983
+ re: /^([^\s(]+):?\s*\((\d+),(\d+)\)\s*:?\s*(error|warning|note)\s+([A-Z]+\d+)\s*:\s*(.*)$/gm,
523984
+ build: (m2) => ({
523985
+ file: m2[1],
523986
+ line: parseInt(m2[2], 10),
523987
+ col: parseInt(m2[3], 10),
523988
+ severity: m2[4],
523989
+ code: m2[5],
523990
+ message: (m2[6] ?? "").trim().slice(0, 240)
523991
+ })
523992
+ },
523993
+ // Style C: error[CODE]: message ... --> file:line:col (Rust-style)
523994
+ {
523995
+ re: /^error(?:\[([A-Z]\d+)\])?:\s*(.+?)\n\s*-->\s*([^\s:]+):(\d+):(\d+)/gms,
523996
+ build: (m2) => ({
523997
+ file: m2[3],
523998
+ line: parseInt(m2[4], 10),
523999
+ col: parseInt(m2[5], 10),
524000
+ severity: "error",
524001
+ code: (m2[1] ?? "").trim(),
524002
+ message: (m2[2] ?? "").trim().slice(0, 240)
524003
+ })
524004
+ },
524005
+ // Style D: FAILED file::test_id [- message] (pytest, similar)
524006
+ {
524007
+ re: /^FAILED\s+([^\s:]+)::([\w\-.[\]/]+)(?:\s+-\s+(.*))?$/gm,
524008
+ build: (m2) => ({
524009
+ file: m2[1],
524010
+ severity: "error",
524011
+ // Treat the test id as the cluster code so a flapping test stays in
524012
+ // its own bucket, while many distinct tests in the same file each
524013
+ // form their own cluster (more accurate than collapsing all
524014
+ // pytest failures in a file into one).
524015
+ code: m2[2],
524016
+ message: (m2[3] ?? "").trim().slice(0, 240)
524017
+ })
524018
+ }
524019
+ ];
524020
+ DEFAULTS = {
524021
+ warnAttempts: 3,
524022
+ criticalAttempts: 5,
524023
+ cooldownAttempts: 2,
524024
+ maxClusters: 200
524025
+ };
524026
+ ErrorClusterTracker = class {
524027
+ opts;
524028
+ now;
524029
+ clusters = /* @__PURE__ */ new Map();
524030
+ lastEmittedAt = /* @__PURE__ */ new Map();
524031
+ // key → observations counter at last emit
524032
+ observationCounter = 0;
524033
+ constructor(options2 = {}) {
524034
+ this.opts = { ...DEFAULTS, ...options2 };
524035
+ this.now = options2.now ?? Date.now;
524036
+ }
524037
+ /**
524038
+ * Feed a new tool output into the tracker. Returns the changes for this
524039
+ * observation plus a one-line summary suitable for context injection.
524040
+ *
524041
+ * @param output raw stdout/stderr from the tool
524042
+ * @param toolKey optional caller-supplied scope (e.g. "tsc:/path/to/cwd").
524043
+ * If two distinct tools emit similar errors in the same
524044
+ * file but in different scopes, pass distinct keys to
524045
+ * keep their clusters separate. Default: "" (shared).
524046
+ */
524047
+ observe(output, toolKey = "") {
524048
+ this.observationCounter++;
524049
+ const errors = parseErrors(output);
524050
+ const observedClusters = /* @__PURE__ */ new Map();
524051
+ for (const e2 of errors) {
524052
+ const key = clusterKey(toolKey, e2.file, e2.code);
524053
+ const cur = observedClusters.get(key);
524054
+ if (cur)
524055
+ cur.count += 1;
524056
+ else
524057
+ observedClusters.set(key, { count: 1, sample: e2.message });
524058
+ }
524059
+ const changes = [];
524060
+ const lines = [];
524061
+ let hasCritical = false;
524062
+ const nowMs = this.now();
524063
+ for (const [key, { count, sample }] of observedClusters) {
524064
+ const prior = this.clusters.get(key);
524065
+ if (!prior) {
524066
+ const ck = parseClusterKey(key);
524067
+ const entry = {
524068
+ key: ck,
524069
+ count,
524070
+ sample,
524071
+ observations: 1,
524072
+ attemptsSinceCountChange: 1,
524073
+ lastUpdated: nowMs,
524074
+ previousCount: 0
524075
+ };
524076
+ this.clusters.set(key, entry);
524077
+ changes.push({ kind: "new", cluster: entry });
524078
+ continue;
524079
+ }
524080
+ prior.observations++;
524081
+ prior.lastUpdated = nowMs;
524082
+ prior.sample = sample;
524083
+ if (count === prior.count) {
524084
+ prior.attemptsSinceCountChange++;
524085
+ } else {
524086
+ prior.previousCount = prior.count;
524087
+ prior.count = count;
524088
+ prior.attemptsSinceCountChange = 1;
524089
+ }
524090
+ if (count > prior.previousCount && prior.attemptsSinceCountChange === 1) {
524091
+ changes.push({ kind: "grown", cluster: prior, from: prior.previousCount, to: count });
524092
+ lines.push(`${shortFile(prior.key.file)}: ${prior.previousCount}→${count}× ${prior.key.code || "errors"} (regressed)`);
524093
+ } else if (count < prior.previousCount && prior.attemptsSinceCountChange === 1) {
524094
+ changes.push({ kind: "shrunk", cluster: prior, from: prior.previousCount, to: count });
524095
+ lines.push(`${shortFile(prior.key.file)}: ${prior.previousCount}→${count}× ${prior.key.code || "errors"} (improving)`);
524096
+ } else if (prior.attemptsSinceCountChange >= this.opts.warnAttempts) {
524097
+ if (this.lastEmittedAt.has(key)) {
524098
+ const last2 = this.lastEmittedAt.get(key);
524099
+ const sinceEmit = this.observationCounter - last2;
524100
+ if (sinceEmit < this.opts.cooldownAttempts)
524101
+ continue;
524102
+ }
524103
+ const isCritical = prior.attemptsSinceCountChange >= this.opts.criticalAttempts;
524104
+ if (isCritical)
524105
+ hasCritical = true;
524106
+ changes.push({
524107
+ kind: "stagnant",
524108
+ cluster: prior,
524109
+ attempts: prior.attemptsSinceCountChange,
524110
+ severity: isCritical ? "critical" : "warn"
524111
+ });
524112
+ lines.push(formatStagnantLine(prior, isCritical));
524113
+ this.lastEmittedAt.set(key, this.observationCounter);
524114
+ }
524115
+ }
524116
+ for (const [key, prior] of [...this.clusters]) {
524117
+ if (observedClusters.has(key))
524118
+ continue;
524119
+ if (prior.count > 0 && nowMs - prior.lastUpdated < 6e4) {
524120
+ changes.push({ kind: "resolved", key: prior.key });
524121
+ lines.push(`${shortFile(prior.key.file)}: ${prior.count}× ${prior.key.code || "errors"} → 0 (resolved)`);
524122
+ this.clusters.delete(key);
524123
+ this.lastEmittedAt.delete(key);
524124
+ }
524125
+ }
524126
+ this.evictIfNeeded();
524127
+ return {
524128
+ changes,
524129
+ summaryLine: lines.length === 0 ? "" : `[ERROR CLUSTERS] ${lines.join(" | ").slice(0, 360)}`,
524130
+ hasCritical
524131
+ };
524132
+ }
524133
+ /** Read-only snapshot of currently tracked clusters (defensive copy). */
524134
+ snapshot() {
524135
+ return [...this.clusters.values()].map((c9) => ({ ...c9, key: { ...c9.key } }));
524136
+ }
524137
+ /** Remove all state — useful at session boundaries. */
524138
+ reset() {
524139
+ this.clusters.clear();
524140
+ this.lastEmittedAt.clear();
524141
+ this.observationCounter = 0;
524142
+ }
524143
+ evictIfNeeded() {
524144
+ if (this.clusters.size <= this.opts.maxClusters)
524145
+ return;
524146
+ const sorted = [...this.clusters.entries()].sort((a2, b) => a2[1].lastUpdated - b[1].lastUpdated);
524147
+ const toEvict = sorted.slice(0, this.clusters.size - this.opts.maxClusters);
524148
+ for (const [key] of toEvict) {
524149
+ this.clusters.delete(key);
524150
+ this.lastEmittedAt.delete(key);
524151
+ }
524152
+ }
524153
+ };
524154
+ }
524155
+ });
524156
+
523921
524157
  // packages/orchestrator/dist/agenticRunner.js
523922
524158
  import { existsSync as _fsExistsSync, readFileSync as _fsReadFileSync } from "node:fs";
523923
524159
  import { join as _pathJoin } from "node:path";
@@ -528604,6 +528840,25 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
528604
528840
  toolCallLog[_toolLogTailIdx].intentBucket = _verifierResult.intentBucket;
528605
528841
  toolCallLog[_toolLogTailIdx].outcomeClass = _verifierResult.outcomeClass;
528606
528842
  }
528843
+ if (tc.name === "shell" && (result.output || result.error)) {
528844
+ try {
528845
+ const ecMod = await Promise.resolve().then(() => (init_errorClusterTracker(), errorClusterTracker_exports));
528846
+ if (!this._errorClusterTracker) {
528847
+ this._errorClusterTracker = new ecMod.ErrorClusterTracker();
528848
+ }
528849
+ const tracker = this._errorClusterTracker;
528850
+ const scope = _verifierResult?.intentBucket ?? "shell";
528851
+ const obs = tracker.observe(((result.output ?? "") + "\n" + (result.error ?? "")).slice(0, 1e5), scope);
528852
+ if (obs.summaryLine) {
528853
+ if (obs.hasCritical) {
528854
+ messages2.push({ role: "system", content: obs.summaryLine });
528855
+ } else {
528856
+ pushSoftInjection("system", obs.summaryLine);
528857
+ }
528858
+ }
528859
+ } catch {
528860
+ }
528861
+ }
528607
528862
  this._hookManager.runPostToolUse(tc.name, finalArgs, (result.output ?? "").slice(0, 2e3), this._sessionId);
528608
528863
  } catch (err) {
528609
528864
  result = { success: false, output: "", error: err instanceof Error ? err.message : String(err) };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.508",
3
+ "version": "0.187.509",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "open-agents-ai",
9
- "version": "0.187.508",
9
+ "version": "0.187.509",
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.508",
3
+ "version": "0.187.509",
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",