agentsmesh 0.36.0 → 0.38.0

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.
@@ -137,7 +137,7 @@ declare const DEFAULT_RECALL_LIMIT = 10;
137
137
  * lean; without a budget a broad match can return ~450+ rule-tokens. `--all`
138
138
  * (CLI) bypasses both caps. The top result is always kept (see RankOptions).
139
139
  */
140
- declare const DEFAULT_RECALL_MAX_TOKENS = 400;
140
+ declare const DEFAULT_RECALL_MAX_TOKENS = 1200;
141
141
  /**
142
142
  * Rank matched lessons by relevance and apply optional caps. Three lightweight
143
143
  * signals are weighted-reciprocal-rank-fused (RRF): trigger specificity (inverse
@@ -383,13 +383,8 @@ declare function importLegacyLessons(projectRoot: string, options: ImportLegacyO
383
383
  /**
384
384
  * Cross-platform process lock backed by an atomic mkdir.
385
385
  *
386
- * Acquire semantics: mkdir with no `recursive` option is atomic per POSIX and
387
- * returns EEXIST if the directory already exists. That makes it a reliable
388
- * cross-process mutex without any third-party dependency.
389
- *
390
386
  * Stale recovery: the holder writes its PID and start timestamp into the lock
391
- * dir. On contention we peek at the PID: a dead same-host holder is evicted at
392
- * once. A live holder (or one on another host, whose PID we cannot probe) is
387
+ * dir. A dead same-host holder is evicted at once. A live or remote holder is
393
388
  * evicted only past `staleMs` — an hours-long bound that catches a hung
394
389
  * process or a recycled PID, never a slow but healthy run.
395
390
  */
package/dist/lessons.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { o as RankedLesson, j as LessonsQuery, A as AddLessonInput, a as AddLessonOptions, b as AddLessonResult } from './init-Dtp4V1_v.js';
2
- export { c as AddLessonTriggers, D as DEFAULT_RECALL_LIMIT, X as DEFAULT_RECALL_MAX_TOKENS, I as ImportLegacyOptions, d as ImportLegacyReport, Y as LESSONS_LOCK_FILENAME, L as LESSONS_PROCEDURAL_RULE, e as Lesson, f as LessonStatus, g as LessonsGraph, Z as LessonsGraphExistsError, h as LessonsGraphSchema, i as LessonsPaths, M as MatchedLesson, k as MergeLessonsOptions, l as MergeLessonsResult, m as MutateOptions, R as RankOptions, n as RankReason, S as ScaffoldLessonsResult, p as StripMarkersOptions, q as StripMarkersReport, T as Topic, r as Trigger, s as TriggerKind, U as UnknownTopicError, V as ValidationFinding, t as ValidationLevel, u as ValidationReport, v as acquireLessonsLock, w as addLesson, x as graphFilePath, y as importLegacyLessons, _ as lessonsLockPath, z as lessonsPaths, B as loadLessonsGraph, C as mergeLessons, E as mutateLessonsGraph, F as parseGraph, G as queryLessons, H as rankLessons, J as scaffoldLessons, K as serializeGraph, N as stripLegacyMarkers, O as stripMarkersInGraph, P as toRelPath, Q as tryLoadLessonsGraph, W as validateLessonsGraph } from './init-Dtp4V1_v.js';
1
+ import { o as RankedLesson, j as LessonsQuery, A as AddLessonInput, a as AddLessonOptions, b as AddLessonResult } from './init-z8Dlr5Cm.js';
2
+ export { c as AddLessonTriggers, D as DEFAULT_RECALL_LIMIT, X as DEFAULT_RECALL_MAX_TOKENS, I as ImportLegacyOptions, d as ImportLegacyReport, Y as LESSONS_LOCK_FILENAME, L as LESSONS_PROCEDURAL_RULE, e as Lesson, f as LessonStatus, g as LessonsGraph, Z as LessonsGraphExistsError, h as LessonsGraphSchema, i as LessonsPaths, M as MatchedLesson, k as MergeLessonsOptions, l as MergeLessonsResult, m as MutateOptions, R as RankOptions, n as RankReason, S as ScaffoldLessonsResult, p as StripMarkersOptions, q as StripMarkersReport, T as Topic, r as Trigger, s as TriggerKind, U as UnknownTopicError, V as ValidationFinding, t as ValidationLevel, u as ValidationReport, v as acquireLessonsLock, w as addLesson, x as graphFilePath, y as importLegacyLessons, _ as lessonsLockPath, z as lessonsPaths, B as loadLessonsGraph, C as mergeLessons, E as mutateLessonsGraph, F as parseGraph, G as queryLessons, H as rankLessons, J as scaffoldLessons, K as serializeGraph, N as stripLegacyMarkers, O as stripMarkersInGraph, P as toRelPath, Q as tryLoadLessonsGraph, W as validateLessonsGraph } from './init-z8Dlr5Cm.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/lessons.js CHANGED
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
  import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, statSync, renameSync, readdirSync, realpathSync, appendFileSync } from 'fs';
