opencode-swarm 7.39.0 → 7.40.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/cli/index.js CHANGED
@@ -34,7 +34,7 @@ var package_default;
34
34
  var init_package = __esm(() => {
35
35
  package_default = {
36
36
  name: "opencode-swarm",
37
- version: "7.39.0",
37
+ version: "7.40.0",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -17643,6 +17643,13 @@ var init_schema = __esm(() => {
17643
17643
  redaction: exports_external.object({
17644
17644
  rejectDurableSecrets: exports_external.boolean().default(true)
17645
17645
  }).default({ rejectDurableSecrets: true }),
17646
+ maintenance: exports_external.object({
17647
+ lowUtilityMaxConfidence: exports_external.number().min(0).max(1).default(0.45),
17648
+ lowUtilityMinAgeDays: exports_external.number().int().min(1).max(3650).default(30)
17649
+ }).default({
17650
+ lowUtilityMaxConfidence: 0.45,
17651
+ lowUtilityMinAgeDays: 30
17652
+ }),
17646
17653
  hardDelete: exports_external.boolean().default(false)
17647
17654
  });
17648
17655
  CuratorConfigSchema = exports_external.object({
@@ -45408,6 +45415,10 @@ function resolveMemoryConfig(input) {
45408
45415
  redaction: {
45409
45416
  ...DEFAULT_MEMORY_CONFIG.redaction,
45410
45417
  ...input?.redaction ?? {}
45418
+ },
45419
+ maintenance: {
45420
+ ...DEFAULT_MEMORY_CONFIG.maintenance,
45421
+ ...input?.maintenance ?? {}
45411
45422
  }
45412
45423
  };
45413
45424
  }
@@ -45439,6 +45450,10 @@ var init_config3 = __esm(() => {
45439
45450
  redaction: {
45440
45451
  rejectDurableSecrets: true
45441
45452
  },
45453
+ maintenance: {
45454
+ lowUtilityMaxConfidence: 0.45,
45455
+ lowUtilityMinAgeDays: 30
45456
+ },
45442
45457
  hardDelete: false
45443
45458
  };
45444
45459
  DURABLE_MEMORY_KINDS = new Set([
@@ -45822,6 +45837,167 @@ var init_curator_decision_helpers = __esm(() => {
45822
45837
  init_schema2();
45823
45838
  });
45824
45839
 
45840
+ // src/memory/maintenance.ts
45841
+ async function buildMemoryMaintenanceReport(provider, options = {}) {
45842
+ const now = options.now ?? new Date;
45843
+ const limit = Math.max(1, Math.trunc(options.limit ?? 20));
45844
+ const memories = await provider.list({
45845
+ includeExpired: true,
45846
+ includeInactive: true
45847
+ });
45848
+ const proposals = await loadMaintenanceProposals(provider, limit);
45849
+ const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
45850
+ const usageByMemory = summarizeRecallByMemory(recallUsage);
45851
+ const usageByRole = summarizeRecallByRole(recallUsage);
45852
+ const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
45853
+ const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
45854
+ const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
45855
+ const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
45856
+ const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
45857
+ maxConfidence: options.lowUtilityMaxConfidence ?? DEFAULT_LOW_UTILITY_MAX_CONFIDENCE,
45858
+ minAgeDays: options.lowUtilityMinAgeDays ?? DEFAULT_LOW_UTILITY_MIN_AGE_DAYS
45859
+ })).sort(memorySort);
45860
+ const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
45861
+ const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
45862
+ const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
45863
+ return {
45864
+ generatedAt: now.toISOString(),
45865
+ totalMemories: memories.length,
45866
+ activeMemories: activeMemories.length,
45867
+ deletedMemories: deletedMemories.slice(0, limit),
45868
+ expiredScratchMemories: expiredScratchMemories.slice(0, limit),
45869
+ supersededMemories: supersededMemories.slice(0, limit),
45870
+ supersededChains: buildSupersededChains(memories).slice(0, limit),
45871
+ lowUtilityMemories: lowUtilityMemories.slice(0, limit),
45872
+ neverRecalledMemories: neverRecalledMemories.slice(0, limit),
45873
+ 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),
45874
+ recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
45875
+ rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
45876
+ pendingProposals: pendingProposals.slice(0, limit),
45877
+ recallEventCount: recallUsage.length
45878
+ };
45879
+ }
45880
+ function shouldCompactMemory(memory, now = new Date) {
45881
+ if (memory.metadata.deleted === true)
45882
+ return "deleted";
45883
+ if (memory.supersededBy)
45884
+ return "superseded";
45885
+ if (memory.kind === "scratch" && isExpired(memory, now)) {
45886
+ return "expired_scratch";
45887
+ }
45888
+ return null;
45889
+ }
45890
+ function isActiveMemory(memory, now) {
45891
+ return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
45892
+ }
45893
+ function isLowUtility(memory, usageByMemory, now, options) {
45894
+ if (usageByMemory.has(memory.id))
45895
+ return false;
45896
+ const updated = Date.parse(memory.updatedAt);
45897
+ const ageDays = Number.isFinite(updated) ? (now.getTime() - updated) / (24 * 60 * 60 * 1000) : 0;
45898
+ return memory.confidence <= options.maxConfidence || ageDays >= options.minAgeDays;
45899
+ }
45900
+ function summarizeRecallByMemory(usageEvents) {
45901
+ const byMemory = new Map;
45902
+ for (const event of usageEvents) {
45903
+ event.memoryIds.forEach((memoryId, index) => {
45904
+ const role = event.agentRole ?? "unknown";
45905
+ const existing = byMemory.get(memoryId) ?? {
45906
+ memoryId,
45907
+ count: 0,
45908
+ lastRecalledAt: event.timestamp,
45909
+ agentRoles: {},
45910
+ averageScore: 0,
45911
+ scoreTotal: 0,
45912
+ scoreCount: 0
45913
+ };
45914
+ existing.count++;
45915
+ existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
45916
+ existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
45917
+ const score = event.scores[index];
45918
+ if (typeof score === "number" && Number.isFinite(score)) {
45919
+ existing.scoreTotal += score;
45920
+ existing.scoreCount++;
45921
+ existing.averageScore = existing.scoreTotal / existing.scoreCount;
45922
+ }
45923
+ byMemory.set(memoryId, existing);
45924
+ });
45925
+ }
45926
+ return new Map(Array.from(byMemory, ([memoryId, value]) => [
45927
+ memoryId,
45928
+ {
45929
+ memoryId,
45930
+ count: value.count,
45931
+ lastRecalledAt: value.lastRecalledAt,
45932
+ agentRoles: value.agentRoles,
45933
+ averageScore: value.averageScore
45934
+ }
45935
+ ]));
45936
+ }
45937
+ async function loadMaintenanceProposals(provider, limit) {
45938
+ if (!provider.listProposals)
45939
+ return [];
45940
+ const [pending, rejected, recent] = await Promise.all([
45941
+ provider.listProposals({ status: "pending", limit }),
45942
+ provider.listProposals({ status: "rejected", limit }),
45943
+ provider.listProposals({ limit: Math.max(limit * 4, 100) })
45944
+ ]);
45945
+ const byId = new Map;
45946
+ for (const proposal of [...pending, ...rejected, ...recent]) {
45947
+ byId.set(proposal.id, proposal);
45948
+ }
45949
+ return Array.from(byId.values());
45950
+ }
45951
+ function summarizeRecallByRole(usageEvents) {
45952
+ const byRole = new Map;
45953
+ for (const event of usageEvents) {
45954
+ const role = event.agentRole ?? "unknown";
45955
+ const existing = byRole.get(role) ?? {
45956
+ agentRole: role,
45957
+ count: 0,
45958
+ memoryIds: {}
45959
+ };
45960
+ existing.count++;
45961
+ for (const memoryId of event.memoryIds) {
45962
+ existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
45963
+ }
45964
+ byRole.set(role, existing);
45965
+ }
45966
+ return byRole;
45967
+ }
45968
+ function buildSupersededChains(memories) {
45969
+ const byId = new Map(memories.map((memory) => [memory.id, memory]));
45970
+ const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
45971
+ const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
45972
+ return roots.map((root) => {
45973
+ const chain = [root.id];
45974
+ const seen = new Set(chain);
45975
+ let cursor = root;
45976
+ while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
45977
+ chain.push(cursor.supersededBy);
45978
+ seen.add(cursor.supersededBy);
45979
+ cursor = byId.get(cursor.supersededBy);
45980
+ }
45981
+ return {
45982
+ rootId: root.id,
45983
+ chain,
45984
+ reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
45985
+ };
45986
+ });
45987
+ }
45988
+ function memorySort(a, b) {
45989
+ return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
45990
+ }
45991
+ function proposalSort(a, b) {
45992
+ const aTime = a.reviewedAt ?? a.createdAt;
45993
+ const bTime = b.reviewedAt ?? b.createdAt;
45994
+ return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
45995
+ }
45996
+ var DEFAULT_LOW_UTILITY_MAX_CONFIDENCE = 0.45, DEFAULT_LOW_UTILITY_MIN_AGE_DAYS = 30;
45997
+ var init_maintenance = __esm(() => {
45998
+ init_schema2();
45999
+ });
46000
+
45825
46001
  // src/memory/role-profiles.ts
45826
46002
  function resolveMemoryRecallProfile(agentRole) {
45827
46003
  const role = normalizeMemoryAgentRole(agentRole);
@@ -46209,16 +46385,21 @@ class LocalJsonlMemoryProvider {
46209
46385
  }
46210
46386
  async recordRecallUsage(event) {
46211
46387
  await this.initialize();
46212
- await this.audit("recall", event.bundleId, JSON.stringify({
46213
- query: event.query,
46214
- scopes: event.scopes,
46215
- kinds: event.kinds,
46216
- memoryIds: event.memoryIds,
46217
- scores: event.scores,
46218
- tokenEstimate: event.tokenEstimate,
46219
- agentRole: event.agentRole,
46220
- runId: event.runId
46221
- }));
46388
+ await this.audit("recall", event.bundleId, undefined, event);
46389
+ }
46390
+ async listRecallUsage(filter = {}) {
46391
+ await this.initialize();
46392
+ const events = await readAuditEvents(this.pathFor("audit"));
46393
+ const usage = [];
46394
+ for (const event of events) {
46395
+ if (event.operation !== "recall")
46396
+ continue;
46397
+ const parsed = parseRecallUsageEvent(event);
46398
+ if (parsed)
46399
+ usage.push(parsed);
46400
+ }
46401
+ usage.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
46402
+ return usage.slice(0, filter.limit ?? usage.length);
46222
46403
  }
46223
46404
  async list(filter = {}) {
46224
46405
  await this.initialize();
@@ -46238,7 +46419,9 @@ class LocalJsonlMemoryProvider {
46238
46419
  return !Number.isFinite(expires) || expires > now;
46239
46420
  });
46240
46421
  }
46241
- records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
46422
+ if (!filter.includeInactive) {
46423
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
46424
+ }
46242
46425
  records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
46243
46426
  return records.slice(0, filter.limit ?? records.length);
46244
46427
  }
@@ -46350,6 +46533,41 @@ class LocalJsonlMemoryProvider {
46350
46533
  await writeJsonlAtomic(this.pathFor("memories"), Array.from(this.memories.values()));
46351
46534
  await this.audit("compact", "memories");
46352
46535
  }
46536
+ async compactMaintenance(options = {}) {
46537
+ await this.initialize();
46538
+ const now = options.now ? new Date(options.now) : new Date;
46539
+ const kept = [];
46540
+ const result = {
46541
+ dryRun: options.dryRun !== false,
46542
+ removedDeleted: 0,
46543
+ removedSuperseded: 0,
46544
+ removedExpiredScratch: 0,
46545
+ remaining: 0
46546
+ };
46547
+ for (const memory of this.memories.values()) {
46548
+ const compactReason = shouldCompactMemory(memory, now);
46549
+ if (compactReason === "deleted") {
46550
+ result.removedDeleted++;
46551
+ continue;
46552
+ }
46553
+ if (compactReason === "superseded") {
46554
+ result.removedSuperseded++;
46555
+ continue;
46556
+ }
46557
+ if (compactReason === "expired_scratch") {
46558
+ result.removedExpiredScratch++;
46559
+ continue;
46560
+ }
46561
+ kept.push(memory);
46562
+ }
46563
+ result.remaining = kept.length;
46564
+ if (result.dryRun)
46565
+ return result;
46566
+ this.memories = new Map(kept.map((memory) => [memory.id, memory]));
46567
+ await writeJsonlAtomic(this.pathFor("memories"), kept);
46568
+ await this.audit("compact", "memories", "removed deleted, superseded, and expired scratch memories", result);
46569
+ return result;
46570
+ }
46353
46571
  async audit(operation, targetId, reason, eventJson) {
46354
46572
  const event = {
46355
46573
  id: randomUUID3(),
@@ -46428,6 +46646,46 @@ async function readJsonl(filePath) {
46428
46646
  }
46429
46647
  return records;
46430
46648
  }
46649
+ async function readAuditEvents(filePath) {
46650
+ const values = await readJsonl(filePath);
46651
+ const events = [];
46652
+ for (const value of values) {
46653
+ if (!value || typeof value !== "object")
46654
+ continue;
46655
+ const candidate = value;
46656
+ if (typeof candidate.id !== "string" || typeof candidate.operation !== "string" || typeof candidate.targetId !== "string" || typeof candidate.timestamp !== "string") {
46657
+ continue;
46658
+ }
46659
+ events.push(candidate);
46660
+ }
46661
+ return events;
46662
+ }
46663
+ function parseRecallUsageEvent(event) {
46664
+ const raw = event.eventJson ?? event.reason;
46665
+ if (typeof raw !== "string" && (!raw || typeof raw !== "object")) {
46666
+ return null;
46667
+ }
46668
+ try {
46669
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
46670
+ if (!Array.isArray(parsed.memoryIds) || typeof parsed.query !== "string") {
46671
+ return null;
46672
+ }
46673
+ return {
46674
+ bundleId: typeof parsed.bundleId === "string" ? parsed.bundleId : event.targetId,
46675
+ query: parsed.query,
46676
+ scopes: Array.isArray(parsed.scopes) ? parsed.scopes : [],
46677
+ kinds: Array.isArray(parsed.kinds) ? parsed.kinds : undefined,
46678
+ memoryIds: parsed.memoryIds.filter((memoryId) => typeof memoryId === "string"),
46679
+ scores: Array.isArray(parsed.scores) ? parsed.scores.filter((score) => typeof score === "number" && Number.isFinite(score)) : [],
46680
+ tokenEstimate: typeof parsed.tokenEstimate === "number" ? parsed.tokenEstimate : 0,
46681
+ agentRole: typeof parsed.agentRole === "string" ? parsed.agentRole : undefined,
46682
+ runId: typeof parsed.runId === "string" ? parsed.runId : undefined,
46683
+ timestamp: typeof parsed.timestamp === "string" ? parsed.timestamp : event.timestamp
46684
+ };
46685
+ } catch {
46686
+ return null;
46687
+ }
46688
+ }
46431
46689
  async function appendJsonl(filePath, value) {
46432
46690
  await mkdir8(path29.dirname(filePath), { recursive: true });
46433
46691
  await appendFile4(filePath, `${JSON.stringify(value)}
@@ -46447,6 +46705,7 @@ var init_local_jsonl_provider = __esm(() => {
46447
46705
  init_config3();
46448
46706
  init_curator_decision_helpers();
46449
46707
  init_errors6();
46708
+ init_maintenance();
46450
46709
  init_schema2();
46451
46710
  init_scoring();
46452
46711
  });
@@ -46704,6 +46963,10 @@ class SQLiteMemoryProvider {
46704
46963
  redaction: {
46705
46964
  ...DEFAULT_MEMORY_CONFIG.redaction,
46706
46965
  ...config3.redaction ?? {}
46966
+ },
46967
+ maintenance: {
46968
+ ...DEFAULT_MEMORY_CONFIG.maintenance,
46969
+ ...config3.maintenance ?? {}
46707
46970
  }
46708
46971
  };
46709
46972
  }
@@ -46809,6 +47072,26 @@ class SQLiteMemoryProvider {
46809
47072
  ) VALUES (?, ?, ?, ?)`, [randomUUID4(), event.bundleId, event.timestamp, JSON.stringify(event)]);
46810
47073
  await this.event("recall", event.bundleId, JSON.stringify(event));
46811
47074
  }
47075
+ async listRecallUsage(filter = {}) {
47076
+ await this.initialize();
47077
+ const rows = typeof filter.limit === "number" ? this.requireDb().query(`SELECT usage_json
47078
+ FROM memory_recall_usage
47079
+ ORDER BY timestamp DESC
47080
+ LIMIT ?`).all(Math.max(1, Math.trunc(filter.limit))) : this.requireDb().query(`SELECT usage_json
47081
+ FROM memory_recall_usage
47082
+ ORDER BY timestamp DESC
47083
+ `).all();
47084
+ const events = [];
47085
+ for (const row of rows) {
47086
+ try {
47087
+ const parsed = JSON.parse(row.usage_json);
47088
+ if (Array.isArray(parsed.memoryIds) && typeof parsed.query === "string") {
47089
+ events.push(parsed);
47090
+ }
47091
+ } catch {}
47092
+ }
47093
+ return events;
47094
+ }
46812
47095
  async list(filter = {}) {
46813
47096
  await this.initialize();
46814
47097
  let records = Array.from(this.memories.values());
@@ -46827,7 +47110,9 @@ class SQLiteMemoryProvider {
46827
47110
  return !Number.isFinite(expires) || expires > now;
46828
47111
  });
46829
47112
  }
46830
- records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
47113
+ if (!filter.includeInactive) {
47114
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
47115
+ }
46831
47116
  records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
46832
47117
  return records.slice(0, filter.limit ?? records.length);
46833
47118
  }
