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/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.40.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({
@@ -66897,6 +66904,10 @@ function resolveMemoryConfig(input) {
66897
66904
  redaction: {
66898
66905
  ...DEFAULT_MEMORY_CONFIG.redaction,
66899
66906
  ...input?.redaction ?? {}
66907
+ },
66908
+ maintenance: {
66909
+ ...DEFAULT_MEMORY_CONFIG.maintenance,
66910
+ ...input?.maintenance ?? {}
66900
66911
  }
66901
66912
  };
66902
66913
  }
@@ -66928,6 +66939,10 @@ var init_config3 = __esm(() => {
66928
66939
  redaction: {
66929
66940
  rejectDurableSecrets: true
66930
66941
  },
66942
+ maintenance: {
66943
+ lowUtilityMaxConfidence: 0.45,
66944
+ lowUtilityMinAgeDays: 30
66945
+ },
66931
66946
  hardDelete: false
66932
66947
  };
66933
66948
  DURABLE_MEMORY_KINDS = new Set([
@@ -67340,6 +67355,167 @@ var init_curator_decision_helpers = __esm(() => {
67340
67355
  init_schema2();
67341
67356
  });
67342
67357
 
67358
+ // src/memory/maintenance.ts
67359
+ async function buildMemoryMaintenanceReport(provider, options = {}) {
67360
+ const now = options.now ?? new Date;
67361
+ const limit = Math.max(1, Math.trunc(options.limit ?? 20));
67362
+ const memories = await provider.list({
67363
+ includeExpired: true,
67364
+ includeInactive: true
67365
+ });
67366
+ const proposals = await loadMaintenanceProposals(provider, limit);
67367
+ const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
67368
+ const usageByMemory = summarizeRecallByMemory(recallUsage);
67369
+ const usageByRole = summarizeRecallByRole(recallUsage);
67370
+ const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
67371
+ const deletedMemories = memories.filter((memory) => memory.metadata.deleted === true);
67372
+ const expiredScratchMemories = memories.filter((memory) => memory.kind === "scratch" && isExpired(memory, now));
67373
+ const supersededMemories = memories.filter((memory) => Boolean(memory.supersededBy));
67374
+ const lowUtilityMemories = activeMemories.filter((memory) => isLowUtility(memory, usageByMemory, now, {
67375
+ maxConfidence: options.lowUtilityMaxConfidence ?? DEFAULT_LOW_UTILITY_MAX_CONFIDENCE,
67376
+ minAgeDays: options.lowUtilityMinAgeDays ?? DEFAULT_LOW_UTILITY_MIN_AGE_DAYS
67377
+ })).sort(memorySort);
67378
+ const neverRecalledMemories = activeMemories.filter((memory) => !usageByMemory.has(memory.id)).sort(memorySort);
67379
+ const rejectedProposalReasons = proposals.filter((proposal) => proposal.status === "rejected").sort(proposalSort);
67380
+ const pendingProposals = proposals.filter((proposal) => proposal.status === "pending").sort(proposalSort);
67381
+ return {
67382
+ generatedAt: now.toISOString(),
67383
+ totalMemories: memories.length,
67384
+ activeMemories: activeMemories.length,
67385
+ deletedMemories: deletedMemories.slice(0, limit),
67386
+ expiredScratchMemories: expiredScratchMemories.slice(0, limit),
67387
+ supersededMemories: supersededMemories.slice(0, limit),
67388
+ supersededChains: buildSupersededChains(memories).slice(0, limit),
67389
+ lowUtilityMemories: lowUtilityMemories.slice(0, limit),
67390
+ neverRecalledMemories: neverRecalledMemories.slice(0, limit),
67391
+ 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),
67392
+ recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
67393
+ rejectedProposalReasons: rejectedProposalReasons.slice(0, limit),
67394
+ pendingProposals: pendingProposals.slice(0, limit),
67395
+ recallEventCount: recallUsage.length
67396
+ };
67397
+ }
67398
+ function shouldCompactMemory(memory, now = new Date) {
67399
+ if (memory.metadata.deleted === true)
67400
+ return "deleted";
67401
+ if (memory.supersededBy)
67402
+ return "superseded";
67403
+ if (memory.kind === "scratch" && isExpired(memory, now)) {
67404
+ return "expired_scratch";
67405
+ }
67406
+ return null;
67407
+ }
67408
+ function isActiveMemory(memory, now) {
67409
+ return memory.metadata.deleted !== true && !memory.supersededBy && !isExpired(memory, now);
67410
+ }
67411
+ function isLowUtility(memory, usageByMemory, now, options) {
67412
+ if (usageByMemory.has(memory.id))
67413
+ return false;
67414
+ const updated = Date.parse(memory.updatedAt);
67415
+ const ageDays = Number.isFinite(updated) ? (now.getTime() - updated) / (24 * 60 * 60 * 1000) : 0;
67416
+ return memory.confidence <= options.maxConfidence || ageDays >= options.minAgeDays;
67417
+ }
67418
+ function summarizeRecallByMemory(usageEvents) {
67419
+ const byMemory = new Map;
67420
+ for (const event of usageEvents) {
67421
+ event.memoryIds.forEach((memoryId, index) => {
67422
+ const role = event.agentRole ?? "unknown";
67423
+ const existing = byMemory.get(memoryId) ?? {
67424
+ memoryId,
67425
+ count: 0,
67426
+ lastRecalledAt: event.timestamp,
67427
+ agentRoles: {},
67428
+ averageScore: 0,
67429
+ scoreTotal: 0,
67430
+ scoreCount: 0
67431
+ };
67432
+ existing.count++;
67433
+ existing.lastRecalledAt = event.timestamp > existing.lastRecalledAt ? event.timestamp : existing.lastRecalledAt;
67434
+ existing.agentRoles[role] = (existing.agentRoles[role] ?? 0) + 1;
67435
+ const score = event.scores[index];
67436
+ if (typeof score === "number" && Number.isFinite(score)) {
67437
+ existing.scoreTotal += score;
67438
+ existing.scoreCount++;
67439
+ existing.averageScore = existing.scoreTotal / existing.scoreCount;
67440
+ }
67441
+ byMemory.set(memoryId, existing);
67442
+ });
67443
+ }
67444
+ return new Map(Array.from(byMemory, ([memoryId, value]) => [
67445
+ memoryId,
67446
+ {
67447
+ memoryId,
67448
+ count: value.count,
67449
+ lastRecalledAt: value.lastRecalledAt,
67450
+ agentRoles: value.agentRoles,
67451
+ averageScore: value.averageScore
67452
+ }
67453
+ ]));
67454
+ }
67455
+ async function loadMaintenanceProposals(provider, limit) {
67456
+ if (!provider.listProposals)
67457
+ return [];
67458
+ const [pending, rejected, recent] = await Promise.all([
67459
+ provider.listProposals({ status: "pending", limit }),
67460
+ provider.listProposals({ status: "rejected", limit }),
67461
+ provider.listProposals({ limit: Math.max(limit * 4, 100) })
67462
+ ]);
67463
+ const byId = new Map;
67464
+ for (const proposal of [...pending, ...rejected, ...recent]) {
67465
+ byId.set(proposal.id, proposal);
67466
+ }
67467
+ return Array.from(byId.values());
67468
+ }
67469
+ function summarizeRecallByRole(usageEvents) {
67470
+ const byRole = new Map;
67471
+ for (const event of usageEvents) {
67472
+ const role = event.agentRole ?? "unknown";
67473
+ const existing = byRole.get(role) ?? {
67474
+ agentRole: role,
67475
+ count: 0,
67476
+ memoryIds: {}
67477
+ };
67478
+ existing.count++;
67479
+ for (const memoryId of event.memoryIds) {
67480
+ existing.memoryIds[memoryId] = (existing.memoryIds[memoryId] ?? 0) + 1;
67481
+ }
67482
+ byRole.set(role, existing);
67483
+ }
67484
+ return byRole;
67485
+ }
67486
+ function buildSupersededChains(memories) {
67487
+ const byId = new Map(memories.map((memory) => [memory.id, memory]));
67488
+ const supersededIds = new Set(memories.filter((memory) => memory.supersededBy).map((memory) => memory.id));
67489
+ const roots = memories.filter((memory) => memory.supersededBy && !(memory.supersedes ?? []).some((id) => supersededIds.has(id)));
67490
+ return roots.map((root) => {
67491
+ const chain = [root.id];
67492
+ const seen = new Set(chain);
67493
+ let cursor = root;
67494
+ while (cursor?.supersededBy && !seen.has(cursor.supersededBy)) {
67495
+ chain.push(cursor.supersededBy);
67496
+ seen.add(cursor.supersededBy);
67497
+ cursor = byId.get(cursor.supersededBy);
67498
+ }
67499
+ return {
67500
+ rootId: root.id,
67501
+ chain,
67502
+ reason: typeof root.metadata.supersedeReason === "string" ? root.metadata.supersedeReason : undefined
67503
+ };
67504
+ });
67505
+ }
67506
+ function memorySort(a, b) {
67507
+ return b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id);
67508
+ }
67509
+ function proposalSort(a, b) {
67510
+ const aTime = a.reviewedAt ?? a.createdAt;
67511
+ const bTime = b.reviewedAt ?? b.createdAt;
67512
+ return bTime.localeCompare(aTime) || a.id.localeCompare(b.id);
67513
+ }
67514
+ var DEFAULT_LOW_UTILITY_MAX_CONFIDENCE = 0.45, DEFAULT_LOW_UTILITY_MIN_AGE_DAYS = 30;
67515
+ var init_maintenance = __esm(() => {
67516
+ init_schema2();
67517
+ });
67518
+
67343
67519
  // src/memory/role-profiles.ts
67344
67520
  function resolveMemoryRecallProfile(agentRole) {
67345
67521
  const role = normalizeMemoryAgentRole(agentRole);
@@ -67727,16 +67903,21 @@ class LocalJsonlMemoryProvider {
67727
67903
  }
67728
67904
  async recordRecallUsage(event) {
67729
67905
  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
- }));
67906
+ await this.audit("recall", event.bundleId, undefined, event);
67907
+ }
67908
+ async listRecallUsage(filter = {}) {
67909
+ await this.initialize();
67910
+ const events = await readAuditEvents(this.pathFor("audit"));
67911
+ const usage = [];
67912
+ for (const event of events) {
67913
+ if (event.operation !== "recall")
67914
+ continue;
67915
+ const parsed = parseRecallUsageEvent(event);
67916
+ if (parsed)
67917
+ usage.push(parsed);
67918
+ }
67919
+ usage.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
67920
+ return usage.slice(0, filter.limit ?? usage.length);
67740
67921
  }
