opencode-swarm 7.51.2 → 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 +82 -51
- package/dist/hooks/context-sanitizer.d.ts +25 -0
- package/dist/hooks/curator.d.ts +1 -1
- package/dist/hooks/knowledge-reader.d.ts +3 -0
- package/dist/hooks/knowledge-store.d.ts +3 -0
- package/dist/index.js +293 -271
- package/package.json +1 -1
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.
|
|
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",
|
|
@@ -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
|
|
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
|
|
36083
|
-
|
|
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
|
|
36092
|
-
|
|
36093
|
-
|
|
36094
|
-
|
|
36095
|
-
|
|
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
|
-
|
|
36112
|
-
|
|
36113
|
-
|
|
36114
|
-
|
|
36115
|
-
|
|
36116
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
37860
|
-
|
|
37861
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
47713
|
-
await
|
|
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
|
|
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;
|
package/dist/hooks/curator.d.ts
CHANGED
|
@@ -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
|
|
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;
|