opencode-swarm 7.51.1 → 7.51.3

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/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.51.1",
55
+ version: "7.51.3",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -99,7 +99,7 @@ var init_package = __esm(() => {
99
99
  check: "biome check --write .",
100
100
  dev: "bun run build && opencode",
101
101
  "package:smoke": "node scripts/package-smoke.mjs",
102
- prepublishOnly: "bun run build",
102
+ prepare: "bun run build",
103
103
  "repro:704": "node scripts/repro-704.mjs"
104
104
  },
105
105
  dependencies: {
@@ -35916,7 +35916,7 @@ var init_task_file = __esm(() => {
35916
35916
 
35917
35917
  // src/hooks/knowledge-store.ts
35918
35918
  import { existsSync as existsSync7 } from "fs";
35919
- import { appendFile as appendFile2, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
35919
+ import { appendFile as appendFile2, mkdir as mkdir2, readFile as readFile3 } from "fs/promises";
35920
35920
  import * as os3 from "os";
35921
35921
  import * as path11 from "path";
35922
35922
  function resolveSwarmKnowledgePath(directory) {
@@ -36079,23 +36079,25 @@ async function rewriteKnowledge(filePath, entries) {
36079
36079
  }
36080
36080
  }
36081
36081
  }
36082
- async function enforceKnowledgeCap(filePath, maxEntries) {
36083
- let release = null;
36082
+ async function transactFile(filePath, read, write, mutate) {
36083
+ const dir = path11.dirname(filePath);
36084
36084
  try {
36085
- const dir = path11.dirname(filePath);
36086
36085
  await mkdir2(dir, { recursive: true });
36086
+ } catch {
36087
+ return false;
36088
+ }
36089
+ let release = null;
36090
+ try {
36087
36091
  release = await import_proper_lockfile3.default.lock(dir, {
36088
36092
  retries: { retries: 5, minTimeout: 100, maxTimeout: 500 },
36089
36093
  stale: 5000
36090
36094
  });
36091
- const entries = await readKnowledge(filePath);
36092
- if (entries.length > maxEntries) {
36093
- const trimmed = entries.slice(entries.length - maxEntries);
36094
- const content = trimmed.map((e) => JSON.stringify(e)).join(`
36095
- `) + (trimmed.length > 0 ? `
36096
- ` : "");
36097
- await writeFile3(filePath, content, "utf-8");
36098
- }
36095
+ const data = await read(filePath);
36096
+ const result = mutate(data);
36097
+ if (result === null)
36098
+ return false;
36099
+ await write(filePath, result);
36100
+ return true;
36099
36101
  } finally {
36100
36102
  if (release) {
36101
36103
  try {
@@ -36104,17 +36106,31 @@ async function enforceKnowledgeCap(filePath, maxEntries) {
36104
36106
  }
36105
36107
  }
36106
36108
  }
36109
+ async function transactKnowledge(filePath, mutate) {
36110
+ return transactFile(filePath, readKnowledge, async (fp, entries) => {
36111
+ const content = entries.map((e) => JSON.stringify(e)).join(`
36112
+ `) + (entries.length > 0 ? `
36113
+ ` : "");
36114
+ await atomicWriteFile(fp, content);
36115
+ }, mutate);
36116
+ }
36117
+ async function enforceKnowledgeCap(filePath, maxEntries) {
36118
+ await transactKnowledge(filePath, (entries) => {
36119
+ if (entries.length <= maxEntries)
36120
+ return null;
36121
+ return entries.slice(entries.length - maxEntries);
36122
+ });
36123
+ }
36107
36124
  async function appendRejectedLesson(directory, lesson) {
36108
36125
  const filePath = resolveSwarmRejectedPath(directory);
36109
- const existing = await readRejectedLessons(directory);
36110
36126
  const MAX = 20;
36111
- const updated = [...existing, lesson];
36112
- if (updated.length > MAX) {
36113
- const trimmed = updated.slice(updated.length - MAX);
36114
- await rewriteKnowledge(filePath, trimmed);
36115
- } else {
36116
- await appendKnowledge(filePath, lesson);
36117
- }
36127
+ await transactKnowledge(filePath, (existing) => {
36128
+ const updated = [...existing, lesson];
36129
+ if (updated.length > MAX) {
36130
+ return updated.slice(updated.length - MAX);
36131
+ }
36132
+ return updated;
36133
+ });
36118
36134
  }
36119
36135
  function normalize2(text) {
36120
36136
  const s = typeof text === "string" ? text : String(text ?? "");
@@ -36243,7 +36259,7 @@ async function applyConfidenceDeltas(filePath, deltas) {
36243
36259
  const content = entries.map((e) => JSON.stringify(e)).join(`
36244
36260
  `) + (entries.length > 0 ? `
36245
36261
  ` : "");
36246
- await writeFile3(filePath, content, "utf-8");
36262
+ await atomicWriteFile(filePath, content);
36247
36263
  }
36248
36264
  } catch (err) {
36249
36265
  console.warn(`[knowledge-store] applyConfidenceDeltas failed on ${filePath} (fail-open):`, err instanceof Error ? err.message : String(err));
@@ -36262,7 +36278,7 @@ var init_knowledge_store = __esm(() => {
36262
36278
  });
36263
36279
 
36264
36280
  // src/hooks/knowledge-validator.ts
36265
- import { appendFile as appendFile3, mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
36281
+ import { appendFile as appendFile3, mkdir as mkdir3 } from "fs/promises";
36266
36282
  import * as path12 from "path";
36267
36283
  function normalizeText(text) {
36268
36284
  return text.normalize("NFKC").toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
@@ -36439,7 +36455,8 @@ async function quarantineEntry(directory, entryId, reason, reportedBy) {
36439
36455
  let release;
36440
36456
  try {
36441
36457
  release = await import_proper_lockfile4.default.lock(swarmDir, {
36442
- retries: { retries: 3, minTimeout: 100 }
36458
+ retries: { retries: 5, minTimeout: 100, maxTimeout: 500 },
36459
+ stale: 5000
36443
36460
  });
36444
36461
  const entries = await readKnowledge(knowledgePath);
36445
36462
  const entry = entries.find((e) => e.id === entryId);
@@ -36458,7 +36475,7 @@ async function quarantineEntry(directory, entryId, reason, reportedBy) {
36458
36475
  const jsonlContent = remaining.length > 0 ? `${remaining.map((e) => JSON.stringify(e)).join(`
36459
36476
  `)}
36460
36477
  ` : "";
36461
- await writeFile4(knowledgePath, jsonlContent, "utf-8");
36478
+ await atomicWriteFile(knowledgePath, jsonlContent);
36462
36479
  await appendFile3(quarantinePath, `${JSON.stringify(quarantined)}
36463
36480
  `, "utf-8");
36464
36481
  const quarantinedEntries = await readKnowledge(quarantinePath);
@@ -36467,7 +36484,7 @@ async function quarantineEntry(directory, entryId, reason, reportedBy) {
36467
36484
  const capContent = trimmed.length > 0 ? `${trimmed.map((e) => JSON.stringify(e)).join(`
36468
36485
  `)}
36469
36486
  ` : "";
36470
- await writeFile4(quarantinePath, capContent, "utf-8");
36487
+ await atomicWriteFile(quarantinePath, capContent);
36471
36488
  }
36472
36489
  const rejectedRecord = {
36473
36490
  id: entryId,
@@ -36502,7 +36519,8 @@ async function restoreEntry(directory, entryId) {
36502
36519
  let release;
36503
36520
  try {
36504
36521
  release = await import_proper_lockfile4.default.lock(swarmDir, {
36505
- retries: { retries: 3, minTimeout: 100 }
36522
+ retries: { retries: 5, minTimeout: 100, maxTimeout: 500 },
36523
+ stale: 5000
36506
36524
  });
36507
36525
  const quarantinedEntries = await readKnowledge(quarantinePath);
36508
36526
  const entryToRestore = quarantinedEntries.find((e) => e.id === entryId);
@@ -36531,7 +36549,7 @@ async function restoreEntry(directory, entryId) {
36531
36549
  const jsonlContent = remaining.length > 0 ? `${remaining.map((e) => JSON.stringify(e)).join(`
36532
36550
  `)}
36533
36551
  ` : "";
36534
- await writeFile4(quarantinePath, jsonlContent, "utf-8");
36552
+ await atomicWriteFile(quarantinePath, jsonlContent);
36535
36553
  await appendFile3(knowledgePath, `${JSON.stringify(original)}
36536
36554
  `, "utf-8");
36537
36555
  const rejectedEntries = await readKnowledge(rejectedPath);
@@ -36539,7 +36557,7 @@ async function restoreEntry(directory, entryId) {
36539
36557
  const rejectedContent = filtered.length > 0 ? `${filtered.map((e) => JSON.stringify(e)).join(`
36540
36558
  `)}
36541
36559
  ` : "";
36542
- await writeFile4(rejectedPath, rejectedContent, "utf-8");
36560
+ await atomicWriteFile(rejectedPath, rejectedContent);
36543
36561
  } finally {
36544
36562
  if (release) {
36545
36563
  await release();
@@ -36548,6 +36566,7 @@ async function restoreEntry(directory, entryId) {
36548
36566
  }
36549
36567
  var import_proper_lockfile4, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, VALID_CATEGORIES, TECH_REFERENCE_WORDS, ACTION_VERB_WORDS, NEGATION_PAIRS, ALLOWED_SKILL_PATH_PREFIXES, VALID_DIRECTIVE_PRIORITIES;
36550
36568
  var init_knowledge_validator = __esm(() => {
36569
+ init_task_file();
36551
36570
  init_logger();
36552
36571
  init_knowledge_store();
36553
36572
  import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
@@ -36666,7 +36685,7 @@ var init_knowledge_validator = __esm(() => {
36666
36685
 
36667
36686
  // src/services/skill-generator.ts
36668
36687
  import { existsSync as existsSync8, unlinkSync as unlinkSync5 } from "fs";
36669
- import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile5 } from "fs/promises";
36688
+ import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile3 } from "fs/promises";
36670
36689
  import * as path13 from "path";
36671
36690
  function sanitizeSlug(input) {
36672
36691
  const lc = input.toLowerCase().trim();
@@ -36858,7 +36877,7 @@ function escapeMarkdown(s) {
36858
36877
  async function atomicWrite(p, content) {
36859
36878
  await mkdir4(path13.dirname(p), { recursive: true });
36860
36879
  const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
36861
- await writeFile5(tmp, content, "utf-8");
36880
+ await writeFile3(tmp, content, "utf-8");
36862
36881
  await rename3(tmp, p);
36863
36882
  }
36864
36883
  async function generateSkills(req) {
@@ -37446,7 +37465,7 @@ var init_hive_promoter = __esm(() => {
37446
37465
 
37447
37466
  // src/hooks/knowledge-events.ts
37448
37467
  import { existsSync as existsSync10 } from "fs";
37449
- import { appendFile as appendFile4, mkdir as mkdir5, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
37468
+ import { appendFile as appendFile4, mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
37450
37469
  import * as path16 from "path";
37451
37470
  function resolveKnowledgeEventsPath(directory) {
37452
37471
  return path16.join(directory, ".swarm", "knowledge-events.jsonl");
@@ -37781,8 +37800,7 @@ async function processRetractions(retractions, directory) {
37781
37800
  }
37782
37801
  async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, config3, options) {
37783
37802
  const knowledgePath = resolveSwarmKnowledgePath(directory);
37784
- const existingEntries = await readKnowledge(knowledgePath) ?? [];
37785
- let stored = 0;
37803
+ const snapshot = await readKnowledge(knowledgePath) ?? [];
37786
37804
  let skipped = 0;
37787
37805
  let rejected = 0;
37788
37806
  const categoryByTag = new Map([
@@ -37797,6 +37815,8 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
37797
37815
  ["other", "other"],
37798
37816
  ["todo", "todo"]
37799
37817
  ]);
37818
+ const snapshotPlusNew = [...snapshot];
37819
+ const toAdd = [];
37800
37820
  for (const lesson of lessons) {
37801
37821
  const tags = inferTags(lesson);
37802
37822
  let category = "process";
@@ -37811,7 +37831,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
37811
37831
  scope: "global",
37812
37832
  confidence: computeConfidence(0, true)
37813
37833
  };
37814
- const result = validateLesson(lesson, existingEntries.map((e) => e.lesson), meta3);
37834
+ const result = validateLesson(lesson, snapshotPlusNew.map((e) => e.lesson), meta3);
37815
37835
  if (result.valid === false || result.severity === "error") {
37816
37836
  const rejectedLesson = {
37817
37837
  id: crypto.randomUUID(),
@@ -37824,7 +37844,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
37824
37844
  rejected++;
37825
37845
  continue;
37826
37846
  }
37827
- const duplicate = findNearDuplicate(lesson, existingEntries, config3.dedup_threshold);
37847
+ const duplicate = findNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
37828
37848
  if (duplicate) {
37829
37849
  skipped++;
37830
37850
  continue;
@@ -37856,9 +37876,20 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
37856
37876
  project_name: projectName,
37857
37877
  auto_generated: true
37858
37878
  };
37859
- await appendKnowledge(knowledgePath, entry);
37860
- stored++;
37861
- existingEntries.push(entry);
37879
+ toAdd.push(entry);
37880
+ snapshotPlusNew.push(entry);
37881
+ }
37882
+ let stored = 0;
37883
+ if (toAdd.length > 0) {
37884
+ await transactKnowledge(knowledgePath, (current) => {
37885
+ const trulyNew = toAdd.filter((e) => !findNearDuplicate(e.lesson, current, config3.dedup_threshold));
37886
+ const extraDups = toAdd.length - trulyNew.length;
37887
+ skipped += extraDups;
37888
+ if (trulyNew.length === 0)
37889
+ return null;
37890
+ stored = trulyNew.length;
37891
+ return [...current, ...trulyNew];
37892
+ });
37862
37893
  }
37863
37894
  await enforceKnowledgeCap(knowledgePath, config3.swarm_max_entries);
37864
37895
  if (!options?.skipAutoPromotion) {
@@ -38100,7 +38131,7 @@ var init_skill_improver_llm_factory = __esm(() => {
38100
38131
 
38101
38132
  // src/services/skill-improver-quota.ts
38102
38133
  import { existsSync as existsSync11 } from "fs";
38103
- import { mkdir as mkdir6, readFile as readFile6, rename as rename4, writeFile as writeFile7 } from "fs/promises";
38134
+ import { mkdir as mkdir6, readFile as readFile6, rename as rename4, writeFile as writeFile5 } from "fs/promises";
38104
38135
  import * as path17 from "path";
38105
38136
  async function acquireLock(dir) {
38106
38137
  const acquire = import_proper_lockfile6.default.lock(dir, LOCK_RETRY_OPTS);
@@ -38147,7 +38178,7 @@ async function readState(filePath) {
38147
38178
  async function writeState(filePath, state) {
38148
38179
  await mkdir6(path17.dirname(filePath), { recursive: true });
38149
38180
  const tmp = `${filePath}.tmp-${process.pid}`;
38150
- await writeFile7(tmp, JSON.stringify(state, null, 2), "utf-8");
38181
+ await writeFile5(tmp, JSON.stringify(state, null, 2), "utf-8");
38151
38182
  await rename4(tmp, filePath);
38152
38183
  }
38153
38184
  async function getQuotaState(directory, opts) {
@@ -38234,7 +38265,7 @@ var init_skill_improver_quota = __esm(() => {
38234
38265
 
38235
38266
  // src/services/skill-improver.ts
38236
38267
  import { existsSync as existsSync12 } from "fs";
38237
- import { mkdir as mkdir7, rename as rename5, writeFile as writeFile8 } from "fs/promises";
38268
+ import { mkdir as mkdir7, rename as rename5, writeFile as writeFile6 } from "fs/promises";
38238
38269
  import * as path18 from "path";
38239
38270
  function timestampSlug(d) {
38240
38271
  return d.toISOString().replace(/[:.]/g, "-");
@@ -38242,7 +38273,7 @@ function timestampSlug(d) {
38242
38273
  async function atomicWrite2(p, content) {
38243
38274
  await mkdir7(path18.dirname(p), { recursive: true });
38244
38275
  const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
38245
- await writeFile8(tmp, content, "utf-8");
38276
+ await writeFile6(tmp, content, "utf-8");
38246
38277
  await rename5(tmp, p);
38247
38278
  }
38248
38279
  async function gatherInventory(directory) {
@@ -45945,7 +45976,7 @@ var KNOWLEDGE_SCHEMA_VERSION = 2;
45945
45976
  // src/hooks/knowledge-migrator.ts
45946
45977
  import { randomUUID as randomUUID3 } from "crypto";
45947
45978
  import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
45948
- import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
45979
+ import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
45949
45980
  import * as path29 from "path";
45950
45981
  async function migrateKnowledgeToExternal(_directory, _config) {
45951
45982
  return {
@@ -46176,7 +46207,7 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
46176
46207
  migration_tool: "knowledge-migrator.ts"
46177
46208
  };
46178
46209
  await mkdir8(path29.dirname(sentinelPath), { recursive: true });
46179
- await writeFile9(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
46210
+ await writeFile7(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
46180
46211
  }
46181
46212
  var _internals19;
46182
46213
  var init_knowledge_migrator = __esm(() => {
@@ -47229,7 +47260,7 @@ import {
47229
47260
  mkdir as mkdir9,
47230
47261
  readFile as readFile10,
47231
47262
  rename as rename6,
47232
- writeFile as writeFile10
47263
+ writeFile as writeFile8
47233
47264
  } from "fs/promises";
47234
47265
  import * as path30 from "path";
47235
47266
 
@@ -47640,7 +47671,7 @@ async function writeJsonlAtomic(filePath, values) {
47640
47671
  const content = values.map((value) => JSON.stringify(value)).join(`
47641
47672
  `) + (values.length > 0 ? `
47642
47673
  ` : "");
47643
- await writeFile10(tmp, content, "utf-8");
47674
+ await writeFile8(tmp, content, "utf-8");
47644
47675
  await rename6(tmp, filePath);
47645
47676
  }
47646
47677
  var init_local_jsonl_provider = __esm(() => {
@@ -47661,7 +47692,7 @@ var init_prompt_block = __esm(() => {
47661
47692
 
47662
47693
  // src/memory/jsonl-migration.ts
47663
47694
  import { existsSync as existsSync21 } from "fs";
47664
- import { copyFile, mkdir as mkdir10, readFile as readFile11, stat as stat3, writeFile as writeFile11 } from "fs/promises";
47695
+ import { copyFile, mkdir as mkdir10, readFile as readFile11, stat as stat3, writeFile as writeFile9 } from "fs/promises";
47665
47696
  import * as path31 from "path";
47666
47697
  function resolveMemoryStorageDir(rootDirectory, config3 = {}) {
47667
47698
  const resolved = resolveConfig(config3);
@@ -47709,14 +47740,14 @@ async function writeJsonlExport(rootDirectory, config3, memories, proposals) {
47709
47740
  await mkdir10(exportDir, { recursive: true });
47710
47741
  const memoriesPath = path31.join(exportDir, "memories.jsonl");
47711
47742
  const proposalsPath = path31.join(exportDir, "proposals.jsonl");
47712
- await writeFile11(memoriesPath, toJsonl(memories), "utf-8");
47713
- await writeFile11(proposalsPath, toJsonl(proposals), "utf-8");
47743
+ await writeFile9(memoriesPath, toJsonl(memories), "utf-8");
47744
+ await writeFile9(proposalsPath, toJsonl(proposals), "utf-8");
47714
47745
  return { directory: exportDir, memoriesPath, proposalsPath };
47715
47746
  }
47716
47747
  async function writeMigrationReport(rootDirectory, report, config3 = {}) {
47717
47748
  const reportPath = path31.join(resolveMemoryStorageDir(rootDirectory, config3), "migration-report.json");
47718
47749
  await mkdir10(path31.dirname(reportPath), { recursive: true });
47719
- await writeFile11(reportPath, `${JSON.stringify(report, null, 2)}
47750
+ await writeFile9(reportPath, `${JSON.stringify(report, null, 2)}
47720
47751
  `, "utf-8");
47721
47752
  return reportPath;
47722
47753
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared context sanitizer for architect-context injection blocks.
3
+ *
4
+ * Any text that enters the architect context from untrusted sources (run-memory
5
+ * failure reasons, drift reports, curator briefings, rejected-pattern warnings,
6
+ * or knowledge lessons) MUST pass through sanitizeContextText() before injection.
7
+ *
8
+ * Threat model:
9
+ * - Control characters (except tab/LF/CR) that can produce invisible payloads
10
+ * - Zero-width characters used to hide injected instructions
11
+ * - BiDi override characters used for visual spoofing
12
+ * - Triple-backtick sequences used to break out of code blocks
13
+ * - `system:` / `SYSTEM:` prefix lines that mimic system-prompt directives
14
+ * - XML-style `<system>`, `<tool_call>` tags and all `</tag>` closing tags used for structured prompt injection
15
+ */
16
+ /**
17
+ * Sanitizes arbitrary context text to prevent prompt injection into LLM context.
18
+ *
19
+ * Preserves human-readable formatting (spaces, newlines, tabs) while neutralizing
20
+ * instruction-like payloads. Idempotent: applying twice yields the same result.
21
+ *
22
+ * @param text - The raw text to sanitize
23
+ * @returns Sanitized text safe for LLM context injection
24
+ */
25
+ export declare function sanitizeContextText(text: string): string;
@@ -158,7 +158,7 @@ export declare function runCuratorPhase(directory: string, phase: number, agents
158
158
  }, llmDelegate?: CuratorLLMDelegate): Promise<CuratorPhaseResult>;
159
159
  /**
160
160
  * Apply curator knowledge recommendations: promote, archive, or flag contradictions.
161
- * Uses readKnowledge + rewriteKnowledge pattern for atomic updates.
161
+ * Uses transactKnowledge for atomic locked read-modify-write updates.
162
162
  * @param directory - The workspace directory
163
163
  * @param recommendations - Array of knowledge recommendations to apply
164
164
  * @param knowledgeConfig - Knowledge configuration (for path resolution)
@@ -18,6 +18,7 @@ export interface RankedEntry extends KnowledgeEntryBase {
18
18
  };
19
19
  finalScore: number;
20
20
  }
21
+ declare function transactShownFile(shownFile: string, mutate: (data: Record<string, string[]>) => Record<string, string[]> | null): Promise<boolean>;
21
22
  export declare function readMergedKnowledge(directory: string, config: KnowledgeConfig, context?: ProjectContext, opts?: {
22
23
  skipScopeFilter?: boolean;
23
24
  }): Promise<RankedEntry[]>;
@@ -33,4 +34,6 @@ export declare const _internals: {
33
34
  readMergedKnowledge: typeof readMergedKnowledge;
34
35
  updateRetrievalOutcome: typeof updateRetrievalOutcome;
35
36
  scoreDirectiveAgainstContext: typeof scoreDirectiveAgainstContext;
37
+ transactShownFile: typeof transactShownFile;
36
38
  };
39
+ export {};
@@ -22,6 +22,8 @@ export declare function readRetractionRecords(directory: string): Promise<Knowle
22
22
  export declare function appendRetractionRecord(directory: string, record: KnowledgeRetractionRecord): Promise<void>;
23
23
  export declare function appendKnowledge<T>(filePath: string, entry: T): Promise<void>;
24
24
  export declare function rewriteKnowledge<T>(filePath: string, entries: T[]): Promise<void>;
25
+ export declare function transactFile<T>(filePath: string, read: (filePath: string) => Promise<T>, write: (filePath: string, data: T) => Promise<void>, mutate: (data: T) => T | null): Promise<boolean>;
26
+ export declare function transactKnowledge<T>(filePath: string, mutate: (entries: T[]) => T[] | null): Promise<boolean>;
25
27
  export declare function enforceKnowledgeCap<T>(filePath: string, maxEntries: number): Promise<void>;
26
28
  export interface SweepResult {
27
29
  scanned: number;
@@ -73,6 +75,7 @@ export declare const _internals: {
73
75
  readRejectedLessons: typeof readRejectedLessons;
74
76
  appendKnowledge: typeof appendKnowledge;
75
77
  rewriteKnowledge: typeof rewriteKnowledge;
78
+ transactKnowledge: typeof transactKnowledge;
76
79
  enforceKnowledgeCap: typeof enforceKnowledgeCap;
77
80
  sweepAgedEntries: typeof sweepAgedEntries;
78
81
  sweepStaleTodos: typeof sweepStaleTodos;