67741
67922
  async list(filter = {}) {
67742
67923
  await this.initialize();
@@ -67756,7 +67937,9 @@ class LocalJsonlMemoryProvider {
67756
67937
  return !Number.isFinite(expires) || expires > now;
67757
67938
  });
67758
67939
  }
67759
- records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
67940
+ if (!filter.includeInactive) {
67941
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
67942
+ }
67760
67943
  records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
67761
67944
  return records.slice(0, filter.limit ?? records.length);
67762
67945
  }
@@ -67868,6 +68051,41 @@ class LocalJsonlMemoryProvider {
67868
68051
  await writeJsonlAtomic(this.pathFor("memories"), Array.from(this.memories.values()));
67869
68052
  await this.audit("compact", "memories");
67870
68053
  }
68054
+ async compactMaintenance(options = {}) {
68055
+ await this.initialize();
68056
+ const now = options.now ? new Date(options.now) : new Date;
68057
+ const kept = [];
68058
+ const result = {
68059
+ dryRun: options.dryRun !== false,
68060
+ removedDeleted: 0,
68061
+ removedSuperseded: 0,
68062
+ removedExpiredScratch: 0,
68063
+ remaining: 0
68064
+ };
68065
+ for (const memory of this.memories.values()) {
68066
+ const compactReason = shouldCompactMemory(memory, now);
68067
+ if (compactReason === "deleted") {
68068
+ result.removedDeleted++;
68069
+ continue;
68070
+ }
68071
+ if (compactReason === "superseded") {
68072
+ result.removedSuperseded++;
68073
+ continue;
68074
+ }
68075
+ if (compactReason === "expired_scratch") {
68076
+ result.removedExpiredScratch++;
68077
+ continue;
68078
+ }
68079
+ kept.push(memory);
68080
+ }
68081
+ result.remaining = kept.length;
68082
+ if (result.dryRun)
68083
+ return result;
68084
+ this.memories = new Map(kept.map((memory) => [memory.id, memory]));
68085
+ await writeJsonlAtomic(this.pathFor("memories"), kept);
68086
+ await this.audit("compact", "memories", "removed deleted, superseded, and expired scratch memories", result);
68087
+ return result;
68088
+ }
67871
68089
  async audit(operation, targetId, reason, eventJson) {
67872
68090
  const event = {
67873
68091
  id: randomUUID4(),
@@ -67946,6 +68164,46 @@ async function readJsonl(filePath) {
67946
68164
  }
67947
68165
  return records;
67948
68166
  }
68167
+ async function readAuditEvents(filePath) {
68168
+ const values = await readJsonl(filePath);
68169
+ const events = [];
68170
+ for (const value of values) {
68171
+ if (!value || typeof value !== "object")
68172
+ continue;
68173
+ const candidate = value;
68174
+ if (typeof candidate.id !== "string" || typeof candidate.operation !== "string" || typeof candidate.targetId !== "string" || typeof candidate.timestamp !== "string") {
68175
+ continue;
68176
+ }
68177
+ events.push(candidate);
68178
+ }
68179
+ return events;
68180
+ }
68181
+ function parseRecallUsageEvent(event) {
68182
+ const raw = event.eventJson ?? event.reason;
68183
+ if (typeof raw !== "string" && (!raw || typeof raw !== "object")) {
68184
+ return null;
68185
+ }
68186
+ try {
68187
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
68188
+ if (!Array.isArray(parsed.memoryIds) || typeof parsed.query !== "string") {
68189
+ return null;
68190
+ }
68191
+ return {
68192
+ bundleId: typeof parsed.bundleId === "string" ? parsed.bundleId : event.targetId,
68193
+ query: parsed.query,
68194
+ scopes: Array.isArray(parsed.scopes) ? parsed.scopes : [],
68195
+ kinds: Array.isArray(parsed.kinds) ? parsed.kinds : undefined,
68196
+ memoryIds: parsed.memoryIds.filter((memoryId) => typeof memoryId === "string"),
68197
+ scores: Array.isArray(parsed.scores) ? parsed.scores.filter((score) => typeof score === "number" && Number.isFinite(score)) : [],
68198
+ tokenEstimate: typeof parsed.tokenEstimate === "number" ? parsed.tokenEstimate : 0,
68199
+ agentRole: typeof parsed.agentRole === "string" ? parsed.agentRole : undefined,
68200
+ runId: typeof parsed.runId === "string" ? parsed.runId : undefined,
68201
+ timestamp: typeof parsed.timestamp === "string" ? parsed.timestamp : event.timestamp
68202
+ };
68203
+ } catch {
68204
+ return null;
68205
+ }
68206
+ }
67949
68207
  async function appendJsonl(filePath, value) {
67950
68208
  await mkdir9(path39.dirname(filePath), { recursive: true });
67951
68209
  await appendFile5(filePath, `${JSON.stringify(value)}
@@ -67965,6 +68223,7 @@ var init_local_jsonl_provider = __esm(() => {
67965
68223
  init_config3();
67966
68224
  init_curator_decision_helpers();
67967
68225
  init_errors6();
68226
+ init_maintenance();
67968
68227
  init_schema2();
67969
68228
  init_scoring();
67970
68229
  });
@@ -68296,6 +68555,10 @@ class SQLiteMemoryProvider {
68296
68555
  redaction: {
68297
68556
  ...DEFAULT_MEMORY_CONFIG.redaction,
68298
68557
  ...config3.redaction ?? {}
68558
+ },
68559
+ maintenance: {
68560
+ ...DEFAULT_MEMORY_CONFIG.maintenance,
68561
+ ...config3.maintenance ?? {}
68299
68562
  }
68300
68563
  };
68301
68564
  }
@@ -68401,6 +68664,26 @@ class SQLiteMemoryProvider {
68401
68664
  ) VALUES (?, ?, ?, ?)`, [randomUUID5(), event.bundleId, event.timestamp, JSON.stringify(event)]);
68402
68665
  await this.event("recall", event.bundleId, JSON.stringify(event));
68403
68666
  }
68667
+ async listRecallUsage(filter = {}) {
68668
+ await this.initialize();
68669
+ const rows = typeof filter.limit === "number" ? this.requireDb().query(`SELECT usage_json
68670
+ FROM memory_recall_usage
68671
+ ORDER BY timestamp DESC
68672
+ LIMIT ?`).all(Math.max(1, Math.trunc(filter.limit))) : this.requireDb().query(`SELECT usage_json
68673
+ FROM memory_recall_usage
68674
+ ORDER BY timestamp DESC
68675
+ `).all();
68676
+ const events = [];
68677
+ for (const row of rows) {
68678
+ try {
68679
+ const parsed = JSON.parse(row.usage_json);
68680
+ if (Array.isArray(parsed.memoryIds) && typeof parsed.query === "string") {
68681
+ events.push(parsed);
68682
+ }
68683
+ } catch {}
68684
+ }
68685
+ return events;
68686
+ }
68404
68687
  async list(filter = {}) {
68405
68688
  await this.initialize();
68406
68689
  let records = Array.from(this.memories.values());
@@ -68419,7 +68702,9 @@ class SQLiteMemoryProvider {
68419
68702
  return !Number.isFinite(expires) || expires > now;
68420
68703
  });
68421
68704
  }