3
3
  import { resolve, dirname, join, relative, sep, basename, extname } from 'path';
4
4
  import { stringify, parse, parseDocument, YAMLSeq, YAMLMap } from 'yaml';
5
- import { createHash } from 'crypto';
5
+ import { createHash, randomUUID } from 'crypto';
6
6
  import picomatch from 'picomatch';
7
- import { mkdir, rm, writeFile, readFile, stat, lstat, unlink, rename, chmod } from 'fs/promises';
7
+ import { mkdir, rm, writeFile, readFile, stat, lstat, open, rename } from 'fs/promises';
8
8
  import { tmpdir, hostname } from 'os';
9
- import 'timers/promises';
9
+ import { setTimeout as setTimeout$1 } from 'timers/promises';
10
10
 
11
11
  // src/lessons/graph-schema.ts
12
12
  var CURRENT_GRAPH_VERSION = 2;
@@ -1190,8 +1190,7 @@ async function acquireProcessLock(lockPath, opts = {}) {
1190
1190
  if (acquired) return acquired;
1191
1191
  const existing = await inspectLock(lockPath);
1192
1192
  if (existing !== "young" && isStale(existing, stale)) {
1193
- await rm(lockPath, { recursive: true, force: true }).catch(() => {
1194
- });
1193
+ await rm(lockPath, { recursive: true, force: true });
1195
1194
  continue;
1196
1195
  }
1197
1196
  if (attempt >= retries) {
@@ -1215,7 +1214,13 @@ async function tryAcquire(lockPath) {
1215
1214
  started: Date.now(),
1216
1215
  hostname: getHostname()
1217
1216
  };
1218
- await writeFile(metadataPath, JSON.stringify(metadata), "utf-8");
1217
+ try {
1218
+ await writeFile(metadataPath, JSON.stringify(metadata), "utf-8");
1219
+ } catch (error) {
1220
+ await rm(lockPath, { recursive: true, force: true }).catch(() => {
1221
+ });
1222
+ throw error;
1223
+ }
1219
1224
  let released = false;
1220
1225
  const cleanup = () => {
1221
1226
  if (released) return;
@@ -1582,6 +1587,28 @@ function collectFanout(graph, findings) {
1582
1587
  function normalizeRule2(rule) {
1583
1588
  return rule.trim().replace(/\s+/g, " ").toLowerCase();
1584
1589
  }
1590
+ var TIED_TRIGGER_SET_THRESHOLD = 5;
1591
+ function collectTriggerSetCollisions(graph, findings) {
1592
+ const bySet = /* @__PURE__ */ new Map();
1593
+ for (const [id, lesson] of Object.entries(graph.lessons)) {
1594
+ if (lesson.status !== "active") continue;
1595
+ const key = [...lesson.triggers].sort().join(",");
1596
+ if (key === "") continue;
1597
+ const group = bySet.get(key);
1598
+ if (group === void 0) bySet.set(key, [id]);
1599
+ else group.push(id);
1600
+ }
1601
+ const contested = [...bySet.values()].filter((ids) => ids.length > TIED_TRIGGER_SET_THRESHOLD);
1602
+ if (contested.length === 0) return;
1603
+ const largest = contested.reduce((a, b) => b.length > a.length ? b : a);
1604
+ const affected = contested.reduce((n, ids) => n + ids.length, 0);
1605
+ findings.push({
1606
+ level: "warning",
1607
+ code: "TIED_TRIGGER_SETS",
1608
+ message: `${contested.length} trigger set(s) are each shared by more than ${TIED_TRIGGER_SET_THRESHOLD} active lessons (largest ${largest.length}, ${affected} lessons in total). Identical triggers score identically on specificity, so recall picks among them by weaker signals \u2014 split the triggers so the lesson that matters for a given file can win.`,
1609
+ lessonIds: largest.sort()
1610
+ });
1611
+ }
1585
1612
 
1586
1613
  // src/lessons/validate-keywords.ts
1587
1614
  function collectLowSignalKeywords(graph, findings) {
@@ -1646,6 +1673,7 @@ function validateLessonsGraph(graph, options = {}) {
1646
1673
  collectDuplicateTriggers(graph, findings);
1647
1674
  collectOrphans(graph, findings);
1648
1675
  collectFanout(graph, findings);
1676
+ collectTriggerSetCollisions(graph, findings);
1649
1677
  collectLowSignalKeywords(graph, findings);
1650
1678
  collectStopwordKeywords(graph, findings);
1651
1679
  collectRunnerAnchoredPatterns(graph, findings);
@@ -2239,7 +2267,7 @@ function buildTopicCoherence(matches) {
2239
2267
 
2240
2268
  // src/lessons/ranking.ts
2241
2269
  var DEFAULT_RECALL_LIMIT = 10;
2242
- var DEFAULT_RECALL_MAX_TOKENS = 400;
2270
+ var DEFAULT_RECALL_MAX_TOKENS = 1200;
2243
2271
  var RRF_K = 60;
2244
2272
  var SPECIFICITY_WEIGHT = 5;
2245
2273
  var TOPIC_COHERENCE_WEIGHT = 2;
@@ -3038,6 +3066,91 @@ function shouldNormalizeLineEndings(path) {
3038
3066
  const base = basename(path).toLowerCase();
3039
3067
  return TEXT_DOTFILES.has(base);
3040
3068
  }
3069
+ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
3070
+ ".png",
3071
+ ".jpg",
3072
+ ".jpeg",
3073
+ ".gif",
3074
+ ".bmp",
3075
+ ".ico",
3076
+ ".webp",
3077
+ ".avif",
3078
+ ".tiff",
3079
+ ".pdf",
3080
+ ".zip",
3081
+ ".gz",
3082
+ ".tgz",
3083
+ ".bz2",
3084
+ ".xz",
3085
+ ".7z",
3086
+ ".rar",
3087
+ ".jar",
3088
+ ".woff",
3089
+ ".woff2",
3090
+ ".ttf",
3091
+ ".otf",
3092
+ ".eot",
3093
+ ".mp3",
3094
+ ".mp4",
3095
+ ".wav",
3096
+ ".ogg",
3097
+ ".webm",
3098
+ ".mov",
3099
+ ".wasm",
3100
+ ".bin",
3101
+ ".dat",
3102
+ ".db",
3103
+ ".sqlite",
3104
+ ".so",
3105
+ ".dylib",
3106
+ ".dll",
3107
+ ".exe",
3108
+ ".class",
3109
+ ".pyc",
3110
+ // Office and design documents are zip or proprietary containers.
3111
+ ".xlsx",
3112
+ ".xls",
3113
+ ".docx",
3114
+ ".doc",
3115
+ ".pptx",
3116
+ ".ppt",
3117
+ ".odt",
3118
+ ".ods",
3119
+ ".psd",
3120
+ ".ai",
3121
+ ".sketch",
3122
+ ".fig",
3123
+ ".heic",
3124
+ ".heif",
3125
+ // Archives, columnar data, models and compressed payloads.
3126
+ ".tar",
3127
+ ".zst",
3128
+ ".br",
3129
+ ".lz4",
3130
+ ".parquet",
3131
+ ".avro",
3132
+ ".orc",
3133
+ ".pkl",
3134
+ ".npy",
3135
+ ".npz",
3136
+ ".onnx",
3137
+ ".pt",
3138
+ ".safetensors",
3139
+ ".gguf",
3140
+ ".sqlite3",
3141
+ ".avi",
3142
+ ".mkv",
3143
+ ".flac",
3144
+ ".aac",
3145
+ ".m4a",
3146
+ ".ttc"
3147
+ ]);
3148
+ function isBinaryPayloadPath(path) {
3149
+ return BINARY_EXTENSIONS.has(extname(path).toLowerCase());
3150
+ }
3151
+ function payloadEncodingFor(path) {
3152
+ return isBinaryPayloadPath(path) ? "latin1" : "utf-8";
3153
+ }
3041
3154
  function normalizeLineEndings(content) {
3042
3155
  return content.replace(/\r\n?/g, "\n");
3043
3156
  }
@@ -3045,11 +3158,28 @@ var EXECUTABLE_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".sh", ".bash", ".zs
3045
3158
  function executableModeFor(path) {
3046
3159
  return EXECUTABLE_SCRIPT_EXTENSIONS.has(extname(path).toLowerCase()) ? 493 : void 0;
3047
3160
  }
3161
+ var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY", "ENOTEMPTY", "EEXIST"]);
3162
+ async function renameWithRetry(from, to, options = {}) {
3163
+ const attempts = options.attempts ?? 5;
3164
+ const delayMs = options.delayMs ?? 50;
3165
+ for (let attempt = 0; ; attempt++) {
3166
+ try {
3167
+ await rename(from, to);
3168
+ return;
3169
+ } catch (err) {
3170
+ const code = err.code;
3171
+ const transient = code !== void 0 && TRANSIENT_RENAME_CODES.has(code);
3172
+ if (!transient || attempt >= attempts - 1) throw err;
3173
+ await setTimeout$1(delayMs * 2 ** attempt);
3174
+ }
3175
+ }
3176
+ }
3048
3177
 
3049
3178
  // src/utils/filesystem/fs.ts
3050
3179
  async function readFileSafe(path) {
3051
3180
  try {
3052
- const data = await readFile(path, "utf-8");
3181
+ const data = await readFile(path, payloadEncodingFor(path));
3182
+ if (isBinaryPayloadPath(path)) return data;
3053
3183
  return data.startsWith(UTF8_BOM) ? data.slice(UTF8_BOM.length) : data;
3054
3184
  } catch (err) {
3055
3185
  const e = err;
@@ -3073,40 +3203,28 @@ async function writeFileAtomic(path, content, options) {
3073
3203
  { errnoCode: "EISDIR" }
3074
3204
  );
3075
3205
  }
3076
- if (info.isSymbolicLink()) {
3077
- await unlink(path).catch((e) => {
3078
- if (e.code !== "ENOENT") throw e;
3079
- });
3080
- }
3081
3206
  } catch (err) {
3082
3207
  if (err instanceof FileSystemError) throw err;
3083
3208
  const e = err;
3084
3209
  if (e.code !== "ENOENT") throw err;
3085
3210
  }
3086
- const tmpPath = `${path}.tmp`;
3211
+ const tmpPath = `${path}.tmp-${randomUUID()}`;
3087
3212
  const payload = shouldNormalizeLineEndings(path) ? normalizeLineEndings(content) : content;
3088
3213
  const mode = executableModeFor(path);
3214
+ let handle;
3215
+ let ownsTemporaryFile = false;
3089
3216
  try {
3090
- try {
3091
- const tmpInfo = await lstat(tmpPath);
3092
- if (tmpInfo.isSymbolicLink()) {
3093
- await unlink(tmpPath);
3094
- }
3095
- } catch (tmpErr) {
3096
- if (tmpErr.code !== "ENOENT") throw tmpErr;
3097
- }
3098
- const writeOpts = {
3099
- encoding: "utf-8",
3100
- flag: "w"
3101
- };
3102
- if (mode !== void 0) writeOpts.mode = mode;
3103
- await writeFile(tmpPath, payload, writeOpts);
3104
- await rename(tmpPath, path);
3105
- if (mode !== void 0) {
3106
- await chmod(path, mode);
3107
- }
3217
+ handle = await open(tmpPath, "wx", mode);
3218
+ ownsTemporaryFile = true;
3219
+ await handle.writeFile(payload, payloadEncodingFor(path));
3220
+ if (mode !== void 0) await handle.chmod(mode);
3221
+ await handle.close();
3222
+ handle = void 0;
3223
+ await renameWithRetry(tmpPath, path);
3108
3224
  } catch (err) {
3109
- await rm(tmpPath, { force: true }).catch(() => {
3225
+ await handle?.close().catch(() => {
3226
+ });
3227
+ if (ownsTemporaryFile) await rm(tmpPath, { force: true }).catch(() => {
3110
3228
  });
3111
3229
  const e = err;
3112
3230
  throw new FileSystemError(