@@ -46908,6 +47193,52 @@ class SQLiteMemoryProvider {
46908
47193
  proposals: proposals.length
46909
47194
  };
46910
47195
  }
47196
+ async compactMaintenance(options = {}) {
47197
+ await this.initialize();
47198
+ const now = options.now ? new Date(options.now) : new Date;
47199
+ const kept = [];
47200
+ const removeIds = [];
47201
+ const result = {
47202
+ dryRun: options.dryRun !== false,
47203
+ removedDeleted: 0,
47204
+ removedSuperseded: 0,
47205
+ removedExpiredScratch: 0,
47206
+ remaining: 0
47207
+ };
47208
+ for (const memory of this.memories.values()) {
47209
+ const compactReason = shouldCompactMemory(memory, now);
47210
+ if (compactReason === "deleted") {
47211
+ result.removedDeleted++;
47212
+ removeIds.push(memory.id);
47213
+ continue;
47214
+ }
47215
+ if (compactReason === "superseded") {
47216
+ result.removedSuperseded++;
47217
+ removeIds.push(memory.id);
47218
+ continue;
47219
+ }
47220
+ if (compactReason === "expired_scratch") {
47221
+ result.removedExpiredScratch++;
47222
+ removeIds.push(memory.id);
47223
+ continue;
47224
+ }
47225
+ kept.push(memory);
47226
+ }
47227
+ result.remaining = kept.length;
47228
+ if (result.dryRun)
47229
+ return result;
47230
+ const db = this.requireDb();
47231
+ const compact = db.transaction(() => {
47232
+ for (const id of removeIds) {
47233
+ db.run("DELETE FROM memory_items WHERE id = ?", [id]);
47234
+ this.deleteMemoryFts(id);
47235
+ }
47236
+ this.insertEvent("compact", "memory_items", "removed deleted, superseded, and expired scratch memories", JSON.stringify(result));
47237
+ });
47238
+ compact();
47239
+ this.memories = new Map(kept.map((memory) => [memory.id, memory]));
47240
+ return result;
47241
+ }
46911
47242
  hasMigration(name) {
46912
47243
  const row = this.requireDb().query("SELECT version, name FROM schema_migrations WHERE name = ? LIMIT 1").get(name);
46913
47244
  return Boolean(row);
@@ -47384,6 +47715,7 @@ var init_sqlite_provider = __esm(() => {
47384
47715
  init_curator_decision_helpers();
47385
47716
  init_errors6();
47386
47717
  init_jsonl_migration();
47718
+ init_maintenance();
47387
47719
  init_schema2();
47388
47720
  init_scoring();
47389
47721
  FTS_INDEX_COLUMNS = [
@@ -47911,6 +48243,7 @@ var init_memory = __esm(() => {
47911
48243
  init_injector();
47912
48244
  init_jsonl_migration();
47913
48245
  init_local_jsonl_provider();
48246
+ init_maintenance();
47914
48247
  init_prompt_block();
47915
48248
  init_recall_planner();
47916
48249
  init_redaction();
@@ -47929,6 +48262,10 @@ async function handleMemoryCommand(_directory, _args) {
47929
48262
  "## Swarm Memory",
47930
48263
  "",
47931
48264
  "- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
48265
+ "- `/swarm memory pending` - show pending proposals and recent rejection reasons",
48266
+ "- `/swarm memory recall-log` - summarize recall usage by agent role and memory ID",
48267
+ "- `/swarm memory stale` - list expired scratch, superseded, deleted, and low-utility memories",
48268
+ "- `/swarm memory compact` - dry-run compaction; pass `--confirm` to remove deleted, superseded, and expired scratch records",
47932
48269
  "- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
47933
48270
  "- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
47934
48271
  "- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
@@ -47950,6 +48287,7 @@ async function handleMemoryStatusCommand(directory, _args) {
47950
48287
  `- Storage: \`${storageDir}\``,
47951
48288
  `- SQLite path: \`${sqlitePath}\``,
47952
48289
  `- SQLite database exists: \`${existsSync20(sqlitePath)}\``,
48290
+ `- Automatic destructive cleanup: \`disabled\``,
47953
48291
  "",
47954
48292
  "### Legacy JSONL"
47955
48293
  ];
@@ -47974,6 +48312,119 @@ async function handleMemoryStatusCommand(directory, _args) {
47974
48312
  return lines.join(`
47975
48313
  `);
47976
48314
  }
48315
+ async function handleMemoryPendingCommand(directory, args) {
48316
+ const parsed = parseMaintenanceArgs(args, {
48317
+ usage: "Usage: /swarm memory pending [--limit <n>]",
48318
+ allowConfirm: false
48319
+ });
48320
+ if ("error" in parsed)
48321
+ return parsed.error;
48322
+ const config3 = resolveCommandMemoryConfig(directory);
48323
+ const provider = createMaintenanceProvider(directory, config3);
48324
+ try {
48325
+ await provider.initialize?.();
48326
+ const report = await buildMemoryMaintenanceReport(provider, {
48327
+ ...maintenanceReportOptions(config3, parsed.limit)
48328
+ });
48329
+ const lines = [
48330
+ "## Swarm Memory Pending",
48331
+ "",
48332
+ `- Pending proposals shown: \`${report.pendingProposals.length}\``,
48333
+ `- Rejected proposal reasons shown: \`${report.rejectedProposalReasons.length}\``
48334
+ ];
48335
+ appendProposalLines(lines, "Pending proposals", report.pendingProposals);
48336
+ appendProposalLines(lines, "Rejected proposal reasons", report.rejectedProposalReasons);
48337
+ return lines.join(`
48338
+ `);
48339
+ } finally {
48340
+ await provider.close?.();
48341
+ }
48342
+ }
48343
+ async function handleMemoryRecallLogCommand(directory, args) {
48344
+ const parsed = parseMaintenanceArgs(args, {
48345
+ usage: "Usage: /swarm memory recall-log [--limit <n>]",
48346
+ allowConfirm: false
48347
+ });
48348
+ if ("error" in parsed)
48349
+ return parsed.error;
48350
+ const config3 = resolveCommandMemoryConfig(directory);
48351
+ const provider = createMaintenanceProvider(directory, config3);
48352
+ try {
48353
+ await provider.initialize?.();
48354
+ const report = await buildMemoryMaintenanceReport(provider, {
48355
+ ...maintenanceReportOptions(config3, parsed.limit)
48356
+ });
48357
+ const lines = [
48358
+ "## Swarm Memory Recall Log",
48359
+ "",
48360
+ `- Recall events scanned: \`${report.recallEventCount}\``,
48361
+ `- Most-recalled memories shown: \`${report.mostRecalledMemories.length}\``,
48362
+ `- Never-recalled memories shown: \`${report.neverRecalledMemories.length}\``
48363
+ ];
48364
+ appendRecallRoleLines(lines, report.recallByAgentRole);
48365
+ appendRecallMemoryLines(lines, report.mostRecalledMemories);
48366
+ appendMemoryLines(lines, "Never-recalled memories", report.neverRecalledMemories);
48367
+ return lines.join(`
48368
+ `);
48369
+ } finally {
48370
+ await provider.close?.();
48371
+ }
48372
+ }
48373
+ async function handleMemoryStaleCommand(directory, args) {
48374
+ const parsed = parseMaintenanceArgs(args, {
48375
+ usage: "Usage: /swarm memory stale [--limit <n>]",
48376
+ allowConfirm: false
48377
+ });
48378
+ if ("error" in parsed)
48379
+ return parsed.error;
48380
+ const config3 = resolveCommandMemoryConfig(directory);
48381
+ const provider = createMaintenanceProvider(directory, config3);
48382
+ try {
48383
+ await provider.initialize?.();
48384
+ const report = await buildMemoryMaintenanceReport(provider, {
48385
+ ...maintenanceReportOptions(config3, parsed.limit)
48386
+ });
48387
+ const lines = [
48388
+ "## Swarm Memory Stale",
48389
+ "",
48390
+ `- Active memories: \`${report.activeMemories}\``,
48391
+ `- Expired scratch memories shown: \`${report.expiredScratchMemories.length}\``,
48392
+ `- Deleted tombstones shown: \`${report.deletedMemories.length}\``,
48393
+ `- Superseded memories shown: \`${report.supersededMemories.length}\``,
48394
+ `- Low-utility memories shown: \`${report.lowUtilityMemories.length}\``
48395
+ ];
48396
+ appendMemoryLines(lines, "Expired scratch memories", report.expiredScratchMemories);
48397
+ appendMemoryLines(lines, "Deleted tombstones", report.deletedMemories);
48398
+ appendSupersededChains(lines, report);
48399
+ appendMemoryLines(lines, "Low-utility memories", report.lowUtilityMemories);
48400
+ return lines.join(`
48401
+ `);
48402
+ } finally {
48403
+ await provider.close?.();
48404
+ }
48405
+ }
48406
+ async function handleMemoryCompactCommand(directory, args) {
48407
+ const parsed = parseMaintenanceArgs(args, {
48408
+ usage: "Usage: /swarm memory compact [--confirm]",
48409
+ allowConfirm: true,
48410
+ allowLimit: false
48411
+ });
48412
+ if ("error" in parsed)
48413
+ return parsed.error;
48414
+ const provider = createMaintenanceProvider(directory, resolveCommandMemoryConfig(directory));
48415
+ try {
48416
+ await provider.initialize?.();
48417
+ if (!provider.compactMaintenance) {
48418
+ return "Memory provider does not support compaction.";
48419
+ }
48420
+ const result = await provider.compactMaintenance({
48421
+ dryRun: !parsed.confirm
48422
+ });
48423
+ return formatCompactResult(result, parsed.confirm);
48424
+ } finally {
48425
+ await provider.close?.();
48426
+ }
48427
+ }
47977
48428
  async function handleMemoryMigrateCommand(directory, _args) {
47978
48429
  const config3 = {
47979
48430
  ...resolveCommandMemoryConfig(directory),
@@ -48030,6 +48481,16 @@ async function handleMemoryExportCommand(directory, _args) {
48030
48481
  await provider.close?.();
48031
48482
  }
48032
48483
  }
48484
+ function createMaintenanceProvider(directory, config3) {
48485
+ return createConfiguredMemoryProvider(directory, config3);
48486
+ }
48487
+ function maintenanceReportOptions(config3, limit) {
48488
+ return {
48489
+ limit,
48490
+ lowUtilityMaxConfidence: config3.maintenance.lowUtilityMaxConfidence,
48491
+ lowUtilityMinAgeDays: config3.maintenance.lowUtilityMinAgeDays
48492
+ };
48493
+ }
48033
48494
  async function handleMemoryEvaluateCommand(directory, args) {
48034
48495
  const parsed = parseEvaluateArgs(directory, args);
48035
48496
  if ("error" in parsed)
@@ -48088,6 +48549,28 @@ function parseEvaluateArgs(directory, args) {
48088
48549
  }
48089
48550
  return { json: json3, fixtureDirectory };
48090
48551
  }
48552
+ function parseMaintenanceArgs(args, options) {
48553
+ let limit = 20;
48554
+ let confirm = false;
48555
+ const allowLimit = options.allowLimit ?? true;
48556
+ for (let i = 0;i < args.length; i++) {
48557
+ const arg = args[i];
48558
+ if (arg === "--confirm" && options.allowConfirm) {
48559
+ confirm = true;
48560
+ continue;
48561
+ }
48562
+ if (arg === "--limit" && allowLimit) {
48563
+ const next = args[i + 1];
48564
+ if (!next || !/^\d+$/.test(next))
48565
+ return { error: options.usage };
48566
+ limit = Math.min(100, Math.max(1, Number(next)));
48567
+ i++;
48568
+ continue;
48569
+ }
48570
+ return { error: options.usage };
48571
+ }
48572
+ return { limit, confirm };
48573
+ }
48091
48574
  function resolvePackageRootFromModule(modulePath) {
48092
48575
  const moduleDir = path33.dirname(modulePath);
48093
48576
  const leaf = path33.basename(moduleDir);
@@ -48138,6 +48621,80 @@ function appendInvalidRows(lines, invalidRows) {
48138
48621
  lines.push(`- ... ${invalidRows.length - 20} more`);
48139
48622
  }
48140
48623
  }
48624
+ function appendProposalLines(lines, title, proposals) {
48625
+ lines.push("", `### ${title}`);
48626
+ if (proposals.length === 0) {
48627
+ lines.push("- none");
48628
+ return;
48629
+ }
48630
+ for (const proposal of proposals) {
48631
+ const reason = proposal.status === "rejected" ? ` - ${proposal.rejectionReason ?? "no reason recorded"}` : "";
48632
+ lines.push(`- \`${proposal.id}\` ${proposal.operation} ${proposal.targetMemoryId ?? proposal.proposedRecord?.id ?? "new"} (${proposal.status})${reason}`);
48633
+ }
48634
+ }
48635
+ function appendMemoryLines(lines, title, memories) {
48636
+ lines.push("", `### ${title}`);
48637
+ if (memories.length === 0) {
48638
+ lines.push("- none");
48639
+ return;
48640
+ }
48641
+ for (const memory of memories) {
48642
+ lines.push(`- \`${memory.id}\` ${memory.kind} confidence=${memory.confidence.toFixed(2)} updated=${memory.updatedAt} - ${truncate(memory.text, 100)}`);
48643
+ }
48644
+ }
48645
+ function appendRecallRoleLines(lines, roles) {
48646
+ lines.push("", "### Recall by agent role");
48647
+ if (roles.length === 0) {
48648
+ lines.push("- none");
48649
+ return;
48650
+ }
48651
+ for (const role of roles) {
48652
+ const memoryCount = Object.keys(role.memoryIds).length;
48653
+ lines.push(`- \`${role.agentRole}\`: ${role.count} recall event(s), ${memoryCount} memory ID(s)`);
48654
+ }
48655
+ }
48656
+ function appendRecallMemoryLines(lines, memories) {
48657
+ lines.push("", "### Most-recalled memories");
48658
+ if (memories.length === 0) {
48659
+ lines.push("- none");
48660
+ return;
48661
+ }
48662
+ for (const memory of memories) {
48663
+ lines.push(`- \`${memory.memoryId}\`: ${memory.count} hit(s), last=${memory.lastRecalledAt}, avgScore=${memory.averageScore.toFixed(3)}`);
48664
+ }
48665
+ }
48666
+ function appendSupersededChains(lines, report) {
48667
+ lines.push("", "### Superseded chains");
48668
+ if (report.supersededChains.length === 0) {
48669
+ lines.push("- none");
48670
+ return;
48671
+ }
48672
+ for (const chain of report.supersededChains) {
48673
+ const reason = chain.reason ? ` - ${chain.reason}` : "";
48674
+ lines.push(`- ${chain.chain.map((id) => `\`${id}\``).join(" -> ")}${reason}`);
48675
+ }
48676
+ }
48677
+ function formatCompactResult(result, confirmed) {
48678
+ const lines = [
48679
+ "## Swarm Memory Compact",
48680
+ "",
48681
+ `- Mode: \`${confirmed ? "confirmed" : "dry-run"}\``,
48682
+ `- Deleted tombstones: \`${result.removedDeleted}\``,
48683
+ `- Superseded records: \`${result.removedSuperseded}\``,
48684
+ `- Expired scratch records: \`${result.removedExpiredScratch}\``,
48685
+ `- Remaining memories: \`${result.remaining}\``
48686
+ ];
48687
+ if (!confirmed) {
48688
+ lines.push("", "No records were removed. Re-run `/swarm memory compact --confirm` to apply this compaction.");
48689
+ }
48690
+ return lines.join(`
48691
+ `);
48692
+ }
48693
+ function truncate(value, maxLength) {
48694
+ if (value.length <= maxLength)
48695
+ return value;
48696
+ return `${value.slice(0, maxLength - 3)}...`;
48697
+ }
48141
48698
  var PACKAGE_ROOT;
48142
48699
  var init_memory2 = __esm(() => {
48143
48700
  init_loader();
@@ -56197,7 +56754,7 @@ function classifySwarmCommandToolUse(resolved) {
56197
56754
  return { allowed: true };
56198
56755
  return {
56199
56756
  allowed: false,
56200
- 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."
56757
+ 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."
56201
56758
  };
56202
56759
  }
56203
56760
  if (canonicalKey === "memory evaluate") {
@@ -56210,6 +56767,17 @@ function classifySwarmCommandToolUse(resolved) {
56210
56767
  message: "Usage through swarm_command: `/swarm memory evaluate --json`. Custom fixture directories are only available through direct user command execution."
56211
56768
  };
56212
56769
  }
56770
+ if (canonicalKey === "memory pending" || canonicalKey === "memory recall-log" || canonicalKey === "memory stale") {
56771
+ if (args.length === 0)
56772
+ return { allowed: true };
56773
+ if (args.length === 2 && args[0] === "--limit" && /^\d+$/.test(args[1])) {
56774
+ return { allowed: true };
56775
+ }
56776
+ return {
56777
+ allowed: false,
56778
+ message: `Usage through swarm_command: \`/swarm ${canonicalKey}\` or ` + `\`/swarm ${canonicalKey} --limit <n>\`.`
56779
+ };
56780
+ }
56213
56781
  if (canonicalKey === "retrieve") {
56214
56782
  if (args.length !== 1 || !SUMMARY_ID_PATTERN.test(args[0])) {
56215
56783
  return {
@@ -56261,7 +56829,7 @@ function classifySwarmCommandChatFallbackUse(resolved) {
56261
56829
  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."
56262
56830
  };
56263
56831
  }
56264
- if (canonicalKey === "knowledge migrate" || canonicalKey === "knowledge quarantine" || canonicalKey === "knowledge restore" || canonicalKey === "memory import" || canonicalKey === "memory migrate") {
56832
+ if (canonicalKey === "knowledge migrate" || canonicalKey === "knowledge quarantine" || canonicalKey === "knowledge restore" || canonicalKey === "memory import" || canonicalKey === "memory migrate" || canonicalKey === "memory compact") {
56265
56833
  return {
56266
56834
  allowed: false,
56267
56835
  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."
@@ -56294,6 +56862,10 @@ var init_tool_policy = __esm(() => {
56294
56862
  "knowledge",
56295
56863
  "memory",
56296
56864
  "memory status",
56865
+ "memory pending",
56866
+ "memory recall-log",
56867
+ "memory compact",
56868
+ "memory stale",
56297
56869
  "memory export",
56298
56870
  "memory evaluate",
56299
56871
  "memory import",
@@ -56320,6 +56892,9 @@ var init_tool_policy = __esm(() => {
56320
56892
  "knowledge",
56321
56893
  "memory",
56322
56894
  "memory status",
56895
+ "memory pending",
56896
+ "memory recall-log",
56897
+ "memory stale",
56323
56898
  "memory export",
56324
56899
  "memory evaluate",
56325
56900
  "sync-plan",
@@ -56332,7 +56907,8 @@ var init_tool_policy = __esm(() => {
56332
56907
  "rollback",
56333
56908
  "checkpoint",
56334
56909
  "memory import",
56335
- "memory migrate"
56910
+ "memory migrate",
56911
+ "memory compact"
56336
56912
  ]);
56337
56913
  NO_ARGS = new Set([
56338
56914
  "agents",
@@ -57327,6 +57903,34 @@ Subcommands:
57327
57903
  args: "",
57328
57904
  category: "diagnostics"
57329
57905
  },
57906
+ "memory pending": {
57907
+ handler: (ctx) => handleMemoryPendingCommand(ctx.directory, ctx.args),
57908
+ description: "Show pending Swarm memory proposals and rejection reasons",
57909
+ subcommandOf: "memory",
57910
+ args: "--limit <n>",
57911
+ category: "diagnostics"
57912
+ },
57913
+ "memory recall-log": {
57914
+ handler: (ctx) => handleMemoryRecallLogCommand(ctx.directory, ctx.args),
57915
+ description: "Summarize Swarm memory recall usage",
57916
+ subcommandOf: "memory",
57917
+ args: "--limit <n>",
57918
+ category: "diagnostics"
57919
+ },
57920
+ "memory compact": {
57921
+ handler: (ctx) => handleMemoryCompactCommand(ctx.directory, ctx.args),
57922
+ description: "Compact deleted, superseded, and expired scratch memories",
57923
+ subcommandOf: "memory",
57924
+ args: "--confirm",
57925
+ category: "utility"
57926
+ },
57927
+ "memory stale": {
57928
+ handler: (ctx) => handleMemoryStaleCommand(ctx.directory, ctx.args),
57929
+ description: "List stale and low-utility Swarm memories",
57930
+ subcommandOf: "memory",
57931
+ args: "--limit <n>",
57932
+ category: "diagnostics"
57933
+ },
57330
57934
  "memory export": {
57331
57935
  handler: (ctx) => handleMemoryExportCommand(ctx.directory, ctx.args),
57332
57936
  description: "Export current Swarm memory to JSONL files",