opencode-swarm 7.39.0 → 7.41.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.
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ var package_default;
48
48
  var init_package = __esm(() => {
49
49
  package_default = {
50
50
  name: "opencode-swarm",
51
- version: "7.39.0",
51
+ version: "7.41.0",
52
52
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
53
53
  main: "dist/index.js",
54
54
  types: "dist/index.d.ts",
@@ -15581,6 +15581,13 @@ var init_schema = __esm(() => {
15581
15581
  redaction: exports_external.object({
15582
15582
  rejectDurableSecrets: exports_external.boolean().default(true)
15583
15583
  }).default({ rejectDurableSecrets: true }),
15584
+ maintenance: exports_external.object({
15585
+ lowUtilityMaxConfidence: exports_external.number().min(0).max(1).default(0.45),
15586
+ lowUtilityMinAgeDays: exports_external.number().int().min(1).max(3650).default(30)
15587
+ }).default({
15588
+ lowUtilityMaxConfidence: 0.45,
15589
+ lowUtilityMinAgeDays: 30
15590
+ }),
15584
15591
  hardDelete: exports_external.boolean().default(false)
15585
15592
  });
15586
15593
  CuratorConfigSchema = exports_external.object({
@@ -55884,6 +55891,7 @@ RULES:
55884
55891
  - Output under 2000 chars
55885
55892
  - No code modifications
55886
55893
  - Flag contradictions explicitly with CONTRADICTION: prefix
55894
+ - Memory proposals are for concise durable facts only. Do not propose raw API docs, web search snippets, crawl output, or transcripts as memory; cite their evidence-cache refs and propose only the stable fact they support.
55887
55895
  - If no prior summary exists, state "First session — no prior context"
55888
55896
 
55889
55897
  OUTPUT FORMAT:
@@ -55930,6 +55938,7 @@ RULES:
55930
55938
  - Compliance observations are READ-ONLY — report, do not enforce
55931
55939
  - OBSERVATIONS should not contain directives — report what is observed, do not instruct the architect what to do
55932
55940
  - Extend the digest, never replace it
55941
+ - Memory proposals are for concise durable facts only. Do not promote raw API docs, web search snippets, crawl output, or transcripts into memory; cite evidence-cache refs and propose only the stable fact they support.
55933
55942
 
55934
55943
  OUTPUT FORMAT:
55935
55944
  PHASE_DIGEST:
@@ -66897,6 +66906,10 @@ function resolveMemoryConfig(input) {
66897
66906
  redaction: {
66898
66907
  ...DEFAULT_MEMORY_CONFIG.redaction,
66899
66908
  ...input?.redaction ?? {}
66909
+ },
66910
+ maintenance: {
66911
+ ...DEFAULT_MEMORY_CONFIG.maintenance,
66912
+ ...input?.maintenance ?? {}
66900
66913
  }
66901
66914
  };
66902
66915
  }
@@ -66928,6 +66941,10 @@ var init_config3 = __esm(() => {
66928
66941
  redaction: {
66929
66942
  rejectDurableSecrets: true
66930
66943
  },
66944
+ maintenance: {
66945
+ lowUtilityMaxConfidence: 0.45,
66946
+ lowUtilityMinAgeDays: 30
66947
+ },
66931
66948
  hardDelete: false
66932
66949
  };
66933
66950
  DURABLE_MEMORY_KINDS = new Set([
@@ -67278,6 +67295,17 @@ function validateDecisionMatchesProposal(decision, proposal) {
67278
67295
  throw new MemoryValidationError("curator supersede decision target does not match proposal target");
67279
67296
  }
67280
67297
  }
67298
+ function validateCuratorPromotableMemory(record3) {
67299
+ if (record3.stability !== "durable") {
67300
+ throw new MemoryValidationError("curator memory promotions must be durable facts");
67301
+ }
67302
+ if (!DURABLE_MEMORY_KINDS.has(record3.kind)) {
67303
+ throw new MemoryValidationError("curator memory promotions must use durable fact kinds; store raw docs, search results, and other bulky source material as evidence records instead");
67304
+ }
67305
+ if (normalizeMemoryText(record3.text).length > CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH) {
67306
+ throw new MemoryValidationError(`curator memory promotions must be concise durable facts under ${CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH} characters`);
67307
+ }
67308
+ }
67281
67309
  function applyPatchToMemory(existing, patch, updatedAt) {
67282
67310
  const base = {
67283
67311
  scope: patch.scope ?? existing.scope,
@@ -67335,11 +67363,174 @@ function buildCuratorDecisionEvent(change, proposal) {
67335
67363
  function normalizeTags(tags) {
67336
67364
  return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
67337
67365
  }
67366
+ var CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH = 500;
67338
67367
  var init_curator_decision_helpers = __esm(() => {
67368
+ init_config3();
67339
67369
  init_errors6();
67340
67370
  init_schema2();
67341
67371
  });
67342
67372
 
67373
+ // src/memory/maintenance.ts
67374
+ async function buildMemoryMaintenanceReport(provider, options = {}) {
67375
+ const now = options.now ?? new Date;
67376
+ const limit = Math.max(1, Math.trunc(options.limit ?? 20));
67377
+ const memories = await provider.list({
67378
+ includeExpired: true,
67379
+ includeInactive: true
67380
+ });
67381
+ const proposals = await loadMaintenanceProposals(provider, limit);
67382
+ const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
67383
+ const usageByMemory = summarizeRecallByMemory(recallUsage);
67384
+ const usageByRole = summarizeRecallByRole(recallUsage);
67385
+ const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
67386
+ const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
67387
+ const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
67388
+ const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
67389
+ const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
67390
+ maxConfidence: options.lowUtilityMaxConfidence ?? DEFAULT_LOW_UTILITY_MAX_CONFIDENCE,
67391
+ minAgeDays: options.lowUtilityMinAgeDays ?? DEFAULT_LOW_UTILITY_MIN_AGE_DAYS
67392
+ })).sort(memorySort);
67393
+ const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
67394
+ const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
67395
+ const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
67396
+ return {
67397
+ generatedAt: now.toISOString(),
67398
+ totalMemories: memories.length,
67399
+ activeMemories: activeMemories.length,
67400
+ deletedMemories: deletedMemories.slice(0, limit),
67401
+ expiredScratchMemories: expiredScratchMemories.slice(0, limit),
67402
+ supersededMemories: supersededMemories.slice(0, limit),
67403
+ supersededChains: buildSupersededChains(memories).slice(0, limit),
67404
+ lowUtilityMemories: lowUtilityMemories.slice(0, limit),
67405
+ neverRecalledMemories: neverRecalledMemories.slice(0, limit),
67406
+ mostRecalledMemories: Array.from(usageByMemory.values()).sort((a, b) => b.count - a.count || b.lastRecalledAt.localeCompare(a.lastRecalledAt) || a.memoryId.localeCompare(b.memoryId)).slice(0, limit),
67407
+ recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
67408
+ rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
67409
+ pendingProposals: pendingProposals.slice(0, limit),
67410
+ recallEventCount: recallUsage.length
67411
+ };
67412
+ }
67413
+ function shouldCompactMemory(memory, now = new Date) {
67414
+ if (memory.metadata.deleted === true)
67415
+ return "deleted";
67416
+ if (memory.supersededBy)
67417
+ return "superseded";
67418
+ if (memory.kind === "scratch" && isExpired(memory, now)) {
67419
+ return "expired_scratch";
67420
+ }
67421
+ return null;
67422
+ }
67423
+ function isActiveMemory(memory, now) {
67424
+ return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
67425
+ }
67426
+ function isLowUtility(memory, usageByMemory, now, options) {
67427
+ if (usageByMemory.has(memory.id))
67428
+ return false;
67429
+ const updated = Date.parse(memory.updatedAt);
67430
+ const ageDays = Number.isFinite(updated) ? (now.getTime() - updated) / (24 * 60 * 60 * 1000) : 0;
67431
+ return memory.confidence <= options.maxConfidence || ageDays >= options.minAgeDays;
67432
+ }
67433
+ function summarizeRecallByMemory(usageEvents) {
67434
+ const byMemory = new Map;
67435
+ for (const event of usageEvents) {
67436
+ event.memoryIds.forEach((memoryId, index) => {
67437
+ const role = event.agentRole ?? "unknown";
67438
+ const existing = byMemory.get(memoryId) ?? {
67439
+ memoryId,
67440
+ count: 0,
67441
+ lastRecalledAt: event.timestamp,
67442
+ agentRoles: {},
67443
+ averageScore: 0,
67444
+ scoreTotal: 0,
67445
+ scoreCount: 0
67446
+ };
67447
+ existing.count++;
67448
+ existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
67449
+ existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
67450
+ const score = event.scores[index];
67451
+ if (typeof score === "number" && Number.isFinite(score)) {
67452
+ existing.scoreTotal += score;
67453
+ existing.scoreCount++;
67454
+ existing.averageScore = existing.scoreTotal / existing.scoreCount;
67455
+ }
67456
+ byMemory.set(memoryId, existing);
67457
+ });
67458
+ }
67459
+ return new Map(Array.from(byMemory, ([memoryId, value]) => [
67460
+ memoryId,
67461
+ {
67462
+ memoryId,
67463
+ count: value.count,
67464
+ lastRecalledAt: value.lastRecalledAt,
67465
+ agentRoles: value.agentRoles,
67466
+ averageScore: value.averageScore
67467
+ }
67468
+ ]));
67469
+ }
67470
+ async function loadMaintenanceProposals(provider, limit) {
67471
+ if (!provider.listProposals)
67472
+ return [];
67473
+ const [pending, rejected, recent] = await Promise.all([
67474
+ provider.listProposals({ status: "pending", limit }),
67475
+ provider.listProposals({ status: "rejected", limit }),
67476
+ provider.listProposals({ limit: Math.max(limit * 4, 100) })
67477
+ ]);
67478
+ const byId = new Map;
67479
+ for (const proposal of [...pending, ...rejected, ...recent]) {
67480
+ byId.set(proposal.id, proposal);
67481
+ }
67482
+ return Array.from(byId.values());
67483
+ }
67484
+ function summarizeRecallByRole(usageEvents) {
67485
+ const byRole = new Map;
67486
+ for (const event of usageEvents) {
67487
+ const role = event.agentRole ?? "unknown";
67488
+ const existing = byRole.get(role) ?? {
67489
+ agentRole: role,
67490
+ count: 0,
67491
+ memoryIds: {}
67492
+ };
67493
+ existing.count++;
67494
+ for (const memoryId of event.memoryIds) {
67495
+ existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
67496
+ }
67497
+ byRole.set(role, existing);
67498
+ }
67499
+ return byRole;
67500
+ }
67501
+ function buildSupersededChains(memories) {
67502
+ const byId = new Map(memories.map((memory) => [memory.id, memory]));
67503
+ const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
67504
+ const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
67505
+ return roots.map((root) => {
67506
+ const chain = [root.id];
67507
+ const seen = new Set(chain);
67508
+ let cursor = root;
67509
+ while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
67510
+ chain.push(cursor.supersededBy);
67511
+ seen.add(cursor.supersededBy);
67512
+ cursor = byId.get(cursor.supersededBy);
67513
+ }
67514
+ return {
67515
+ rootId: root.id,
67516
+ chain,
67517
+ reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
67518
+ };
67519
+ });
67520
+ }
67521
+ function memorySort(a, b) {
67522
+ return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
67523
+ }
67524
+ function proposalSort(a, b) {
67525
+ const aTime = a.reviewedAt ?? a.createdAt;
67526
+ const bTime = b.reviewedAt ?? b.createdAt;
67527
+ return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
67528
+ }
67529
+ var DEFAULT_LOW_UTILITY_MAX_CONFIDENCE = 0.45, DEFAULT_LOW_UTILITY_MIN_AGE_DAYS = 30;
67530
+ var init_maintenance = __esm(() => {
67531
+ init_schema2();
67532
+ });
67533
+
67343
67534
  // src/memory/role-profiles.ts
67344
67535
  function resolveMemoryRecallProfile(agentRole) {
67345
67536
  const role = normalizeMemoryAgentRole(agentRole);
@@ -67727,16 +67918,21 @@ class LocalJsonlMemoryProvider {
67727
67918
  }
67728
67919
  async recordRecallUsage(event) {
67729
67920
  await this.initialize();
67730
- await this.audit("recall", event.bundleId, JSON.stringify({
67731
- query: event.query,
67732
- scopes: event.scopes,
67733
- kinds: event.kinds,
67734
- memoryIds: event.memoryIds,
67735
- scores: event.scores,
67736
- tokenEstimate: event.tokenEstimate,
67737
- agentRole: event.agentRole,
67738
- runId: event.runId
67739
- }));
67921
+ await this.audit("recall", event.bundleId, undefined, event);
67922
+ }
67923
+ async listRecallUsage(filter = {}) {
67924
+ await this.initialize();
67925
+ const events = await readAuditEvents(this.pathFor("audit"));
67926
+ const usage = [];
67927
+ for (const event of events) {
67928
+ if (event.operation !== "recall")
67929
+ continue;
67930
+ const parsed = parseRecallUsageEvent(event);
67931
+ if (parsed)
67932
+ usage.push(parsed);
67933
+ }
67934
+ usage.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
67935
+ return usage.slice(0, filter.limit ?? usage.length);
67740
67936
  }
67741
67937
  async list(filter = {}) {
67742
67938
  await this.initialize();
@@ -67756,7 +67952,9 @@ class LocalJsonlMemoryProvider {
67756
67952
  return !Number.isFinite(expires) || expires > now;
67757
67953
  });
67758
67954
  }
67759
- records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
67955
+ if (!filter.includeInactive) {
67956
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
67957
+ }
67760
67958
  records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
67761
67959
  return records.slice(0, filter.limit ?? records.length);
67762
67960
  }
@@ -67797,12 +67995,14 @@ class LocalJsonlMemoryProvider {
67797
67995
  ...decision.memory,
67798
67996
  updatedAt: appliedAt
67799
67997
  });
67998
+ validateCuratorPromotableMemory(memory);
67800
67999
  this.memories.set(memory.id, memory);
67801
68000
  await appendJsonl(this.pathFor("memories"), memory);
67802
68001
  memoryId = memory.id;
67803
68002
  } else if (decision.action === "update") {
67804
68003
  const existing = this.activeMemory(decision.targetMemoryId);
67805
68004
  const updated = this.validateDecisionMemory(applyPatchToMemory(existing, decision.patch, appliedAt));
68005
+ validateCuratorPromotableMemory(updated);
67806
68006
  if (updated.id !== existing.id) {
67807
68007
  const tombstone = this.validateDecisionMemory({
67808
68008
  ...existing,
@@ -67828,6 +68028,7 @@ class LocalJsonlMemoryProvider {
67828
68028
  updatedAt: appliedAt,
67829
68029
  supersedes: Array.from(new Set([...decision.replacement.supersedes ?? [], oldMemory.id]))
67830
68030
  });
68031
+ validateCuratorPromotableMemory(replacement);
67831
68032
  const superseded = this.validateDecisionMemory({
67832
68033
  ...oldMemory,
67833
68034
  updatedAt: appliedAt,
@@ -67868,6 +68069,41 @@ class LocalJsonlMemoryProvider {
67868
68069
  await writeJsonlAtomic(this.pathFor("memories"), Array.from(this.memories.values()));
67869
68070
  await this.audit("compact", "memories");
67870
68071
  }
68072
+ async compactMaintenance(options = {}) {
68073
+ await this.initialize();
68074
+ const now = options.now ? new Date(options.now) : new Date;
68075
+ const kept = [];
68076
+ const result = {
68077
+ dryRun: options.dryRun !== false,
68078
+ removedDeleted: 0,
68079
+ removedSuperseded: 0,
68080
+ removedExpiredScratch: 0,
68081
+ remaining: 0
68082
+ };
68083
+ for (const memory of this.memories.values()) {
68084
+ const compactReason = shouldCompactMemory(memory, now);
68085
+ if (compactReason === "deleted") {
68086
+ result.removedDeleted++;
68087
+ continue;
68088
+ }
68089
+ if (compactReason === "superseded") {
68090
+ result.removedSuperseded++;
68091
+ continue;
68092
+ }
68093
+ if (compactReason === "expired_scratch") {
68094
+ result.removedExpiredScratch++;
68095
+ continue;
68096
+ }
68097
+ kept.push(memory);
68098
+ }
68099
+ result.remaining = kept.length;
68100
+ if (result.dryRun)
68101
+ return result;
68102
+ this.memories = new Map(kept.map((memory) => [memory.id, memory]));
68103
+ await writeJsonlAtomic(this.pathFor("memories"), kept);
68104
+ await this.audit("compact", "memories", "removed deleted, superseded, and expired scratch memories", result);
68105
+ return result;
68106
+ }
67871
68107
  async audit(operation, targetId, reason, eventJson) {
67872
68108
  const event = {
67873
68109
  id: randomUUID4(),
@@ -67946,6 +68182,46 @@ async function readJsonl(filePath) {
67946
68182
  }
67947
68183
  return records;
67948
68184
  }
68185
+ async function readAuditEvents(filePath) {
68186
+ const values = await readJsonl(filePath);
68187
+ const events = [];
68188
+ for (const value of values) {
68189
+ if (!value || typeof value !== "object")
68190
+ continue;
68191
+ const candidate = value;
68192
+ if (typeof candidate.id !== "string" || typeof candidate.operation !== "string" || typeof candidate.targetId !== "string" || typeof candidate.timestamp !== "string") {
68193
+ continue;
68194
+ }
68195
+ events.push(candidate);
68196
+ }
68197
+ return events;
68198
+ }
68199
+ function parseRecallUsageEvent(event) {
68200
+ const raw = event.eventJson ?? event.reason;
68201
+ if (typeof raw !== "string" && (!raw || typeof raw !== "object")) {
68202
+ return null;
68203
+ }
68204
+ try {
68205
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
68206
+ if (!Array.isArray(parsed.memoryIds) || typeof parsed.query !== "string") {
68207
+ return null;
68208
+ }
68209
+ return {
68210
+ bundleId: typeof parsed.bundleId === "string" ? parsed.bundleId : event.targetId,
68211
+ query: parsed.query,
68212
+ scopes: Array.isArray(parsed.scopes) ? parsed.scopes : [],
68213
+ kinds: Array.isArray(parsed.kinds) ? parsed.kinds : undefined,
68214
+ memoryIds: parsed.memoryIds.filter((memoryId) => typeof memoryId === "string"),
68215
+ scores: Array.isArray(parsed.scores) ? parsed.scores.filter((score) => typeof score === "number" && Number.isFinite(score)) : [],
68216
+ tokenEstimate: typeof parsed.tokenEstimate === "number" ? parsed.tokenEstimate : 0,
68217
+ agentRole: typeof parsed.agentRole === "string" ? parsed.agentRole : undefined,
68218
+ runId: typeof parsed.runId === "string" ? parsed.runId : undefined,
68219
+ timestamp: typeof parsed.timestamp === "string" ? parsed.timestamp : event.timestamp
68220
+ };
68221
+ } catch {
68222
+ return null;
68223
+ }
68224
+ }
67949
68225
  async function appendJsonl(filePath, value) {
67950
68226
  await mkdir9(path39.dirname(filePath), { recursive: true });
67951
68227
  await appendFile5(filePath, `${JSON.stringify(value)}
@@ -67965,6 +68241,7 @@ var init_local_jsonl_provider = __esm(() => {
67965
68241
  init_config3();
67966
68242
  init_curator_decision_helpers();
67967
68243
  init_errors6();
68244
+ init_maintenance();
67968
68245
  init_schema2();
67969
68246
  init_scoring();
67970
68247
  });
@@ -68296,6 +68573,10 @@ class SQLiteMemoryProvider {
68296
68573
  redaction: {
68297
68574
  ...DEFAULT_MEMORY_CONFIG.redaction,
68298
68575
  ...config3.redaction ?? {}
68576
+ },
68577
+ maintenance: {
68578
+ ...DEFAULT_MEMORY_CONFIG.maintenance,
68579
+ ...config3.maintenance ?? {}
68299
68580
  }
68300
68581
  };
68301
68582
  }
@@ -68401,6 +68682,26 @@ class SQLiteMemoryProvider {
68401
68682
  ) VALUES (?, ?, ?, ?)`, [randomUUID5(), event.bundleId, event.timestamp, JSON.stringify(event)]);
68402
68683
  await this.event("recall", event.bundleId, JSON.stringify(event));
68403
68684
  }
68685
+ async listRecallUsage(filter = {}) {
68686
+ await this.initialize();
68687
+ const rows = typeof filter.limit === "number" ? this.requireDb().query(`SELECT usage_json
68688
+ FROM memory_recall_usage
68689
+ ORDER BY timestamp DESC
68690
+ LIMIT ?`).all(Math.max(1, Math.trunc(filter.limit))) : this.requireDb().query(`SELECT usage_json
68691
+ FROM memory_recall_usage
68692
+ ORDER BY timestamp DESC
68693
+ `).all();
68694
+ const events = [];
68695
+ for (const row of rows) {
68696
+ try {
68697
+ const parsed = JSON.parse(row.usage_json);
68698
+ if (Array.isArray(parsed.memoryIds) && typeof parsed.query === "string") {
68699
+ events.push(parsed);
68700
+ }
68701
+ } catch {}
68702
+ }
68703
+ return events;
68704
+ }
68404
68705
  async list(filter = {}) {
68405
68706
  await this.initialize();
68406
68707
  let records = Array.from(this.memories.values());
@@ -68419,7 +68720,9 @@ class SQLiteMemoryProvider {
68419
68720
  return !Number.isFinite(expires) || expires > now;
68420
68721
  });
68421
68722
  }
68422
- records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
68723
+ if (!filter.includeInactive) {
68724
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
68725
+ }
68423
68726
  records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
68424
68727
  return records.slice(0, filter.limit ?? records.length);
68425
68728
  }
@@ -68500,6 +68803,52 @@ class SQLiteMemoryProvider {
68500
68803
  proposals: proposals.length
68501
68804
  };
68502
68805
  }
68806
+ async compactMaintenance(options = {}) {
68807
+ await this.initialize();
68808
+ const now = options.now ? new Date(options.now) : new Date;
68809
+ const kept = [];
68810
+ const removeIds = [];
68811
+ const result = {
68812
+ dryRun: options.dryRun !== false,
68813
+ removedDeleted: 0,
68814
+ removedSuperseded: 0,
68815
+ removedExpiredScratch: 0,
68816
+ remaining: 0
68817
+ };
68818
+ for (const memory of this.memories.values()) {
68819
+ const compactReason = shouldCompactMemory(memory, now);
68820
+ if (compactReason === "deleted") {
68821
+ result.removedDeleted++;
68822
+ removeIds.push(memory.id);
68823
+ continue;
68824
+ }
68825
+ if (compactReason === "superseded") {
68826
+ result.removedSuperseded++;
68827
+ removeIds.push(memory.id);
68828
+ continue;
68829
+ }
68830
+ if (compactReason === "expired_scratch") {
68831
+ result.removedExpiredScratch++;
68832
+ removeIds.push(memory.id);
68833
+ continue;
68834
+ }
68835
+ kept.push(memory);
68836
+ }
68837
+ result.remaining = kept.length;
68838
+ if (result.dryRun)
68839
+ return result;
68840
+ const db = this.requireDb();
68841
+ const compact = db.transaction(() => {
68842
+ for (const id of removeIds) {
68843
+ db.run("DELETE FROM memory_items WHERE id = ?", [id]);
68844
+ this.deleteMemoryFts(id);
68845
+ }
68846
+ this.insertEvent("compact", "memory_items", "removed deleted, superseded, and expired scratch memories", JSON.stringify(result));
68847
+ });
68848
+ compact();
68849
+ this.memories = new Map(kept.map((memory) => [memory.id, memory]));
68850
+ return result;
68851
+ }
68503
68852
  hasMigration(name2) {
68504
68853
  const row = this.requireDb().query("SELECT version, name FROM schema_migrations WHERE name = ? LIMIT 1").get(name2);
68505
68854
  return Boolean(row);
@@ -68733,12 +69082,14 @@ class SQLiteMemoryProvider {
68733
69082
  ...decision.memory,
68734
69083
  updatedAt: appliedAt
68735
69084
  });
69085
+ validateCuratorPromotableMemory(memory);
68736
69086
  this.writeMemory(memory);
68737
69087
  memories.push(memory);
68738
69088
  memoryId = memory.id;
68739
69089
  } else if (decision.action === "update") {
68740
69090
  const existing = this.readActiveMemory(decision.targetMemoryId);
68741
69091
  const updated = this.validateDecisionMemory(applyPatchToMemory(existing, decision.patch, appliedAt));
69092
+ validateCuratorPromotableMemory(updated);
68742
69093
  if (updated.id !== existing.id) {
68743
69094
  const tombstone = this.validateDecisionMemory({
68744
69095
  ...existing,
@@ -68764,6 +69115,7 @@ class SQLiteMemoryProvider {
68764
69115
  updatedAt: appliedAt,
68765
69116
  supersedes: Array.from(new Set([...decision.replacement.supersedes ?? [], oldMemory.id]))
68766
69117
  });
69118
+ validateCuratorPromotableMemory(replacement);
68767
69119
  const superseded = this.validateDecisionMemory({
68768
69120
  ...oldMemory,
68769
69121
  updatedAt: appliedAt,
@@ -68976,6 +69328,7 @@ var init_sqlite_provider = __esm(() => {
68976
69328
  init_curator_decision_helpers();
68977
69329
  init_errors6();
68978
69330
  init_jsonl_migration();
69331
+ init_maintenance();
68979
69332
  init_schema2();
68980
69333
  init_scoring();
68981
69334
  FTS_INDEX_COLUMNS = [
@@ -70391,6 +70744,7 @@ var init_memory = __esm(() => {
70391
70744
  init_injector();
70392
70745
  init_jsonl_migration();
70393
70746
  init_local_jsonl_provider();
70747
+ init_maintenance();
70394
70748
  init_prompt_block();
70395
70749
  init_recall_planner();
70396
70750
  init_redaction();
@@ -70409,6 +70763,10 @@ async function handleMemoryCommand(_directory, _args) {
70409
70763
  "## Swarm Memory",
70410
70764
  "",
70411
70765
  "- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
70766
+ "- `/swarm memory pending` - show pending proposals and recent rejection reasons",
70767
+ "- `/swarm memory recall-log` - summarize recall usage by agent role and memory ID",
70768
+ "- `/swarm memory stale` - list expired scratch, superseded, deleted, and low-utility memories",
70769
+ "- `/swarm memory compact` - dry-run compaction; pass `--confirm` to remove deleted, superseded, and expired scratch records",
70412
70770
  "- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
70413
70771
  "- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
70414
70772
  "- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
@@ -70430,6 +70788,7 @@ async function handleMemoryStatusCommand(directory, _args) {
70430
70788
  `- Storage: \`${storageDir}\``,
70431
70789
  `- SQLite path: \`${sqlitePath}\``,
70432
70790
  `- SQLite database exists: \`${existsSync25(sqlitePath)}\``,
70791
+ `- Automatic destructive cleanup: \`disabled\``,
70433
70792
  "",
70434
70793
  "### Legacy JSONL"
70435
70794
  ];
@@ -70454,6 +70813,119 @@ async function handleMemoryStatusCommand(directory, _args) {
70454
70813
  return lines.join(`
70455
70814
  `);
70456
70815
  }
70816
+ async function handleMemoryPendingCommand(directory, args2) {
70817
+ const parsed = parseMaintenanceArgs(args2, {
70818
+ usage: "Usage: /swarm memory pending [--limit <n>]",
70819
+ allowConfirm: false
70820
+ });
70821
+ if ("error" in parsed)
70822
+ return parsed.error;
70823
+ const config3 = resolveCommandMemoryConfig(directory);
70824
+ const provider = createMaintenanceProvider(directory, config3);
70825
+ try {
70826
+ await provider.initialize?.();
70827
+ const report = await buildMemoryMaintenanceReport(provider, {
70828
+ ...maintenanceReportOptions(config3, parsed.limit)
70829
+ });
70830
+ const lines = [
70831
+ "## Swarm Memory Pending",
70832
+ "",
70833
+ `- Pending proposals shown: \`${report.pendingProposals.length}\``,
70834
+ `- Rejected proposal reasons shown: \`${report.rejectedProposalReasons.length}\``
70835
+ ];
70836
+ appendProposalLines(lines, "Pending proposals", report.pendingProposals);
70837
+ appendProposalLines(lines, "Rejected proposal reasons", report.rejectedProposalReasons);
70838
+ return lines.join(`
70839
+ `);
70840
+ } finally {
70841
+ await provider.close?.();
70842
+ }
70843
+ }
70844
+ async function handleMemoryRecallLogCommand(directory, args2) {
70845
+ const parsed = parseMaintenanceArgs(args2, {
70846
+ usage: "Usage: /swarm memory recall-log [--limit <n>]",
70847
+ allowConfirm: false
70848
+ });
70849
+ if ("error" in parsed)
70850
+ return parsed.error;
70851
+ const config3 = resolveCommandMemoryConfig(directory);
70852
+ const provider = createMaintenanceProvider(directory, config3);
70853
+ try {
70854
+ await provider.initialize?.();
70855
+ const report = await buildMemoryMaintenanceReport(provider, {
70856
+ ...maintenanceReportOptions(config3, parsed.limit)
70857
+ });
70858
+ const lines = [
70859
+ "## Swarm Memory Recall Log",
70860
+ "",
70861
+ `- Recall events scanned: \`${report.recallEventCount}\``,
70862
+ `- Most-recalled memories shown: \`${report.mostRecalledMemories.length}\``,
70863
+ `- Never-recalled memories shown: \`${report.neverRecalledMemories.length}\``
70864
+ ];
70865
+ appendRecallRoleLines(lines, report.recallByAgentRole);
70866
+ appendRecallMemoryLines(lines, report.mostRecalledMemories);
70867
+ appendMemoryLines(lines, "Never-recalled memories", report.neverRecalledMemories);
70868
+ return lines.join(`
70869
+ `);
70870
+ } finally {
70871
+ await provider.close?.();
70872
+ }
70873
+ }
70874
+ async function handleMemoryStaleCommand(directory, args2) {
70875
+ const parsed = parseMaintenanceArgs(args2, {
70876
+ usage: "Usage: /swarm memory stale [--limit <n>]",
70877
+ allowConfirm: false
70878
+ });
70879
+ if ("error" in parsed)
70880
+ return parsed.error;
70881
+ const config3 = resolveCommandMemoryConfig(directory);
70882
+ const provider = createMaintenanceProvider(directory, config3);
70883
+ try {
70884
+ await provider.initialize?.();
70885
+ const report = await buildMemoryMaintenanceReport(provider, {
70886
+ ...maintenanceReportOptions(config3, parsed.limit)
70887
+ });
70888
+ const lines = [
70889
+ "## Swarm Memory Stale",
70890
+ "",
70891
+ `- Active memories: \`${report.activeMemories}\``,
70892
+ `- Expired scratch memories shown: \`${report.expiredScratchMemories.length}\``,
70893
+ `- Deleted tombstones shown: \`${report.deletedMemories.length}\``,
70894
+ `- Superseded memories shown: \`${report.supersededMemories.length}\``,
70895
+ `- Low-utility memories shown: \`${report.lowUtilityMemories.length}\``
70896
+ ];
70897
+ appendMemoryLines(lines, "Expired scratch memories", report.expiredScratchMemories);
70898
+ appendMemoryLines(lines, "Deleted tombstones", report.deletedMemories);
70899
+ appendSupersededChains(lines, report);
70900
+ appendMemoryLines(lines, "Low-utility memories", report.lowUtilityMemories);
70901
+ return lines.join(`
70902
+ `);
70903
+ } finally {
70904
+ await provider.close?.();
70905
+ }
70906
+ }
70907
+ async function handleMemoryCompactCommand(directory, args2) {
70908
+ const parsed = parseMaintenanceArgs(args2, {
70909
+ usage: "Usage: /swarm memory compact [--confirm]",
70910
+ allowConfirm: true,
70911
+ allowLimit: false
70912
+ });
70913
+ if ("error" in parsed)
70914
+ return parsed.error;
70915
+ const provider = createMaintenanceProvider(directory, resolveCommandMemoryConfig(directory));
70916
+ try {
70917
+ await provider.initialize?.();
70918
+ if (!provider.compactMaintenance) {
70919
+ return "Memory provider does not support compaction.";
70920
+ }
70921
+ const result = await provider.compactMaintenance({
70922
+ dryRun: !parsed.confirm
70923
+ });
70924
+ return formatCompactResult(result, parsed.confirm);
70925
+ } finally {
70926
+ await provider.close?.();
70927
+ }
70928
+ }
70457
70929
  async function handleMemoryMigrateCommand(directory, _args) {
70458
70930
  const config3 = {
70459
70931
  ...resolveCommandMemoryConfig(directory),
@@ -70510,6 +70982,16 @@ async function handleMemoryExportCommand(directory, _args) {
70510
70982
  await provider.close?.();
70511
70983
  }
70512
70984
  }
70985
+ function createMaintenanceProvider(directory, config3) {
70986
+ return createConfiguredMemoryProvider(directory, config3);
70987
+ }
70988
+ function maintenanceReportOptions(config3, limit) {
70989
+ return {
70990
+ limit,
70991
+ lowUtilityMaxConfidence: config3.maintenance.lowUtilityMaxConfidence,
70992
+ lowUtilityMinAgeDays: config3.maintenance.lowUtilityMinAgeDays
70993
+ };
70994
+ }
70513
70995
  async function handleMemoryEvaluateCommand(directory, args2) {
70514
70996
  const parsed = parseEvaluateArgs(directory, args2);
70515
70997
  if ("error" in parsed)
@@ -70568,6 +71050,28 @@ function parseEvaluateArgs(directory, args2) {
70568
71050
  }
70569
71051
  return { json: json3, fixtureDirectory };
70570
71052
  }
71053
+ function parseMaintenanceArgs(args2, options) {
71054
+ let limit = 20;
71055
+ let confirm = false;
71056
+ const allowLimit = options.allowLimit ?? true;
71057
+ for (let i2 = 0;i2 < args2.length; i2++) {
71058
+ const arg = args2[i2];
71059
+ if (arg === "--confirm" && options.allowConfirm) {
71060
+ confirm = true;
71061
+ continue;
71062
+ }
71063
+ if (arg === "--limit" && allowLimit) {
71064
+ const next = args2[i2 + 1];
71065
+ if (!next || !/^\d+$/.test(next))
71066
+ return { error: options.usage };
71067
+ limit = Math.min(100, Math.max(1, Number(next)));
71068
+ i2++;
71069
+ continue;
71070
+ }
71071
+ return { error: options.usage };
71072
+ }
71073
+ return { limit, confirm };
71074
+ }
70571
71075
  function resolvePackageRootFromModule(modulePath) {
70572
71076
  const moduleDir = path45.dirname(modulePath);
70573
71077
  const leaf = path45.basename(moduleDir);
@@ -70618,6 +71122,80 @@ function appendInvalidRows(lines, invalidRows) {
70618
71122
  lines.push(`- ... ${invalidRows.length - 20} more`);
70619
71123
  }
70620
71124
  }
71125
+ function appendProposalLines(lines, title, proposals) {
71126
+ lines.push("", `### ${title}`);
71127
+ if (proposals.length === 0) {
71128
+ lines.push("- none");
71129
+ return;
71130
+ }
71131
+ for (const proposal of proposals) {
71132
+ const reason = proposal.status === "rejected" ? ` - ${proposal.rejectionReason ?? "no reason recorded"}` : "";
71133
+ lines.push(`- \`${proposal.id}\` ${proposal.operation} ${proposal.targetMemoryId ?? proposal.proposedRecord?.id ?? "new"} (${proposal.status})${reason}`);
71134
+ }
71135
+ }
71136
+ function appendMemoryLines(lines, title, memories) {
71137
+ lines.push("", `### ${title}`);
71138
+ if (memories.length === 0) {
71139
+ lines.push("- none");
71140
+ return;
71141
+ }
71142
+ for (const memory of memories) {
71143
+ lines.push(`- \`${memory.id}\` ${memory.kind} confidence=${memory.confidence.toFixed(2)} updated=${memory.updatedAt} - ${truncate(memory.text, 100)}`);
71144
+ }
71145
+ }
71146
+ function appendRecallRoleLines(lines, roles) {
71147
+ lines.push("", "### Recall by agent role");
71148
+ if (roles.length === 0) {
71149
+ lines.push("- none");
71150
+ return;
71151
+ }
71152
+ for (const role of roles) {
71153
+ const memoryCount = Object.keys(role.memoryIds).length;
71154
+ lines.push(`- \`${role.agentRole}\`: ${role.count} recall event(s), ${memoryCount} memory ID(s)`);
71155
+ }
71156
+ }
71157
+ function appendRecallMemoryLines(lines, memories) {
71158
+ lines.push("", "### Most-recalled memories");
71159
+ if (memories.length === 0) {
71160
+ lines.push("- none");
71161
+ return;
71162
+ }
71163
+ for (const memory of memories) {
71164
+ lines.push(`- \`${memory.memoryId}\`: ${memory.count} hit(s), last=${memory.lastRecalledAt}, avgScore=${memory.averageScore.toFixed(3)}`);
71165
+ }
71166
+ }
71167
+ function appendSupersededChains(lines, report) {
71168
+ lines.push("", "### Superseded chains");
71169
+ if (report.supersededChains.length === 0) {
71170
+ lines.push("- none");
71171
+ return;
71172
+ }
71173
+ for (const chain of report.supersededChains) {
71174
+ const reason = chain.reason ? ` - ${chain.reason}` : "";
71175
+ lines.push(`- ${chain.chain.map((id) => `\`${id}\``).join(" -> ")}${reason}`);
71176
+ }
71177
+ }
71178
+ function formatCompactResult(result, confirmed) {
71179
+ const lines = [
71180
+ "## Swarm Memory Compact",
71181
+ "",
71182
+ `- Mode: \`${confirmed ? "confirmed" : "dry-run"}\``,
71183
+ `- Deleted tombstones: \`${result.removedDeleted}\``,
71184
+ `- Superseded records: \`${result.removedSuperseded}\``,
71185
+ `- Expired scratch records: \`${result.removedExpiredScratch}\``,
71186
+ `- Remaining memories: \`${result.remaining}\``
71187
+ ];
71188
+ if (!confirmed) {
71189
+ lines.push("", "No records were removed. Re-run `/swarm memory compact --confirm` to apply this compaction.");
71190
+ }
71191
+ return lines.join(`
71192
+ `);
71193
+ }
71194
+ function truncate(value, maxLength) {
71195
+ if (value.length <= maxLength)
71196
+ return value;
71197
+ return `${value.slice(0, maxLength - 3)}...`;
71198
+ }
70621
71199
  var PACKAGE_ROOT;
70622
71200
  var init_memory2 = __esm(() => {
70623
71201
  init_loader();
@@ -79002,7 +79580,7 @@ function classifySwarmCommandToolUse(resolved) {
79002
79580
  return { allowed: true };
79003
79581
  return {
79004
79582
  allowed: false,
79005
- message: "Use `/swarm memory status`, `/swarm memory export`, or `/swarm memory evaluate --json` through swarm_command. Memory import and migrate are intentionally excluded from chat-tool execution."
79583
+ message: "Use `/swarm memory status`, `/swarm memory pending`, `/swarm memory recall-log`, `/swarm memory stale`, `/swarm memory export`, or `/swarm memory evaluate --json` through swarm_command. Memory import, migrate, and compact are intentionally excluded from chat-tool execution."
79006
79584
  };
79007
79585
  }
79008
79586
  if (canonicalKey === "memory evaluate") {
@@ -79015,6 +79593,17 @@ function classifySwarmCommandToolUse(resolved) {
79015
79593
  message: "Usage through swarm_command: `/swarm memory evaluate --json`. Custom fixture directories are only available through direct user command execution."
79016
79594
  };
79017
79595
  }
79596
+ if (canonicalKey === "memory pending" || canonicalKey === "memory recall-log" || canonicalKey === "memory stale") {
79597
+ if (args2.length === 0)
79598
+ return { allowed: true };
79599
+ if (args2.length === 2 && args2[0] === "--limit" && /^\d+$/.test(args2[1])) {
79600
+ return { allowed: true };
79601
+ }
79602
+ return {
79603
+ allowed: false,
79604
+ message: `Usage through swarm_command: \`/swarm ${canonicalKey}\` or ` + `\`/swarm ${canonicalKey} --limit <n>\`.`
79605
+ };
79606
+ }
79018
79607
  if (canonicalKey === "retrieve") {
79019
79608
  if (args2.length !== 1 || !SUMMARY_ID_PATTERN.test(args2[0])) {
79020
79609
  return {
@@ -79066,7 +79655,7 @@ function classifySwarmCommandChatFallbackUse(resolved) {
79066
79655
  message: "/swarm config doctor --fix is not available through chat fallback because it can modify configuration files. Run the CLI command directly when you intend to apply fixes."
79067
79656
  };
79068
79657
  }
79069
- if (canonicalKey === "knowledge migrate" || canonicalKey === "knowledge quarantine" || canonicalKey === "knowledge restore" || canonicalKey === "memory import" || canonicalKey === "memory migrate") {
79658
+ if (canonicalKey === "knowledge migrate" || canonicalKey === "knowledge quarantine" || canonicalKey === "knowledge restore" || canonicalKey === "memory import" || canonicalKey === "memory migrate" || canonicalKey === "memory compact") {
79070
79659
  return {
79071
79660
  allowed: false,
79072
79661
  message: `/swarm ${canonicalKey} is not available through chat fallback because it mutates .swarm state. ` + "Run the CLI command directly after confirming the intended state change."
@@ -79099,6 +79688,10 @@ var init_tool_policy = __esm(() => {
79099
79688
  "knowledge",
79100
79689
  "memory",
79101
79690
  "memory status",
79691
+ "memory pending",
79692
+ "memory recall-log",
79693
+ "memory compact",
79694
+ "memory stale",
79102
79695
  "memory export",
79103
79696
  "memory evaluate",
79104
79697
  "memory import",
@@ -79125,6 +79718,9 @@ var init_tool_policy = __esm(() => {
79125
79718
  "knowledge",
79126
79719
  "memory",
79127
79720
  "memory status",
79721
+ "memory pending",
79722
+ "memory recall-log",
79723
+ "memory stale",
79128
79724
  "memory export",
79129
79725
  "memory evaluate",
79130
79726
  "sync-plan",
@@ -79137,7 +79733,8 @@ var init_tool_policy = __esm(() => {
79137
79733
  "rollback",
79138
79734
  "checkpoint",
79139
79735
  "memory import",
79140
- "memory migrate"
79736
+ "memory migrate",
79737
+ "memory compact"
79141
79738
  ]);
79142
79739
  NO_ARGS = new Set([
79143
79740
  "agents",
@@ -80132,6 +80729,34 @@ Subcommands:
80132
80729
  args: "",
80133
80730
  category: "diagnostics"
80134
80731
  },
80732
+ "memory pending": {
80733
+ handler: (ctx) => handleMemoryPendingCommand(ctx.directory, ctx.args),
80734
+ description: "Show pending Swarm memory proposals and rejection reasons",
80735
+ subcommandOf: "memory",
80736
+ args: "--limit <n>",
80737
+ category: "diagnostics"
80738
+ },
80739
+ "memory recall-log": {
80740
+ handler: (ctx) => handleMemoryRecallLogCommand(ctx.directory, ctx.args),
80741
+ description: "Summarize Swarm memory recall usage",
80742
+ subcommandOf: "memory",
80743
+ args: "--limit <n>",
80744
+ category: "diagnostics"
80745
+ },
80746
+ "memory compact": {
80747
+ handler: (ctx) => handleMemoryCompactCommand(ctx.directory, ctx.args),
80748
+ description: "Compact deleted, superseded, and expired scratch memories",
80749
+ subcommandOf: "memory",
80750
+ args: "--confirm",
80751
+ category: "utility"
80752
+ },
80753
+ "memory stale": {
80754
+ handler: (ctx) => handleMemoryStaleCommand(ctx.directory, ctx.args),
80755
+ description: "List stale and low-utility Swarm memories",
80756
+ subcommandOf: "memory",
80757
+ args: "--limit <n>",
80758
+ category: "diagnostics"
80759
+ },
80135
80760
  "memory export": {
80136
80761
  handler: (ctx) => handleMemoryExportCommand(ctx.directory, ctx.args),
80137
80762
  description: "Export current Swarm memory to JSONL files",
@@ -83887,6 +84512,7 @@ API: [exact names/signatures/versions to use]
83887
84512
  PLATFORM: [cross-platform notes if OS-interaction APIs]
83888
84513
  GOTCHAS: [common pitfalls or edge cases]
83889
84514
  DEPS: [required dependencies/tools]
84515
+ EVIDENCE_REFS: [cite evidence-cache:<id>, URL, file, or doc refs used; use "none" if no external evidence was available]
83890
84516
 
83891
84517
  ## DOMAIN CHECKLISTS
83892
84518
  Apply the relevant checklist when the DOMAIN matches:
@@ -83925,7 +84551,8 @@ Cache lookup steps:
83925
84551
  1. If \`.swarm/context.md\` does not exist: proceed with fresh research.
83926
84552
  2. If the \`## Research Sources\` section is absent: proceed with fresh research.
83927
84553
  3. If URL/topic IS listed in ## Research Sources: reuse cached summary — no re-fetch needed.
83928
- 4. If cache miss (URL/topic not listed): fetch URL, then append this line at the end of your response:
84554
+ 4. If fresh search/API-doc/crawl evidence is provided, cite its \`evidence-cache:<id>\` refs in EVIDENCE_REFS. Raw docs/search snippets are evidence, not memory.
84555
+ 5. If cache miss (URL/topic not listed): fetch URL, then append this line at the end of your response:
83929
84556
  CACHE-UPDATE: [YYYY-MM-DD] | [URL or topic] | [one-line summary of finding]
83930
84557
  The Architect will save this line to .swarm/context.md ## Research Sources. Do NOT write to any file yourself.
83931
84558
 
@@ -89644,11 +90271,11 @@ var init_curator_drift = __esm(() => {
89644
90271
  var exports_project_context = {};
89645
90272
  __export(exports_project_context, {
89646
90273
  buildProjectContext: () => buildProjectContext,
89647
- _internals: () => _internals64,
90274
+ _internals: () => _internals65,
89648
90275
  LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
89649
90276
  });
89650
90277
  import * as fs115 from "node:fs";
89651
- import * as path154 from "node:path";
90278
+ import * as path155 from "node:path";
89652
90279
  function detectFileExists2(directory, pattern) {
89653
90280
  if (pattern.includes("*") || pattern.includes("?")) {
89654
90281
  try {
@@ -89660,7 +90287,7 @@ function detectFileExists2(directory, pattern) {
89660
90287
  }
89661
90288
  }
89662
90289
  try {
89663
- fs115.accessSync(path154.join(directory, pattern));
90290
+ fs115.accessSync(path155.join(directory, pattern));
89664
90291
  return true;
89665
90292
  } catch {
89666
90293
  return false;
@@ -89669,7 +90296,7 @@ function detectFileExists2(directory, pattern) {
89669
90296
  function selectTestCommandFromScriptsTest(backend, directory) {
89670
90297
  let pkgRaw;
89671
90298
  try {
89672
- pkgRaw = fs115.readFileSync(path154.join(directory, "package.json"), "utf-8");
90299
+ pkgRaw = fs115.readFileSync(path155.join(directory, "package.json"), "utf-8");
89673
90300
  } catch {
89674
90301
  return null;
89675
90302
  }
@@ -89728,7 +90355,7 @@ function selectLintCommand(backend, directory) {
89728
90355
  return null;
89729
90356
  }
89730
90357
  async function buildProjectContext(directory) {
89731
- const backend = await _internals64.pickBackend(directory);
90358
+ const backend = await _internals65.pickBackend(directory);
89732
90359
  if (!backend)
89733
90360
  return null;
89734
90361
  const ctx = emptyProjectContext();
@@ -89759,16 +90386,16 @@ async function buildProjectContext(directory) {
89759
90386
  if (backend.prompts.reviewerChecklist.length > 0) {
89760
90387
  ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
89761
90388
  }
89762
- const profiles = _internals64.pickedProfiles(directory);
90389
+ const profiles = _internals65.pickedProfiles(directory);
89763
90390
  if (profiles.length > 1) {
89764
90391
  ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
89765
90392
  }
89766
90393
  return ctx;
89767
90394
  }
89768
- var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals64;
90395
+ var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals65;
89769
90396
  var init_project_context = __esm(() => {
89770
90397
  init_dispatch();
89771
- _internals64 = {
90398
+ _internals65 = {
89772
90399
  pickBackend,
89773
90400
  pickedProfiles
89774
90401
  };
@@ -89778,7 +90405,7 @@ var init_project_context = __esm(() => {
89778
90405
  init_package();
89779
90406
  init_agents2();
89780
90407
  init_critic();
89781
- import * as path155 from "node:path";
90408
+ import * as path156 from "node:path";
89782
90409
 
89783
90410
  // src/background/index.ts
89784
90411
  init_event_bus();
@@ -122958,6 +123585,83 @@ function createWebSearchProvider(config3) {
122958
123585
  }
122959
123586
  }
122960
123587
 
123588
+ // src/evidence/documents.ts
123589
+ init_utils2();
123590
+ init_redaction();
123591
+ import { createHash as createHash12 } from "node:crypto";
123592
+ import { appendFile as appendFile13, mkdir as mkdir24 } from "node:fs/promises";
123593
+ import * as path150 from "node:path";
123594
+ var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
123595
+ var MAX_EVIDENCE_TEXT_LENGTH = 4000;
123596
+ async function writeEvidenceDocuments(directory, inputs, now = () => new Date) {
123597
+ const filePath = validateSwarmPath(directory, EVIDENCE_CACHE_FILE);
123598
+ const capturedAt = now().toISOString();
123599
+ const records = inputs.map((input) => createEvidenceDocumentRecord(input, capturedAt)).filter((record3) => record3 !== null);
123600
+ if (records.length > 0) {
123601
+ await mkdir24(path150.dirname(filePath), { recursive: true });
123602
+ await appendFile13(filePath, `${records.map((record3) => JSON.stringify(record3)).join(`
123603
+ `)}
123604
+ `, "utf-8");
123605
+ }
123606
+ return {
123607
+ path: ".swarm/evidence-cache/documents.jsonl",
123608
+ records,
123609
+ refs: records.map((record3) => record3.ref)
123610
+ };
123611
+ }
123612
+ function createEvidenceDocumentRecord(input, defaultCapturedAt) {
123613
+ const text = normalizeEvidenceText(input.text ?? input.snippet ?? "");
123614
+ if (!text)
123615
+ return null;
123616
+ const capturedAt = input.capturedAt ?? defaultCapturedAt;
123617
+ const base = {
123618
+ sourceType: input.sourceType,
123619
+ query: normalizeOptional(input.query),
123620
+ title: normalizeOptional(input.title),
123621
+ url: normalizeOptional(input.url),
123622
+ text
123623
+ };
123624
+ const id = createEvidenceDocumentId(base);
123625
+ return {
123626
+ id,
123627
+ ref: `evidence-cache:${id}`,
123628
+ ...base,
123629
+ capturedAt,
123630
+ createdBy: normalizeOptional(input.createdBy),
123631
+ metadata: input.metadata ?? {}
123632
+ };
123633
+ }
123634
+ function createEvidenceDocumentId(input) {
123635
+ const hash3 = createHash12("sha256").update([
123636
+ input.sourceType,
123637
+ input.query ?? "",
123638
+ input.title ?? "",
123639
+ input.url ?? "",
123640
+ input.text
123641
+ ].join(`
123642
+ `)).digest("hex");
123643
+ return `evd_${hash3.slice(0, 16)}`;
123644
+ }
123645
+ function normalizeEvidenceText(text) {
123646
+ const normalized = redactSecrets(text.replace(/\s+/g, " ").trim());
123647
+ return truncateEvidenceText(normalized, MAX_EVIDENCE_TEXT_LENGTH);
123648
+ }
123649
+ function truncateEvidenceText(text, maxLength) {
123650
+ if (text.length <= maxLength)
123651
+ return text;
123652
+ const truncated = text.slice(0, maxLength);
123653
+ const lastPlaceholderStart = truncated.lastIndexOf("[REDACTED:");
123654
+ const lastPlaceholderEnd = truncated.lastIndexOf("]");
123655
+ if (lastPlaceholderStart > lastPlaceholderEnd) {
123656
+ return truncated.slice(0, lastPlaceholderStart).trimEnd();
123657
+ }
123658
+ return truncated;
123659
+ }
123660
+ function normalizeOptional(value) {
123661
+ const normalized = value?.replace(/\s+/g, " ").trim();
123662
+ return normalized ? redactSecrets(normalized) : undefined;
123663
+ }
123664
+
122961
123665
  // src/tools/web-search.ts
122962
123666
  init_create_tool();
122963
123667
  init_resolve_working_directory();
@@ -123018,6 +123722,7 @@ var web_search = createSwarmTool({
123018
123722
  }
123019
123723
  try {
123020
123724
  const results = await provider.search(parsed.data.query, maxResults);
123725
+ const evidence = await captureSearchEvidence(dirResult.directory, parsed.data.query, results);
123021
123726
  const ok2 = {
123022
123727
  success: true,
123023
123728
  query: parsed.data.query,
@@ -123025,8 +123730,15 @@ var web_search = createSwarmTool({
123025
123730
  results: results.map(({ title, url: url3, snippet }) => ({
123026
123731
  title,
123027
123732
  url: url3,
123028
- snippet
123029
- }))
123733
+ snippet,
123734
+ evidenceRef: evidence.refByUrl.get(url3)
123735
+ })),
123736
+ evidence: {
123737
+ stored: evidence.stored,
123738
+ path: evidence.path,
123739
+ refs: evidence.refs,
123740
+ error: evidence.error
123741
+ }
123030
123742
  };
123031
123743
  return JSON.stringify(ok2, null, 2);
123032
123744
  } catch (err3) {
@@ -123039,6 +123751,39 @@ var web_search = createSwarmTool({
123039
123751
  }
123040
123752
  }
123041
123753
  });
123754
+ async function captureSearchEvidence(directory, query, results) {
123755
+ try {
123756
+ const written = await _internals64.writeEvidenceDocuments(directory, results.map((result) => ({
123757
+ sourceType: "web_search",
123758
+ query,
123759
+ title: result.title,
123760
+ url: result.url,
123761
+ snippet: result.snippet,
123762
+ createdBy: "web_search"
123763
+ })));
123764
+ const refByUrl = new Map;
123765
+ for (const record3 of written.records) {
123766
+ if (record3.url)
123767
+ refByUrl.set(record3.url, record3.ref);
123768
+ }
123769
+ return {
123770
+ stored: written.records.length > 0,
123771
+ path: written.path,
123772
+ refs: written.refs,
123773
+ refByUrl
123774
+ };
123775
+ } catch (err3) {
123776
+ return {
123777
+ stored: false,
123778
+ refs: [],
123779
+ refByUrl: new Map,
123780
+ error: err3 instanceof Error ? err3.message : String(err3)
123781
+ };
123782
+ }
123783
+ }
123784
+ var _internals64 = {
123785
+ writeEvidenceDocuments
123786
+ };
123042
123787
  // src/tools/write-drift-evidence.ts
123043
123788
  init_zod();
123044
123789
  init_qa_gate_profile();
@@ -123047,7 +123792,7 @@ init_ledger();
123047
123792
  init_manager();
123048
123793
  init_create_tool();
123049
123794
  import fs111 from "node:fs";
123050
- import path150 from "node:path";
123795
+ import path151 from "node:path";
123051
123796
  function normalizeVerdict(verdict) {
123052
123797
  switch (verdict) {
123053
123798
  case "APPROVED":
@@ -123095,7 +123840,7 @@ async function executeWriteDriftEvidence(args2, directory) {
123095
123840
  entries: [evidenceEntry]
123096
123841
  };
123097
123842
  const filename = "drift-verifier.json";
123098
- const relativePath = path150.join("evidence", String(phase), filename);
123843
+ const relativePath = path151.join("evidence", String(phase), filename);
123099
123844
  let validatedPath;
123100
123845
  try {
123101
123846
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -123106,10 +123851,10 @@ async function executeWriteDriftEvidence(args2, directory) {
123106
123851
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
123107
123852
  }, null, 2);
123108
123853
  }
123109
- const evidenceDir = path150.dirname(validatedPath);
123854
+ const evidenceDir = path151.dirname(validatedPath);
123110
123855
  try {
123111
123856
  await fs111.promises.mkdir(evidenceDir, { recursive: true });
123112
- const tempPath = path150.join(evidenceDir, `.${filename}.tmp`);
123857
+ const tempPath = path151.join(evidenceDir, `.${filename}.tmp`);
123113
123858
  await fs111.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
123114
123859
  await fs111.promises.rename(tempPath, validatedPath);
123115
123860
  let snapshotInfo;
@@ -123205,7 +123950,7 @@ var write_drift_evidence = createSwarmTool({
123205
123950
  init_zod();
123206
123951
  init_loader();
123207
123952
  import fs112 from "node:fs";
123208
- import path151 from "node:path";
123953
+ import path152 from "node:path";
123209
123954
  init_utils2();
123210
123955
  init_manager();
123211
123956
  init_create_tool();
@@ -123293,7 +124038,7 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
123293
124038
  timestamp: synthesis.timestamp
123294
124039
  };
123295
124040
  const filename = "final-council.json";
123296
- const relativePath = path151.join("evidence", filename);
124041
+ const relativePath = path152.join("evidence", filename);
123297
124042
  let validatedPath;
123298
124043
  try {
123299
124044
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -123307,10 +124052,10 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
123307
124052
  const evidenceContent = {
123308
124053
  entries: [evidenceEntry]
123309
124054
  };
123310
- const evidenceDir = path151.dirname(validatedPath);
124055
+ const evidenceDir = path152.dirname(validatedPath);
123311
124056
  try {
123312
124057
  await fs112.promises.mkdir(evidenceDir, { recursive: true });
123313
- const tempPath = path151.join(evidenceDir, `.${filename}.tmp`);
124058
+ const tempPath = path152.join(evidenceDir, `.${filename}.tmp`);
123314
124059
  await fs112.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
123315
124060
  await fs112.promises.rename(tempPath, validatedPath);
123316
124061
  return JSON.stringify({
@@ -123369,7 +124114,7 @@ init_zod();
123369
124114
  init_utils2();
123370
124115
  init_create_tool();
123371
124116
  import fs113 from "node:fs";
123372
- import path152 from "node:path";
124117
+ import path153 from "node:path";
123373
124118
  function normalizeVerdict2(verdict) {
123374
124119
  switch (verdict) {
123375
124120
  case "APPROVED":
@@ -123417,7 +124162,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
123417
124162
  entries: [evidenceEntry]
123418
124163
  };
123419
124164
  const filename = "hallucination-guard.json";
123420
- const relativePath = path152.join("evidence", String(phase), filename);
124165
+ const relativePath = path153.join("evidence", String(phase), filename);
123421
124166
  let validatedPath;
123422
124167
  try {
123423
124168
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -123428,10 +124173,10 @@ async function executeWriteHallucinationEvidence(args2, directory) {
123428
124173
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
123429
124174
  }, null, 2);
123430
124175
  }
123431
- const evidenceDir = path152.dirname(validatedPath);
124176
+ const evidenceDir = path153.dirname(validatedPath);
123432
124177
  try {
123433
124178
  await fs113.promises.mkdir(evidenceDir, { recursive: true });
123434
- const tempPath = path152.join(evidenceDir, `.${filename}.tmp`);
124179
+ const tempPath = path153.join(evidenceDir, `.${filename}.tmp`);
123435
124180
  await fs113.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
123436
124181
  await fs113.promises.rename(tempPath, validatedPath);
123437
124182
  return JSON.stringify({
@@ -123480,7 +124225,7 @@ init_zod();
123480
124225
  init_utils2();
123481
124226
  init_create_tool();
123482
124227
  import fs114 from "node:fs";
123483
- import path153 from "node:path";
124228
+ import path154 from "node:path";
123484
124229
  function normalizeVerdict3(verdict) {
123485
124230
  switch (verdict) {
123486
124231
  case "PASS":
@@ -123554,7 +124299,7 @@ async function executeWriteMutationEvidence(args2, directory) {
123554
124299
  entries: [evidenceEntry]
123555
124300
  };
123556
124301
  const filename = "mutation-gate.json";
123557
- const relativePath = path153.join("evidence", String(phase), filename);
124302
+ const relativePath = path154.join("evidence", String(phase), filename);
123558
124303
  let validatedPath;
123559
124304
  try {
123560
124305
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -123565,10 +124310,10 @@ async function executeWriteMutationEvidence(args2, directory) {
123565
124310
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
123566
124311
  }, null, 2);
123567
124312
  }
123568
- const evidenceDir = path153.dirname(validatedPath);
124313
+ const evidenceDir = path154.dirname(validatedPath);
123569
124314
  try {
123570
124315
  await fs114.promises.mkdir(evidenceDir, { recursive: true });
123571
- const tempPath = path153.join(evidenceDir, `.${filename}.tmp`);
124316
+ const tempPath = path154.join(evidenceDir, `.${filename}.tmp`);
123572
124317
  await fs114.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
123573
124318
  await fs114.promises.rename(tempPath, validatedPath);
123574
124319
  return JSON.stringify({
@@ -123918,7 +124663,7 @@ async function initializeOpenCodeSwarm(ctx) {
123918
124663
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
123919
124664
  preflightTriggerManager = new PTM(automationConfig);
123920
124665
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
123921
- const swarmDir = path155.resolve(ctx.directory, ".swarm");
124666
+ const swarmDir = path156.resolve(ctx.directory, ".swarm");
123922
124667
  statusArtifact = new ASA(swarmDir);
123923
124668
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
123924
124669
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -124522,7 +125267,7 @@ ${promptRaw}`;
124522
125267
  "ci-failure-resolver": "CI/CD failure resolution"
124523
125268
  };
124524
125269
  const skillPaths = topSkills.map((s) => {
124525
- const dirName = path155.basename(path155.dirname(s.skillPath));
125270
+ const dirName = path156.basename(path156.dirname(s.skillPath));
124526
125271
  const desc = SKILL_DESCRIPTIONS[dirName] ?? dirName;
124527
125272
  return `file:${s.skillPath} (-- ${desc})`;
124528
125273
  }).join(", ");
@@ -124531,7 +125276,7 @@ ${promptRaw}`;
124531
125276
 
124532
125277
  ${promptRaw}`;
124533
125278
  argsRecord.prompt = newPrompt;
124534
- const skillNames = topSkills.map((s) => `${path155.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
125279
+ const skillNames = topSkills.map((s) => `${path156.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
124535
125280
  console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
124536
125281
  for (const skill of topSkills) {
124537
125282
  try {