68422
- records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
68705
+ if (!filter.includeInactive) {
68706
+ records = records.filter((record3) => !record3.supersededBy && record3.metadata.deleted !== true);
68707
+ }
68423
68708
  records.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
68424
68709
  return records.slice(0, filter.limit ?? records.length);
68425
68710
  }
@@ -68500,6 +68785,52 @@ class SQLiteMemoryProvider {
68500
68785
  proposals: proposals.length
68501
68786
  };
68502
68787
  }
68788
+ async compactMaintenance(options = {}) {
68789
+ await this.initialize();
68790
+ const now = options.now ? new Date(options.now) : new Date;
68791
+ const kept = [];
68792
+ const removeIds = [];
68793
+ const result = {
68794
+ dryRun: options.dryRun !== false,
68795
+ removedDeleted: 0,
68796
+ removedSuperseded: 0,
68797
+ removedExpiredScratch: 0,
68798
+ remaining: 0
68799
+ };
68800
+ for (const memory of this.memories.values()) {
68801
+ const compactReason = shouldCompactMemory(memory, now);
68802
+ if (compactReason === "deleted") {
68803
+ result.removedDeleted++;
68804
+ removeIds.push(memory.id);
68805
+ continue;
68806
+ }
68807
+ if (compactReason === "superseded") {
68808
+ result.removedSuperseded++;
68809
+ removeIds.push(memory.id);
68810
+ continue;
68811
+ }
68812
+ if (compactReason === "expired_scratch") {
68813
+ result.removedExpiredScratch++;
68814
+ removeIds.push(memory.id);
68815
+ continue;
68816
+ }
68817
+ kept.push(memory);
68818
+ }
68819
+ result.remaining = kept.length;
68820
+ if (result.dryRun)
68821
+ return result;
68822
+ const db = this.requireDb();
68823
+ const compact = db.transaction(() => {
68824
+ for (const id of removeIds) {
68825
+ db.run("DELETE FROM memory_items WHERE id = ?", [id]);
68826
+ this.deleteMemoryFts(id);
68827
+ }
68828
+ this.insertEvent("compact", "memory_items", "removed deleted, superseded, and expired scratch memories", JSON.stringify(result));
68829
+ });
68830
+ compact();
68831
+ this.memories = new Map(kept.map((memory) => [memory.id, memory]));
68832
+ return result;
68833
+ }
68503
68834
  hasMigration(name2) {
68504
68835
  const row = this.requireDb().query("SELECT version, name FROM schema_migrations WHERE name = ? LIMIT 1").get(name2);
68505
68836
  return Boolean(row);
@@ -68976,6 +69307,7 @@ var init_sqlite_provider = __esm(() => {
68976
69307
  init_curator_decision_helpers();
68977
69308
  init_errors6();
68978
69309
  init_jsonl_migration();
69310
+ init_maintenance();
68979
69311
  init_schema2();
68980
69312
  init_scoring();
68981
69313
  FTS_INDEX_COLUMNS = [
@@ -70391,6 +70723,7 @@ var init_memory = __esm(() => {
70391
70723
  init_injector();
70392
70724
  init_jsonl_migration();
70393
70725
  init_local_jsonl_provider();
70726
+ init_maintenance();
70394
70727
  init_prompt_block();
70395
70728
  init_recall_planner();
70396
70729
  init_redaction();
@@ -70409,6 +70742,10 @@ async function handleMemoryCommand(_directory, _args) {
70409
70742
  "## Swarm Memory",
70410
70743
  "",
70411
70744
  "- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
70745
+ "- `/swarm memory pending` - show pending proposals and recent rejection reasons",
70746
+ "- `/swarm memory recall-log` - summarize recall usage by agent role and memory ID",
70747
+ "- `/swarm memory stale` - list expired scratch, superseded, deleted, and low-utility memories",
70748
+ "- `/swarm memory compact` - dry-run compaction; pass `--confirm` to remove deleted, superseded, and expired scratch records",
70412
70749
  "- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
70413
70750
  "- `/swarm memory import` - import `.swarm/memory/{memories,proposals}.jsonl` into SQLite",
70414
70751
  "- `/swarm memory migrate` - run the one-time legacy JSONL to SQLite migration",
@@ -70430,6 +70767,7 @@ async function handleMemoryStatusCommand(directory, _args) {
70430
70767
  `- Storage: \`${storageDir}\``,
70431
70768
  `- SQLite path: \`${sqlitePath}\``,
70432
70769
  `- SQLite database exists: \`${existsSync25(sqlitePath)}\``,
70770
+ `- Automatic destructive cleanup: \`disabled\``,
70433
70771
  "",
70434
70772
  "### Legacy JSONL"
70435
70773
  ];
@@ -70454,6 +70792,119 @@ async function handleMemoryStatusCommand(directory, _args) {
70454
70792
  return lines.join(`
70455
70793
  `);
70456
70794
  }
70795
+ async function handleMemoryPendingCommand(directory, args2) {
70796
+ const parsed = parseMaintenanceArgs(args2, {
70797
+ usage: "Usage: /swarm memory pending [--limit <n>]",
70798
+ allowConfirm: false
70799
+ });
70800
+ if ("error" in parsed)
70801
+ return parsed.error;
70802
+ const config3 = resolveCommandMemoryConfig(directory);
70803
+ const provider = createMaintenanceProvider(directory, config3);
70804
+ try {
70805
+ await provider.initialize?.();
70806
+ const report = await buildMemoryMaintenanceReport(provider, {
70807
+ ...maintenanceReportOptions(config3, parsed.limit)
70808
+ });
70809
+ const lines = [
70810
+ "## Swarm Memory Pending",
70811
+ "",
70812
+ `- Pending proposals shown: \`${report.pendingProposals.length}\``,
70813
+ `- Rejected proposal reasons shown: \`${report.rejectedProposalReasons.length}\``
70814
+ ];
70815
+ appendProposalLines(lines, "Pending proposals", report.pendingProposals);
70816
+ appendProposalLines(lines, "Rejected proposal reasons", report.rejectedProposalReasons);
70817
+ return lines.join(`
70818
+ `);
70819
+ } finally {
70820
+ await provider.close?.();
70821
+ }
70822
+ }
70823
+ async function handleMemoryRecallLogCommand(directory, args2) {
70824
+ const parsed = parseMaintenanceArgs(args2, {
70825
+ usage: "Usage: /swarm memory recall-log [--limit <n>]",
70826
+ allowConfirm: false
70827
+ });
70828
+ if ("error" in parsed)
70829
+ return parsed.error;
70830
+ const config3 = resolveCommandMemoryConfig(directory);
70831
+ const provider = createMaintenanceProvider(directory, config3);
70832
+ try {
70833
+ await provider.initialize?.();
70834
+ const report = await buildMemoryMaintenanceReport(provider, {
70835
+ ...maintenanceReportOptions(config3, parsed.limit)
70836
+ });
70837
+ const lines = [
70838
+ "## Swarm Memory Recall Log",
70839
+ "",
70840
+ `- Recall events scanned: \`${report.recallEventCount}\``,
70841
+ `- Most-recalled memories shown: \`${report.mostRecalledMemories.length}\``,
70842
+ `- Never-recalled memories shown: \`${report.neverRecalledMemories.length}\``
70843
+ ];
70844
+ appendRecallRoleLines(lines, report.recallByAgentRole);
70845
+ appendRecallMemoryLines(lines, report.mostRecalledMemories);
70846
+ appendMemoryLines(lines, "Never-recalled memories", report.neverRecalledMemories);
70847
+ return lines.join(`
70848
+ `);
70849
+ } finally {
70850
+ await provider.close?.();
70851
+ }
70852
+ }
70853
+ async function handleMemoryStaleCommand(directory, args2) {
70854
+ const parsed = parseMaintenanceArgs(args2, {
70855
+ usage: "Usage: /swarm memory stale [--limit <n>]",
70856
+ allowConfirm: false
70857
+ });
70858
+ if ("error" in parsed)
70859
+ return parsed.error;
70860
+ const config3 = resolveCommandMemoryConfig(directory);
70861
+ const provider = createMaintenanceProvider(directory, config3);
70862
+ try {
70863
+ await provider.initialize?.();
70864
+ const report = await buildMemoryMaintenanceReport(provider, {
70865
+ ...maintenanceReportOptions(config3, parsed.limit)
70866
+ });
70867
+ const lines = [
70868
+ "## Swarm Memory Stale",
70869
+ "",
70870
+ `- Active memories: \`${report.activeMemories}\``,
70871
+ `- Expired scratch memories shown: \`${report.expiredScratchMemories.length}\``,
70872
+ `- Deleted tombstones shown: \`${report.deletedMemories.length}\``,
70873
+ `- Superseded memories shown: \`${report.supersededMemories.length}\``,
70874
+ `- Low-utility memories shown: \`${report.lowUtilityMemories.length}\``
70875
+ ];
70876
+ appendMemoryLines(lines, "Expired scratch memories", report.expiredScratchMemories);
70877
+ appendMemoryLines(lines, "Deleted tombstones", report.deletedMemories);
70878
+ appendSupersededChains(lines, report);
70879
+ appendMemoryLines(lines, "Low-utility memories", report.lowUtilityMemories);
70880
+ return lines.join(`
70881
+ `);
70882
+ } finally {
70883
+ await provider.close?.();
70884
+ }
70885
+ }
70886
+ async function handleMemoryCompactCommand(directory, args2) {
70887
+ const parsed = parseMaintenanceArgs(args2, {
70888
+ usage: "Usage: /swarm memory compact [--confirm]",
70889
+ allowConfirm: true,
70890
+ allowLimit: false
70891
+ });
70892
+ if ("error" in parsed)
70893
+ return parsed.error;
70894
+ const provider = createMaintenanceProvider(directory, resolveCommandMemoryConfig(directory));
70895
+ try {
70896
+ await provider.initialize?.();
70897
+ if (!provider.compactMaintenance) {
70898
+ return "Memory provider does not support compaction.";
70899
+ }
70900
+ const result = await provider.compactMaintenance({
70901
+ dryRun: !parsed.confirm
70902
+ });
70903
+ return formatCompactResult(result, parsed.confirm);
70904
+ } finally {
70905
+ await provider.close?.();
70906
+ }
70907
+ }
70457
70908
  async function handleMemoryMigrateCommand(directory, _args) {
70458
70909
  const config3 = {
70459
70910
  ...resolveCommandMemoryConfig(directory),
@@ -70510,6 +70961,16 @@ async function handleMemoryExportCommand(directory, _args) {
70510
70961
  await provider.close?.();
70511
70962
  }
70512
70963
  }
70964
+ function createMaintenanceProvider(directory, config3) {
70965
+ return createConfiguredMemoryProvider(directory, config3);
70966
+ }
70967
+ function maintenanceReportOptions(config3, limit) {
70968
+ return {
70969
+ limit,
70970
+ lowUtilityMaxConfidence: config3.maintenance.lowUtilityMaxConfidence,
70971
+ lowUtilityMinAgeDays: config3.maintenance.lowUtilityMinAgeDays
70972
+ };
70973
+ }
70513
70974
  async function handleMemoryEvaluateCommand(directory, args2) {
70514
70975
  const parsed = parseEvaluateArgs(directory, args2);
70515
70976
  if ("error" in parsed)
@@ -70568,6 +71029,28 @@ function parseEvaluateArgs(directory, args2) {
70568
71029
  }
70569
71030
  return { json: json3, fixtureDirectory };
70570
71031
  }
71032
+ function parseMaintenanceArgs(args2, options) {
71033
+ let limit = 20;
71034
+ let confirm = false;
71035
+ const allowLimit = options.allowLimit ?? true;
71036
+ for (let i2 = 0;i2 < args2.length; i2++) {
71037
+ const arg = args2[i2];
71038
+ if (arg === "--confirm" && options.allowConfirm) {
71039
+ confirm = true;
71040
+ continue;
71041
+ }
71042
+ if (arg === "--limit" && allowLimit) {
71043
+ const next = args2[i2 + 1];
71044
+ if (!next || !/^\d+$/.test(next))
71045
+ return { error: options.usage };
71046
+ limit = Math.min(100, Math.max(1, Number(next)));
71047
+ i2++;
71048
+ continue;
71049
+ }
71050
+ return { error: options.usage };
71051
+ }
71052
+ return { limit, confirm };
71053
+ }
70571
71054
  function resolvePackageRootFromModule(modulePath) {
70572
71055
  const moduleDir = path45.dirname(modulePath);
70573
71056
  const leaf = path45.basename(moduleDir);
@@ -70618,6 +71101,80 @@ function appendInvalidRows(lines, invalidRows) {
70618
71101
  lines.push(`- ... ${invalidRows.length - 20} more`);
70619
71102
  }
70620
71103
  }
71104
+ function appendProposalLines(lines, title, proposals) {
71105
+ lines.push("", `### ${title}`);
71106
+ if (proposals.length === 0) {
71107
+ lines.push("- none");
71108
+ return;
71109
+ }
71110
+ for (const proposal of proposals) {
71111
+ const reason = proposal.status === "rejected" ? ` - ${proposal.rejectionReason ?? "no reason recorded"}` : "";
71112
+ lines.push(`- \`${proposal.id}\` ${proposal.operation} ${proposal.targetMemoryId ?? proposal.proposedRecord?.id ?? "new"} (${proposal.status})${reason}`);
71113
+ }
71114
+ }
71115
+ function appendMemoryLines(lines, title, memories) {
71116
+ lines.push("", `### ${title}`);
71117
+ if (memories.length === 0) {
71118
+ lines.push("- none");
71119
+ return;
71120
+ }
71121
+ for (const memory of memories) {
71122
+ lines.push(`- \`${memory.id}\` ${memory.kind} confidence=${memory.confidence.toFixed(2)} updated=${memory.updatedAt} - ${truncate(memory.text, 100)}`);
71123
+ }
71124
+ }
71125
+ function appendRecallRoleLines(lines, roles) {
71126
+ lines.push("", "### Recall by agent role");
71127
+ if (roles.length === 0) {
71128
+ lines.push("- none");
71129
+ return;
71130
+ }
71131
+ for (const role of roles) {
71132
+ const memoryCount = Object.keys(role.memoryIds).length;
71133
+ lines.push(`- \`${role.agentRole}\`: ${role.count} recall event(s), ${memoryCount} memory ID(s)`);
71134
+ }
71135
+ }
71136
+ function appendRecallMemoryLines(lines, memories) {
71137
+ lines.push("", "### Most-recalled memories");
71138
+ if (memories.length === 0) {
71139
+ lines.push("- none");
71140
+ return;
71141
+ }
71142
+ for (const memory of memories) {
71143
+ lines.push(`- \`${memory.memoryId}\`: ${memory.count} hit(s), last=${memory.lastRecalledAt}, avgScore=${memory.averageScore.toFixed(3)}`);
71144
+ }
71145
+ }
71146
+ function appendSupersededChains(lines, report) {
71147
+ lines.push("", "### Superseded chains");
71148
+ if (report.supersededChains.length === 0) {
71149
+ lines.push("- none");
71150
+ return;
71151
+ }
71152
+ for (const chain of report.supersededChains) {
71153
+ const reason = chain.reason ? ` - ${chain.reason}` : "";
71154
+ lines.push(`- ${chain.chain.map((id) => `\`${id}\``).join(" -> ")}${reason}`);
71155
+ }
71156
+ }
71157
+ function formatCompactResult(result, confirmed) {
71158
+ const lines = [
71159
+ "## Swarm Memory Compact",
71160
+ "",
71161
+ `- Mode: \`${confirmed ? "confirmed" : "dry-run"}\``,
71162
+ `- Deleted tombstones: \`${result.removedDeleted}\``,
71163
+ `- Superseded records: \`${result.removedSuperseded}\``,
71164
+ `- Expired scratch records: \`${result.removedExpiredScratch}\``,
71165
+ `- Remaining memories: \`${result.remaining}\``
71166
+ ];
71167
+ if (!confirmed) {
71168
+ lines.push("", "No records were removed. Re-run `/swarm memory compact --confirm` to apply this compaction.");
71169
+ }
71170
+ return lines.join(`
71171
+ `);
71172
+ }
71173
+ function truncate(value, maxLength) {
71174
+ if (value.length <= maxLength)
71175
+ return value;
71176
+ return `${value.slice(0, maxLength - 3)}...`;
71177
+ }
70621
71178
  var PACKAGE_ROOT;
70622
71179
  var init_memory2 = __esm(() => {
70623
71180
  init_loader();
@@ -79002,7 +79559,7 @@ function classifySwarmCommandToolUse(resolved) {
79002
79559
  return { allowed: true };
79003
79560
  return {
79004
79561
  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."
79562
+ 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
79563
  };
79007
79564
  }
79008
79565
  if (canonicalKey === "memory evaluate") {
@@ -79015,6 +79572,17 @@ function classifySwarmCommandToolUse(resolved) {
79015
79572
  message: "Usage through swarm_command: `/swarm memory evaluate --json`. Custom fixture directories are only available through direct user command execution."
79016
79573
  };
79017
79574
  }
79575
+ if (canonicalKey === "memory pending" || canonicalKey === "memory recall-log" || canonicalKey === "memory stale") {
79576
+ if (args2.length === 0)
79577
+ return { allowed: true };
79578
+ if (args2.length === 2 && args2[0] === "--limit" && /^\d+$/.test(args2[1])) {
79579
+ return { allowed: true };
79580
+ }
79581
+ return {
79582
+ allowed: false,
79583
+ message: `Usage through swarm_command: \`/swarm ${canonicalKey}\` or ` + `\`/swarm ${canonicalKey} --limit <n>\`.`
79584
+ };
79585
+ }
79018
79586
  if (canonicalKey === "retrieve") {
79019
79587
  if (args2.length !== 1 || !SUMMARY_ID_PATTERN.test(args2[0])) {
79020
79588
  return {
@@ -79066,7 +79634,7 @@ function classifySwarmCommandChatFallbackUse(resolved) {
79066
79634
  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
79635
  };
79068
79636
  }
79069
- if (canonicalKey === "knowledge migrate" || canonicalKey === "knowledge quarantine" || canonicalKey === "knowledge restore" || canonicalKey === "memory import" || canonicalKey === "memory migrate") {
79637
+ if (canonicalKey === "knowledge migrate" || canonicalKey === "knowledge quarantine" || canonicalKey === "knowledge restore" || canonicalKey === "memory import" || canonicalKey === "memory migrate" || canonicalKey === "memory compact") {
79070
79638
  return {
79071
79639
  allowed: false,
79072
79640
  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 +79667,10 @@ var init_tool_policy = __esm(() => {
79099
79667
  "knowledge",
79100
79668
  "memory",
79101
79669
  "memory status",
79670
+ "memory pending",
79671
+ "memory recall-log",
79672
+ "memory compact",
79673
+ "memory stale",
79102
79674
  "memory export",
79103
79675
  "memory evaluate",
79104
79676
  "memory import",
@@ -79125,6 +79697,9 @@ var init_tool_policy = __esm(() => {
79125
79697
  "knowledge",
79126
79698
  "memory",
79127
79699
  "memory status",
79700
+ "memory pending",
79701
+ "memory recall-log",
79702
+ "memory stale",
79128
79703
  "memory export",
79129
79704
  "memory evaluate",
79130
79705
  "sync-plan",
@@ -79137,7 +79712,8 @@ var init_tool_policy = __esm(() => {
79137
79712
  "rollback",
79138
79713
  "checkpoint",
79139
79714
  "memory import",
79140
- "memory migrate"
79715
+ "memory migrate",
79716
+ "memory compact"
79141
79717
  ]);
79142
79718
  NO_ARGS = new Set([
79143
79719
  "agents",
@@ -80132,6 +80708,34 @@ Subcommands:
80132
80708
  args: "",
80133
80709
  category: "diagnostics"
80134
80710
  },
80711
+ "memory pending": {
80712
+ handler: (ctx) => handleMemoryPendingCommand(ctx.directory, ctx.args),
80713
+ description: "Show pending Swarm memory proposals and rejection reasons",
80714
+ subcommandOf: "memory",
80715
+ args: "--limit <n>",
80716
+ category: "diagnostics"
80717
+ },
80718
+ "memory recall-log": {
80719
+ handler: (ctx) => handleMemoryRecallLogCommand(ctx.directory, ctx.args),
80720
+ description: "Summarize Swarm memory recall usage",
80721
+ subcommandOf: "memory",
80722
+ args: "--limit <n>",
80723
+ category: "diagnostics"
80724
+ },
80725
+ "memory compact": {
80726
+ handler: (ctx) => handleMemoryCompactCommand(ctx.directory, ctx.args),
80727
+ description: "Compact deleted, superseded, and expired scratch memories",
80728
+ subcommandOf: "memory",
80729
+ args: "--confirm",
80730
+ category: "utility"
80731
+ },
80732
+ "memory stale": {
80733
+ handler: (ctx) => handleMemoryStaleCommand(ctx.directory, ctx.args),
80734
+ description: "List stale and low-utility Swarm memories",
80735
+ subcommandOf: "memory",
80736
+ args: "--limit <n>",
80737
+ category: "diagnostics"
80738
+ },
80135
80739
  "memory export": {
80136
80740
  handler: (ctx) => handleMemoryExportCommand(ctx.directory, ctx.args),
80137
80741
  description: "Export current Swarm memory to JSONL files",