opencode-swarm 7.103.3 → 7.105.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.
Files changed (45) hide show
  1. package/.opencode/skills/commit-pr/SKILL.md +15 -7
  2. package/.opencode/skills/swarm-pr-feedback/SKILL.md +7 -0
  3. package/.opencode/skills/swarm-pr-review/SKILL.md +6 -0
  4. package/.opencode/skills/swarm-pr-subscribe/SKILL.md +176 -0
  5. package/dist/background/pr-event-delivery.d.ts +107 -0
  6. package/dist/background/pr-event-subscribers.d.ts +36 -7
  7. package/dist/cli/{config-doctor-4rxjsg22.js → config-doctor-4dhkq5qe.js} +2 -2
  8. package/dist/cli/{guardrail-explain-7c857j83.js → guardrail-explain-9br76zh5.js} +5 -5
  9. package/dist/cli/{guardrail-log-vg0atkrx.js → guardrail-log-peftzya3.js} +3 -3
  10. package/dist/cli/{index-9zb70qfe.js → index-25bc7j87.js} +1 -1
  11. package/dist/cli/{index-m9keg83x.js → index-50qqqbrb.js} +1 -1
  12. package/dist/cli/{index-ga7et2xw.js → index-5qzchq7p.js} +28 -1
  13. package/dist/cli/{index-fhn62ce4.js → index-8mj10cds.js} +8 -6
  14. package/dist/cli/{index-h0h4qjmc.js → index-hxnnp4qj.js} +1 -1
  15. package/dist/cli/{index-ye6zt96v.js → index-qnapygnz.js} +670 -64
  16. package/dist/cli/{index-60prdx3j.js → index-wg8ars51.js} +2 -2
  17. package/dist/cli/index.js +4 -4
  18. package/dist/cli/{schema-az0hgfv0.js → schema-3a02876y.js} +1 -1
  19. package/dist/commands/index.d.ts +1 -1
  20. package/dist/commands/memory.d.ts +1 -0
  21. package/dist/commands/pr-subscribe.d.ts +5 -3
  22. package/dist/commands/registry.d.ts +12 -4
  23. package/dist/config/bundled-skills.d.ts +1 -1
  24. package/dist/config/schema.d.ts +40 -0
  25. package/dist/council/types.d.ts +9 -0
  26. package/dist/graph/import-extractor.d.ts +1 -1
  27. package/dist/hooks/pr-auto-subscribe.d.ts +51 -0
  28. package/dist/index.js +154 -106
  29. package/dist/lang/symbol-visibility.d.ts +2 -0
  30. package/dist/memory/config.d.ts +20 -0
  31. package/dist/memory/gateway.d.ts +1 -0
  32. package/dist/memory/index.d.ts +2 -1
  33. package/dist/memory/maintenance.d.ts +4 -1
  34. package/dist/memory/provider-pool.d.ts +23 -0
  35. package/dist/memory/provider.d.ts +53 -0
  36. package/dist/memory/reward.d.ts +39 -0
  37. package/dist/memory/schema.d.ts +2 -0
  38. package/dist/memory/scoring.d.ts +2 -0
  39. package/dist/memory/sqlite-provider.d.ts +53 -1
  40. package/dist/memory/types.d.ts +5 -0
  41. package/dist/tools/convene-council.d.ts +2 -0
  42. package/dist/tools/repo-graph/builder.d.ts +4 -2
  43. package/dist/tools/submit-phase-council-verdicts.d.ts +1 -0
  44. package/dist/tools/symbols.d.ts +2 -0
  45. package/package.json +2 -1
@@ -57,7 +57,7 @@ import {
57
57
  readDoctorArtifact,
58
58
  removeStraySwarmDir,
59
59
  runConfigDoctor
60
- } from "./index-h0h4qjmc.js";
60
+ } from "./index-hxnnp4qj.js";
61
61
  import {
62
62
  AGENT_TOOL_MAP,
63
63
  ALL_SUBAGENT_NAMES,
@@ -70,7 +70,7 @@ import {
70
70
  TOOL_NAME_SET,
71
71
  resolveExternalSkillsConfig,
72
72
  stripKnownSwarmPrefix
73
- } from "./index-ga7et2xw.js";
73
+ } from "./index-5qzchq7p.js";
74
74
  import {
75
75
  MAX_TRANSIENT_RETRIES,
76
76
  PlanSchema,
@@ -912,7 +912,7 @@ var init_executor = __esm(() => {
912
912
  // package.json
913
913
  var package_default = {
914
914
  name: "opencode-swarm",
915
- version: "7.103.3",
915
+ version: "7.105.0",
916
916
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
917
917
  main: "dist/index.js",
918
918
  types: "dist/index.d.ts",
@@ -967,6 +967,7 @@ var package_default = {
967
967
  ".opencode/skills/design-docs",
968
968
  ".opencode/skills/swarm-pr-review",
969
969
  ".opencode/skills/swarm-pr-feedback",
970
+ ".opencode/skills/swarm-pr-subscribe",
970
971
  ".opencode/skills/issue-ingest",
971
972
  ".opencode/skills/plan",
972
973
  ".opencode/skills/critic-gate",
@@ -1410,6 +1411,7 @@ var BUNDLED_PROJECT_SKILLS = [
1410
1411
  "design-docs",
1411
1412
  "swarm-pr-review",
1412
1413
  "swarm-pr-feedback",
1414
+ "swarm-pr-subscribe",
1413
1415
  "issue-ingest",
1414
1416
  "plan",
1415
1417
  "critic-gate",
@@ -3589,7 +3591,7 @@ function classifySwarmCommandToolUse(resolved) {
3589
3591
  return { allowed: true };
3590
3592
  return {
3591
3593
  allowed: false,
3592
- 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."
3594
+ message: "Use `/swarm memory status`, `/swarm memory pending`, `/swarm memory recall-log`, `/swarm memory value-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."
3593
3595
  };
3594
3596
  }
3595
3597
  if (canonicalKey === "memory evaluate") {
@@ -3625,7 +3627,7 @@ function classifySwarmCommandToolUse(resolved) {
3625
3627
  message: "Usage through swarm_command: `/swarm sdd validate`, `/swarm sdd validate --json`, or `/swarm sdd validate --change <id>`."
3626
3628
  };
3627
3629
  }
3628
- if (canonicalKey === "memory pending" || canonicalKey === "memory recall-log" || canonicalKey === "memory stale") {
3630
+ if (canonicalKey === "memory pending" || canonicalKey === "memory recall-log" || canonicalKey === "memory value-log" || canonicalKey === "memory stale") {
3629
3631
  if (args.length === 0)
3630
3632
  return { allowed: true };
3631
3633
  if (args.length === 2 && args[0] === "--limit" && /^\d+$/.test(args[1])) {
@@ -13377,7 +13379,7 @@ async function runFinalizeStage(ctx) {
13377
13379
  }
13378
13380
  }
13379
13381
  try {
13380
- const { CuratorConfigSchema: CCS } = await import("./schema-az0hgfv0.js");
13382
+ const { CuratorConfigSchema: CCS } = await import("./schema-3a02876y.js");
13381
13383
  const { config: pmLoadedConfig } = _internals20.loadPluginConfigWithMeta(ctx.directory);
13382
13384
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
13383
13385
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -17203,7 +17205,7 @@ async function handleDoctorCommand(directory, args) {
17203
17205
  const result = runConfigDoctor(config, directory);
17204
17206
  let output;
17205
17207
  if (enableAutoFix && result.hasAutoFixableIssues) {
17206
- const { runConfigDoctorWithFixes } = await import("./config-doctor-4rxjsg22.js");
17208
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-4dhkq5qe.js");
17207
17209
  const fixResult = await runConfigDoctorWithFixes(directory, config, true);
17208
17210
  output = formatDoctorMarkdown(fixResult.result);
17209
17211
  } else {
@@ -20605,6 +20607,17 @@ var DEFAULT_CONSOLIDATION_CONFIG = {
20605
20607
  autoApplyMinConfidence: 0.6,
20606
20608
  decayHalfLifeDays: { ...DEFAULT_DECAY_HALF_LIFE_DAYS }
20607
20609
  };
20610
+ var DEFAULT_MEMORY_LEARNING_CONFIG = {
20611
+ learningRate: 0.1,
20612
+ propagationFactor: 0.3,
20613
+ qValueBoostWeight: 0.1,
20614
+ suppressionThreshold: 0.15,
20615
+ promotionThreshold: 0.85,
20616
+ propagationTokenOverlapThreshold: 0.4,
20617
+ propagationEmbeddingCosineThreshold: 0.7,
20618
+ propagationFanout: 20,
20619
+ propagationLookbackDays: 30
20620
+ };
20608
20621
  var DEFAULT_EMBEDDINGS_CONFIG = {
20609
20622
  enabled: false,
20610
20623
  model: "Xenova/all-MiniLM-L6-v2",
@@ -20643,6 +20656,7 @@ var DEFAULT_MEMORY_CONFIG = {
20643
20656
  tokenBudget: 1000
20644
20657
  }
20645
20658
  },
20659
+ learning: { ...DEFAULT_MEMORY_LEARNING_CONFIG },
20646
20660
  writes: {
20647
20661
  mode: "propose"
20648
20662
  },
@@ -20694,6 +20708,10 @@ function resolveMemoryConfig(input) {
20694
20708
  ...input?.recall?.injection ?? {}
20695
20709
  }
20696
20710
  },
20711
+ learning: {
20712
+ ...DEFAULT_MEMORY_CONFIG.learning,
20713
+ ...input?.learning ?? {}
20714
+ },
20697
20715
  writes: {
20698
20716
  ...DEFAULT_MEMORY_CONFIG.writes,
20699
20717
  ...input?.writes ?? {}
@@ -20887,6 +20905,7 @@ var MemoryRecordSchema = exports_external.object({
20887
20905
  updatedAt: exports_external.string().datetime(),
20888
20906
  lastAccessedAt: exports_external.string().datetime().optional(),
20889
20907
  expiresAt: exports_external.string().datetime().optional(),
20908
+ qValue: exports_external.number().min(0).max(1).optional(),
20890
20909
  supersedes: exports_external.array(exports_external.string()).optional(),
20891
20910
  supersededBy: exports_external.string().optional(),
20892
20911
  contentHash: exports_external.string().regex(/^[a-f0-9]{64}$/),
@@ -21242,6 +21261,7 @@ var SCORING_WEIGHTS = {
21242
21261
  roleBoost: 0.05,
21243
21262
  confidence: 0.08
21244
21263
  };
21264
+ var DEFAULT_MEMORY_Q_VALUE = 0.5;
21245
21265
  function tokenize(text) {
21246
21266
  return new Set(text.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).map((token) => token.trim()).filter(Boolean));
21247
21267
  }
@@ -21320,6 +21340,11 @@ function scoreMemoryRecordDetailed(record, request, context) {
21320
21340
  if (record.metadata.deleted === true) {
21321
21341
  return { item: null, skipReason: "filtered" };
21322
21342
  }
21343
+ const qValue = memoryQValue(record);
21344
+ const suppressionThreshold = request.suppressionThreshold ?? 0;
21345
+ if (request.includeLowQ !== true && suppressionThreshold > 0 && qValue < suppressionThreshold) {
21346
+ return { item: null, skipReason: "filtered" };
21347
+ }
21323
21348
  if (!scopeAllowed(record.scope, request.scopes)) {
21324
21349
  return { item: null, skipReason: "filtered" };
21325
21350
  }
@@ -21355,7 +21380,7 @@ function scoreMemoryRecordDetailed(record, request, context) {
21355
21380
  if (request.mode === "injection" && request.requireQuerySignal !== false && !hasQuerySignal) {
21356
21381
  return { item: null, skipReason: "no_signal" };
21357
21382
  }
21358
- const score = textOverlap * SCORING_WEIGHTS.textOverlap + tagOverlap * SCORING_WEIGHTS.tagOverlap + fileOverlap * SCORING_WEIGHTS.fileOverlap + symbolOverlap * SCORING_WEIGHTS.symbolOverlap + taskTermOverlap * SCORING_WEIGHTS.taskTermOverlap + scopeSpecificityBoost(record.scope) * SCORING_WEIGHTS.scopeSpecificityBoost + kindProfileBoost(record.kind, request) * SCORING_WEIGHTS.kindProfileBoost + roleBoost * SCORING_WEIGHTS.roleBoost + record.confidence * SCORING_WEIGHTS.confidence;
21383
+ const score = textOverlap * SCORING_WEIGHTS.textOverlap + tagOverlap * SCORING_WEIGHTS.tagOverlap + fileOverlap * SCORING_WEIGHTS.fileOverlap + symbolOverlap * SCORING_WEIGHTS.symbolOverlap + taskTermOverlap * SCORING_WEIGHTS.taskTermOverlap + scopeSpecificityBoost(record.scope) * SCORING_WEIGHTS.scopeSpecificityBoost + kindProfileBoost(record.kind, request) * SCORING_WEIGHTS.kindProfileBoost + roleBoost * SCORING_WEIGHTS.roleBoost + record.confidence * SCORING_WEIGHTS.confidence + (qValue - DEFAULT_MEMORY_Q_VALUE) * (request.qValueBoostWeight ?? 0);
21359
21384
  const reasonParts = [
21360
21385
  textOverlap > 0 ? `text_overlap=${textOverlap.toFixed(2)}` : null,
21361
21386
  tagOverlap > 0 ? `tag_overlap=${tagOverlap.toFixed(2)}` : null,
@@ -21364,6 +21389,7 @@ function scoreMemoryRecordDetailed(record, request, context) {
21364
21389
  taskTermOverlap > 0 ? `task_terms=${taskTermOverlap.toFixed(2)}` : null,
21365
21390
  kindQueryOverlap > 0 ? `kind_query=${kindQueryOverlap.toFixed(2)}` : null,
21366
21391
  roleBoost > 0 ? "role_profile" : null,
21392
+ request.qValueBoostWeight && request.qValueBoostWeight > 0 ? `q_value=${qValue.toFixed(2)}` : null,
21367
21393
  `scope=${record.scope.type}`,
21368
21394
  `confidence=${record.confidence.toFixed(2)}`
21369
21395
  ].filter(Boolean);
@@ -21377,12 +21403,26 @@ function scoreMemoryRecordDetailed(record, request, context) {
21377
21403
  tagOverlap,
21378
21404
  fileOverlap,
21379
21405
  symbolOverlap,
21406
+ qValue,
21380
21407
  kindMatch,
21381
21408
  scopeMatch
21382
21409
  }
21383
21410
  }
21384
21411
  };
21385
21412
  }
21413
+ function memoryQValue(record) {
21414
+ if (typeof record.qValue === "number" && Number.isFinite(record.qValue)) {
21415
+ return clamp01(record.qValue);
21416
+ }
21417
+ const metadataValue = record.metadata.qValue;
21418
+ if (typeof metadataValue === "number" && Number.isFinite(metadataValue)) {
21419
+ return clamp01(metadataValue);
21420
+ }
21421
+ return DEFAULT_MEMORY_Q_VALUE;
21422
+ }
21423
+ function clamp01(value) {
21424
+ return Math.min(1, Math.max(0, value));
21425
+ }
21386
21426
  function scoreMemoryRecordsWithDiagnostics(records, request) {
21387
21427
  const minScore = request.minScore ?? 0;
21388
21428
  const context = createScoringContext(request);
@@ -21453,6 +21493,14 @@ async function buildMemoryMaintenanceReport(provider, options = {}) {
21453
21493
  });
21454
21494
  const proposals = await loadMaintenanceProposals(provider, limit);
21455
21495
  const recallUsage = provider.listRecallUsage ? await provider.listRecallUsage() : [];
21496
+ const suppressionValueLog = provider.listMemoryValueLog ? await provider.listMemoryValueLog({
21497
+ includeSuppressionCandidatesOnly: true,
21498
+ limit
21499
+ }) : [];
21500
+ const promotionValueLog = provider.listMemoryValueLog ? await provider.listMemoryValueLog({
21501
+ includePromotionCandidatesOnly: true,
21502
+ limit
21503
+ }) : [];
21456
21504
  const usageByMemory = summarizeRecallByMemory(recallUsage);
21457
21505
  const usageByRole = summarizeRecallByRole(recallUsage);
21458
21506
  const activeMemories = memories.filter((memory) => isActiveMemory(memory, now));
@@ -21475,6 +21523,8 @@ async function buildMemoryMaintenanceReport(provider, options = {}) {
21475
21523
  supersededMemories: supersededMemories.slice(0, limit),
21476
21524
  supersededChains: buildSupersededChains(memories).slice(0, limit),
21477
21525
  lowUtilityMemories: lowUtilityMemories.slice(0, limit),
21526
+ lowQValueMemories: suppressionValueLog.slice(0, limit),
21527
+ promotionCandidates: promotionValueLog.slice(0, limit),
21478
21528
  neverRecalledMemories: neverRecalledMemories.slice(0, limit),
21479
21529
  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),
21480
21530
  recallByAgentRole: Array.from(usageByRole.values()).sort((a, b) => b.count - a.count || a.agentRole.localeCompare(b.agentRole)).slice(0, limit),
@@ -22740,6 +22790,23 @@ var MIGRATIONS2 = [
22740
22790
  value TEXT
22741
22791
  );
22742
22792
  `
22793
+ },
22794
+ {
22795
+ version: 7,
22796
+ name: "add_recall_learning_columns",
22797
+ sql: `
22798
+ ALTER TABLE memory_recall_usage ADD COLUMN q_value REAL DEFAULT 0.5;
22799
+ ALTER TABLE memory_recall_usage ADD COLUMN last_reward REAL;
22800
+ ALTER TABLE memory_recall_usage ADD COLUMN task_outcome TEXT;
22801
+ ALTER TABLE memory_recall_usage ADD COLUMN council_verdict_json TEXT;
22802
+ `
22803
+ },
22804
+ {
22805
+ version: 8,
22806
+ name: "add_recall_reward_idempotency_key",
22807
+ sql: `
22808
+ ALTER TABLE memory_recall_usage ADD COLUMN reward_key TEXT;
22809
+ `
22743
22810
  }
22744
22811
  ];
22745
22812
 
@@ -22760,36 +22827,15 @@ class SQLiteMemoryProvider {
22760
22827
  lastAutomaticJsonlMigration = null;
22761
22828
  recallCountSinceLastCompaction = 0;
22762
22829
  isCompacting = false;
22830
+ _internals = {
22831
+ writeMemory: (record) => this.writeMemory(record),
22832
+ setEmbeddingProvider: (provider) => {
22833
+ this.embeddingProvider = provider;
22834
+ }
22835
+ };
22763
22836
  constructor(rootDirectory, config = {}) {
22764
22837
  this.rootDirectory = rootDirectory;
22765
- this.config = {
22766
- ...DEFAULT_MEMORY_CONFIG,
22767
- ...config,
22768
- sqlite: {
22769
- ...DEFAULT_MEMORY_CONFIG.sqlite,
22770
- ...config.sqlite ?? {}
22771
- },
22772
- recall: {
22773
- ...DEFAULT_MEMORY_CONFIG.recall,
22774
- ...config.recall ?? {},
22775
- injection: {
22776
- ...DEFAULT_MEMORY_CONFIG.recall.injection,
22777
- ...config.recall?.injection ?? {}
22778
- }
22779
- },
22780
- writes: {
22781
- ...DEFAULT_MEMORY_CONFIG.writes,
22782
- ...config.writes ?? {}
22783
- },
22784
- redaction: {
22785
- ...DEFAULT_MEMORY_CONFIG.redaction,
22786
- ...config.redaction ?? {}
22787
- },
22788
- maintenance: {
22789
- ...DEFAULT_MEMORY_CONFIG.maintenance,
22790
- ...config.maintenance ?? {}
22791
- }
22792
- };
22838
+ this.config = resolveMemoryConfig(config);
22793
22839
  }
22794
22840
  databasePath() {
22795
22841
  const relativePath = this.config.sqlite.path.replace(/^\.swarm[/\\]?/, "");
@@ -23040,12 +23086,25 @@ class SQLiteMemoryProvider {
23040
23086
  }
23041
23087
  async recordRecallUsage(event) {
23042
23088
  await this.initialize();
23089
+ const recalledRecords = event.memoryIds.map((id) => this.memories.get(id)).filter(isMemoryRecord);
23090
+ const qValue = typeof event.qValue === "number" && Number.isFinite(event.qValue) ? clamp012(event.qValue) : averageMemoryQValue(recalledRecords);
23091
+ const eventWithQValue = {
23092
+ ...event,
23093
+ qValue
23094
+ };
23043
23095
  this.requireDb().run(`INSERT INTO memory_recall_usage (
23044
23096
  id,
23045
23097
  bundle_id,
23046
23098
  timestamp,
23047
- usage_json
23048
- ) VALUES (?, ?, ?, ?)`, [randomUUID6(), event.bundleId, event.timestamp, JSON.stringify(event)]);
23099
+ usage_json,
23100
+ q_value
23101
+ ) VALUES (?, ?, ?, ?, ?)`, [
23102
+ randomUUID6(),
23103
+ event.bundleId,
23104
+ event.timestamp,
23105
+ JSON.stringify(eventWithQValue),
23106
+ qValue
23107
+ ]);
23049
23108
  this.recallCountSinceLastCompaction++;
23050
23109
  const threshold = this.config.maintenance?.autoCompactEveryNRecalls ?? 50;
23051
23110
  if (threshold > 0 && this.recallCountSinceLastCompaction >= threshold && !this.isCompacting) {
@@ -23072,24 +23131,163 @@ class SQLiteMemoryProvider {
23072
23131
  }
23073
23132
  async listRecallUsage(filter = {}) {
23074
23133
  await this.initialize();
23075
- const rows = typeof filter.limit === "number" ? this.requireDb().query(`SELECT usage_json
23134
+ const rows = typeof filter.limit === "number" ? this.requireDb().query(`SELECT id, usage_json, q_value, last_reward, task_outcome, council_verdict_json, reward_key
23076
23135
  FROM memory_recall_usage
23077
23136
  ORDER BY timestamp DESC
23078
- LIMIT ?`).all(Math.max(1, Math.trunc(filter.limit))) : this.requireDb().query(`SELECT usage_json
23137
+ LIMIT ?`).all(Math.max(1, Math.trunc(filter.limit))) : this.requireDb().query(`SELECT id, usage_json, q_value, last_reward, task_outcome, council_verdict_json, reward_key
23079
23138
  FROM memory_recall_usage
23080
23139
  ORDER BY timestamp DESC
23081
23140
  `).all();
23082
23141
  const events = [];
23083
23142
  for (const row of rows) {
23084
- try {
23085
- const parsed = JSON.parse(row.usage_json);
23086
- if (Array.isArray(parsed.memoryIds) && typeof parsed.query === "string") {
23087
- events.push(parsed);
23088
- }
23089
- } catch {}
23143
+ const parsed = parseRecallUsageRow(row);
23144
+ if (parsed)
23145
+ events.push(parsed);
23090
23146
  }
23091
23147
  return events;
23092
23148
  }
23149
+ async applyRecallReward(input) {
23150
+ await this.initialize();
23151
+ const matches = this.matchingRecallUsageRows(input.runIds);
23152
+ if (matches.length === 0) {
23153
+ return emptyRewardResult(input.outcome, "no_recall_usage_for_run");
23154
+ }
23155
+ const rewardKey = deriveRewardKey(input.verdictPayload);
23156
+ const pending = rewardKey === null ? matches : matches.filter((match) => match.rewardKey !== rewardKey);
23157
+ if (pending.length === 0) {
23158
+ const first = matches[0];
23159
+ return {
23160
+ success: true,
23161
+ bundleId: first?.event.bundleId,
23162
+ bundleIds: matches.map((match) => match.event.bundleId),
23163
+ outcome: input.outcome,
23164
+ memoryIds: uniqueStrings(matches.flatMap((match) => match.event.memoryIds)),
23165
+ reward: rewardForOutcome(input.outcome),
23166
+ updatedMemoryIds: [],
23167
+ propagatedMemoryIds: [],
23168
+ qValue: first?.event.qValue,
23169
+ reason: "already_rewarded"
23170
+ };
23171
+ }
23172
+ const reward = rewardForOutcome(input.outcome);
23173
+ const updatedAt = input.timestamp ?? new Date().toISOString();
23174
+ const sourceIds = uniqueStrings(pending.flatMap((match) => match.event.memoryIds));
23175
+ const directUpdates = new Map;
23176
+ const propagatedUpdates = new Map;
23177
+ for (const memoryId of sourceIds) {
23178
+ const nextQValue = this.updateMemoryQValue(memoryId, reward, updatedAt);
23179
+ if (nextQValue !== null) {
23180
+ directUpdates.set(memoryId, nextQValue);
23181
+ }
23182
+ }
23183
+ const promotionThreshold = this.config.learning.promotionThreshold;
23184
+ const promotionRecallThreshold = 5;
23185
+ const usageSummary = summarizeRecallUsage(this.listRecallUsageSync());
23186
+ for (const [memoryId, nextQValue] of directUpdates) {
23187
+ const memory = this.memories.get(memoryId);
23188
+ const recallCount = usageSummary.get(memoryId)?.count ?? 0;
23189
+ if (memory && memory.stability === "session" && nextQValue > promotionThreshold && recallCount > promotionRecallThreshold) {
23190
+ memory.stability = "durable";
23191
+ this._internals.writeMemory(memory);
23192
+ }
23193
+ }
23194
+ const propagationTargets = await this.findPropagationTargets(sourceIds);
23195
+ const db = this.requireDb();
23196
+ const verdictJson = truncateJsonPayload(input.verdictPayload);
23197
+ const result = db.transaction(() => {
23198
+ for (const targetId of propagationTargets) {
23199
+ const propagatedSignal = propagatedRewardSignal(reward, this.config.learning.propagationFactor);
23200
+ const nextQValue = this.updateMemoryQValue(targetId, propagatedSignal, updatedAt);
23201
+ if (nextQValue !== null) {
23202
+ propagatedUpdates.set(targetId, nextQValue);
23203
+ }
23204
+ }
23205
+ const updatedMemoryIds = [...directUpdates.keys()];
23206
+ const propagatedMemoryIds = [...propagatedUpdates.keys()];
23207
+ const combinedQValues = [
23208
+ ...directUpdates.values(),
23209
+ ...propagatedUpdates.values()
23210
+ ];
23211
+ const qValue = combinedQValues.length > 0 ? averageNumbers(combinedQValues) : pending[0]?.event.qValue;
23212
+ for (const match of pending) {
23213
+ const updatedEvent = {
23214
+ ...match.event,
23215
+ qValue,
23216
+ lastReward: reward,
23217
+ taskOutcome: input.outcome
23218
+ };
23219
+ db.run(`UPDATE memory_recall_usage
23220
+ SET usage_json = ?,
23221
+ q_value = ?,
23222
+ last_reward = ?,
23223
+ task_outcome = ?,
23224
+ council_verdict_json = ?,
23225
+ reward_key = ?
23226
+ WHERE id = ?`, [
23227
+ JSON.stringify(updatedEvent),
23228
+ qValue ?? null,
23229
+ reward,
23230
+ input.outcome,
23231
+ verdictJson,
23232
+ rewardKey,
23233
+ match.id
23234
+ ]);
23235
+ }
23236
+ return {
23237
+ updatedMemoryIds,
23238
+ propagatedMemoryIds,
23239
+ qValue
23240
+ };
23241
+ })();
23242
+ for (const match of pending) {
23243
+ await this.event("recall", match.event.bundleId, `applied ${input.outcome} recall reward`);
23244
+ }
23245
+ return {
23246
+ success: true,
23247
+ bundleId: pending[0]?.event.bundleId,
23248
+ bundleIds: pending.map((match) => match.event.bundleId),
23249
+ outcome: input.outcome,
23250
+ memoryIds: sourceIds,
23251
+ reward,
23252
+ updatedMemoryIds: result.updatedMemoryIds,
23253
+ propagatedMemoryIds: result.propagatedMemoryIds,
23254
+ qValue: result.qValue
23255
+ };
23256
+ }
23257
+ async listMemoryValueLog(filter = {}) {
23258
+ await this.initialize();
23259
+ const usageSummary = summarizeRecallUsage(this.listRecallUsageSync());
23260
+ const threshold = this.config.learning.suppressionThreshold;
23261
+ const promotionThreshold = this.config.learning.promotionThreshold;
23262
+ const entries = Array.from(this.memories.values()).map((record) => {
23263
+ const recall = usageSummary.get(record.id);
23264
+ const qValue = memoryQValue(record);
23265
+ const recallCount = recall?.count ?? 0;
23266
+ return {
23267
+ memoryId: record.id,
23268
+ kind: record.kind,
23269
+ scopeKey: stableScopeKey(record.scope),
23270
+ textPreview: truncateText(record.text, 120),
23271
+ qValue,
23272
+ recallCount,
23273
+ lastRecalledAt: recall?.lastRecalledAt,
23274
+ lastReward: recall?.lastReward,
23275
+ taskOutcome: recall?.taskOutcome,
23276
+ promotionCandidate: qValue > promotionThreshold && recallCount > 5,
23277
+ suppressionCandidate: qValue < threshold
23278
+ };
23279
+ });
23280
+ const filtered = entries.filter((entry) => {
23281
+ if (filter.includePromotionCandidatesOnly) {
23282
+ return entry.promotionCandidate;
23283
+ }
23284
+ if (filter.includeSuppressionCandidatesOnly) {
23285
+ return entry.suppressionCandidate;
23286
+ }
23287
+ return true;
23288
+ }).sort((a, b) => (b.lastRecalledAt ?? "").localeCompare(a.lastRecalledAt ?? "") || b.qValue - a.qValue || a.memoryId.localeCompare(b.memoryId));
23289
+ return filtered.slice(0, Math.max(1, Math.trunc(filter.limit ?? 20)));
23290
+ }
23093
23291
  async list(filter = {}) {
23094
23292
  await this.initialize();
23095
23293
  const db = this.requireDb();
@@ -23314,6 +23512,145 @@ class SQLiteMemoryProvider {
23314
23512
  this.memories = new Map(kept.map((memory) => [memory.id, memory]));
23315
23513
  return result;
23316
23514
  }
23515
+ matchingRecallUsageRows(runIds) {
23516
+ const idSet = new Set(runIds.filter((id) => id.length > 0));
23517
+ if (idSet.size === 0)
23518
+ return [];
23519
+ const rows = this.requireDb().query(`SELECT id, usage_json, q_value, last_reward, task_outcome, council_verdict_json, reward_key
23520
+ FROM memory_recall_usage
23521
+ ORDER BY timestamp DESC
23522
+ LIMIT ?`).all(100);
23523
+ const matches = [];
23524
+ const seenRunIds = new Set;
23525
+ for (const row of rows) {
23526
+ if (!row.id)
23527
+ continue;
23528
+ const event = parseRecallUsageRow(row);
23529
+ if (!event?.runId || !idSet.has(event.runId))
23530
+ continue;
23531
+ if (seenRunIds.has(event.runId))
23532
+ continue;
23533
+ seenRunIds.add(event.runId);
23534
+ matches.push({ id: row.id, event, rewardKey: row.reward_key ?? null });
23535
+ }
23536
+ return matches;
23537
+ }
23538
+ updateMemoryQValue(memoryId, rewardSignal, updatedAt) {
23539
+ const current = this.memories.get(memoryId);
23540
+ if (!current)
23541
+ return null;
23542
+ if (current.metadata.deleted === true || current.supersededBy)
23543
+ return null;
23544
+ const eta = this.config.learning.learningRate;
23545
+ const nextQValue = clamp012((1 - eta) * memoryQValue(current) + eta * rewardSignal);
23546
+ const next = {
23547
+ ...current,
23548
+ updatedAt,
23549
+ qValue: nextQValue
23550
+ };
23551
+ this.memories.set(memoryId, next);
23552
+ this.writeMemory(next);
23553
+ return nextQValue;
23554
+ }
23555
+ async findPropagationTargets(sourceMemoryIds) {
23556
+ const lookbackIds = this.recentlyRecalledMemoryIds(this.config.learning.propagationLookbackDays);
23557
+ if (lookbackIds.size === 0)
23558
+ return [];
23559
+ const sourceRecords = sourceMemoryIds.map((id) => this.memories.get(id)).filter(isMemoryRecord);
23560
+ if (sourceRecords.length === 0)
23561
+ return [];
23562
+ const targets = [];
23563
+ const sourceTokenSets = sourceRecords.map((record) => tokenizeText(record.text));
23564
+ const cosineEnabled = this.config.embeddings.enabled && Boolean(this.embeddingProvider);
23565
+ const cosineThreshold = this.config.learning.propagationEmbeddingCosineThreshold;
23566
+ const sourceVectors = cosineEnabled ? await Promise.all(sourceRecords.map((record) => this.getCachedEmbeddingForPropagation(record.text))) : [];
23567
+ for (const candidate of this.memories.values()) {
23568
+ if (sourceMemoryIds.includes(candidate.id))
23569
+ continue;
23570
+ if (!lookbackIds.has(candidate.id))
23571
+ continue;
23572
+ if (candidate.metadata.deleted === true || candidate.supersededBy)
23573
+ continue;
23574
+ const candidateTokens = tokenizeText(candidate.text);
23575
+ let bestOverlap = 0;
23576
+ let sameScopeAsAnySource = false;
23577
+ const sameScopeBySourceIndex = [];
23578
+ for (let i = 0;i < sourceRecords.length; i++) {
23579
+ const source = sourceRecords[i];
23580
+ const sameScope2 = stableScopeKey(candidate.scope) === stableScopeKey(source.scope);
23581
+ sameScopeBySourceIndex[i] = sameScope2;
23582
+ if (sameScope2)
23583
+ sameScopeAsAnySource = true;
23584
+ if (!sameScope2 || candidate.kind !== source.kind)
23585
+ continue;
23586
+ bestOverlap = Math.max(bestOverlap, jaccard(sourceTokenSets[i] ?? new Set, candidateTokens));
23587
+ }
23588
+ let bestCosine = 0;
23589
+ if (cosineEnabled && sameScopeAsAnySource) {
23590
+ const candidateVector = await this.getCachedEmbeddingForPropagation(candidate.text);
23591
+ if (candidateVector) {
23592
+ for (let i = 0;i < sourceVectors.length; i++) {
23593
+ if (!sameScopeBySourceIndex[i])
23594
+ continue;
23595
+ const sourceVector = sourceVectors[i];
23596
+ if (!sourceVector)
23597
+ continue;
23598
+ bestCosine = Math.max(bestCosine, cosineSimilarity(sourceVector, candidateVector));
23599
+ }
23600
+ }
23601
+ }
23602
+ const qualifiesByOverlap = bestOverlap >= this.config.learning.propagationTokenOverlapThreshold;
23603
+ const qualifiesByCosine = cosineEnabled && bestCosine >= cosineThreshold;
23604
+ if (qualifiesByOverlap || qualifiesByCosine) {
23605
+ targets.push({
23606
+ id: candidate.id,
23607
+ overlap: Math.max(bestOverlap, bestCosine)
23608
+ });
23609
+ }
23610
+ }
23611
+ return targets.sort((a, b) => b.overlap - a.overlap || a.id.localeCompare(b.id)).slice(0, this.config.learning.propagationFanout).map((target) => target.id);
23612
+ }
23613
+ async getCachedEmbeddingForPropagation(text) {
23614
+ if (!this.embeddingProvider)
23615
+ return null;
23616
+ const modelVersion = this.embeddingProvider.modelVersion;
23617
+ const normalized = normalizeMemoryText(text).toLowerCase();
23618
+ if (!normalized)
23619
+ return null;
23620
+ const cached = this.embeddingCache?.get(modelVersion, normalized)?.vector;
23621
+ if (cached)
23622
+ return cached;
23623
+ try {
23624
+ const vector = await this.embeddingProvider.embed(normalized);
23625
+ this.embeddingCache?.set(modelVersion, normalized, {
23626
+ vector,
23627
+ modelVersion,
23628
+ queryHash: normalized
23629
+ });
23630
+ return vector;
23631
+ } catch {
23632
+ return null;
23633
+ }
23634
+ }
23635
+ recentlyRecalledMemoryIds(lookbackDays) {
23636
+ const cutoffMs = Date.now() - Math.max(0, lookbackDays) * 24 * 60 * 60 * 1000;
23637
+ const memoryIds = new Set;
23638
+ for (const event of this.listRecallUsageSync(500)) {
23639
+ const timestamp = Date.parse(event.timestamp);
23640
+ if (Number.isFinite(timestamp) && timestamp < cutoffMs)
23641
+ continue;
23642
+ for (const memoryId of event.memoryIds)
23643
+ memoryIds.add(memoryId);
23644
+ }
23645
+ return memoryIds;
23646
+ }
23647
+ listRecallUsageSync(limit = 1000) {
23648
+ const rows = this.requireDb().query(`SELECT id, usage_json, q_value, last_reward, task_outcome, council_verdict_json, reward_key
23649
+ FROM memory_recall_usage
23650
+ ORDER BY timestamp DESC
23651
+ LIMIT ?`).all(Math.max(1, Math.trunc(limit)));
23652
+ return rows.map((row) => parseRecallUsageRow(row)).filter(isRecallUsageEvent);
23653
+ }
23317
23654
  hasMigration(name) {
23318
23655
  const row = this.requireDb().query("SELECT version, name FROM schema_migrations WHERE name = ? LIMIT 1").get(name);
23319
23656
  return Boolean(row);
@@ -23413,7 +23750,13 @@ class SQLiteMemoryProvider {
23413
23750
  continue;
23414
23751
  const apply = db.transaction(() => {
23415
23752
  for (const statement of splitSql(migration.sql)) {
23416
- db.run(statement);
23753
+ try {
23754
+ db.run(statement);
23755
+ } catch (err) {
23756
+ const message = err instanceof Error ? err.message : String(err);
23757
+ if (!/duplicate column name/i.test(message))
23758
+ throw err;
23759
+ }
23417
23760
  }
23418
23761
  db.run("INSERT INTO schema_migrations (version, name) VALUES (?, ?)", [
23419
23762
  migration.version,
@@ -23845,6 +24188,178 @@ class SQLiteMemoryProvider {
23845
24188
  return this.db;
23846
24189
  }
23847
24190
  }
24191
+ function averageMemoryQValue(records) {
24192
+ if (records.length === 0)
24193
+ return DEFAULT_MEMORY_Q_VALUE;
24194
+ return averageNumbers(records.map((record) => memoryQValue(record)));
24195
+ }
24196
+ function averageNumbers(values) {
24197
+ const finite = values.filter((value) => Number.isFinite(value));
24198
+ if (finite.length === 0)
24199
+ return DEFAULT_MEMORY_Q_VALUE;
24200
+ return finite.reduce((sum, value) => sum + value, 0) / finite.length;
24201
+ }
24202
+ function isMemoryRecord(record) {
24203
+ return record !== undefined;
24204
+ }
24205
+ function isRecallUsageEvent(event) {
24206
+ return event !== null;
24207
+ }
24208
+ function rewardForOutcome(outcome) {
24209
+ switch (outcome) {
24210
+ case "approved":
24211
+ return 1;
24212
+ case "rejected":
24213
+ return -1;
24214
+ case "concerns":
24215
+ case "unknown":
24216
+ return 0;
24217
+ }
24218
+ }
24219
+ function propagatedRewardSignal(reward, propagationFactor) {
24220
+ const factor = Math.max(0, Math.min(1, propagationFactor));
24221
+ return DEFAULT_MEMORY_Q_VALUE + factor * (reward - DEFAULT_MEMORY_Q_VALUE);
24222
+ }
24223
+ function emptyRewardResult(outcome, reason) {
24224
+ return {
24225
+ success: false,
24226
+ outcome,
24227
+ memoryIds: [],
24228
+ reward: rewardForOutcome(outcome),
24229
+ updatedMemoryIds: [],
24230
+ propagatedMemoryIds: [],
24231
+ reason
24232
+ };
24233
+ }
24234
+ function parseRecallUsageRow(row) {
24235
+ try {
24236
+ const parsed = JSON.parse(row.usage_json);
24237
+ if (!Array.isArray(parsed.memoryIds) || typeof parsed.query !== "string") {
24238
+ return null;
24239
+ }
24240
+ const event = { ...parsed };
24241
+ if (typeof row.q_value === "number" && Number.isFinite(row.q_value)) {
24242
+ event.qValue = clamp012(row.q_value);
24243
+ }
24244
+ if (typeof row.last_reward === "number" && Number.isFinite(row.last_reward)) {
24245
+ event.lastReward = row.last_reward;
24246
+ }
24247
+ const taskOutcome = normalizeTaskOutcome(row.task_outcome);
24248
+ if (taskOutcome)
24249
+ event.taskOutcome = taskOutcome;
24250
+ return event;
24251
+ } catch {
24252
+ return null;
24253
+ }
24254
+ }
24255
+ function normalizeTaskOutcome(value) {
24256
+ if (value === "approved" || value === "rejected" || value === "concerns" || value === "unknown") {
24257
+ return value;
24258
+ }
24259
+ return;
24260
+ }
24261
+ function truncateJsonPayload(value) {
24262
+ if (value === undefined)
24263
+ return null;
24264
+ let json;
24265
+ try {
24266
+ json = JSON.stringify(value);
24267
+ } catch (err) {
24268
+ return JSON.stringify({
24269
+ unserializable: true,
24270
+ reason: err instanceof Error ? err.message : String(err)
24271
+ });
24272
+ }
24273
+ const maxLength = 8192;
24274
+ if (json.length <= maxLength)
24275
+ return json;
24276
+ return JSON.stringify({
24277
+ truncated: true,
24278
+ preview: json.slice(0, maxLength - 32)
24279
+ });
24280
+ }
24281
+ function deriveRewardKey(verdictPayload) {
24282
+ if (!verdictPayload || typeof verdictPayload !== "object")
24283
+ return null;
24284
+ const payload = verdictPayload;
24285
+ const swarmId = payload.swarmId;
24286
+ const roundNumber = payload.roundNumber;
24287
+ const unit = payload.taskId ?? payload.phaseNumber;
24288
+ const unitOk = typeof unit === "string" || typeof unit === "number";
24289
+ const roundOk = typeof roundNumber === "string" || typeof roundNumber === "number";
24290
+ if (typeof swarmId !== "string" || swarmId.length === 0 || !unitOk || !roundOk) {
24291
+ return null;
24292
+ }
24293
+ return `${swarmId}:${unit}:${roundNumber}`;
24294
+ }
24295
+ function summarizeRecallUsage(events) {
24296
+ const summary = new Map;
24297
+ for (const event of events) {
24298
+ for (const memoryId of event.memoryIds) {
24299
+ const existing = summary.get(memoryId) ?? {
24300
+ count: 0,
24301
+ lastRecalledAt: event.timestamp
24302
+ };
24303
+ existing.count++;
24304
+ if (event.timestamp >= existing.lastRecalledAt) {
24305
+ existing.lastRecalledAt = event.timestamp;
24306
+ existing.lastReward = event.lastReward;
24307
+ existing.taskOutcome = event.taskOutcome;
24308
+ }
24309
+ summary.set(memoryId, existing);
24310
+ }
24311
+ }
24312
+ return summary;
24313
+ }
24314
+ function uniqueStrings(values) {
24315
+ return Array.from(new Set(values.filter((value) => value.length > 0)));
24316
+ }
24317
+ function truncateText(value, maxLength) {
24318
+ if (value.length <= maxLength)
24319
+ return value;
24320
+ return `${value.slice(0, maxLength - 3)}...`;
24321
+ }
24322
+ function tokenizeText(value) {
24323
+ const tokens = new Set;
24324
+ for (const match of value.toLowerCase().matchAll(/[a-z0-9_]{3,}/g)) {
24325
+ const token = match[0];
24326
+ if (!FTS_STOP_WORDS.has(token))
24327
+ tokens.add(token);
24328
+ }
24329
+ return tokens;
24330
+ }
24331
+ function jaccard(left, right) {
24332
+ if (left.size === 0 || right.size === 0)
24333
+ return 0;
24334
+ let intersection = 0;
24335
+ for (const token of left) {
24336
+ if (right.has(token))
24337
+ intersection++;
24338
+ }
24339
+ const union = left.size + right.size - intersection;
24340
+ return union > 0 ? intersection / union : 0;
24341
+ }
24342
+ function cosineSimilarity(a, b) {
24343
+ if (a.length === 0 || a.length !== b.length)
24344
+ return 0;
24345
+ let dot = 0;
24346
+ let normA = 0;
24347
+ let normB = 0;
24348
+ for (let i = 0;i < a.length; i++) {
24349
+ dot += a[i] * b[i];
24350
+ normA += a[i] * a[i];
24351
+ normB += b[i] * b[i];
24352
+ }
24353
+ if (normA === 0 || normB === 0)
24354
+ return 0;
24355
+ const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
24356
+ return Number.isFinite(similarity) ? similarity : 0;
24357
+ }
24358
+ function clamp012(value) {
24359
+ if (!Number.isFinite(value))
24360
+ return DEFAULT_MEMORY_Q_VALUE;
24361
+ return Math.min(1, Math.max(0, value));
24362
+ }
23848
24363
  function splitSql(sql) {
23849
24364
  const statements = [];
23850
24365
  let current = "";
@@ -24154,6 +24669,21 @@ function resolvePoolKey(directory) {
24154
24669
  return path47.resolve(directory);
24155
24670
  }
24156
24671
  }
24672
+ function evictAndClose(directory) {
24673
+ const key = resolvePoolKey(directory);
24674
+ const active = entriesByKey.get(key);
24675
+ if (active) {
24676
+ entriesByKey.delete(key);
24677
+ unlinkEntry(active);
24678
+ callRealClose(active.provider);
24679
+ }
24680
+ for (const deferred of [...deferredEntries]) {
24681
+ if (deferred.key === key) {
24682
+ deferredEntries.delete(deferred);
24683
+ callRealClose(deferred.provider);
24684
+ }
24685
+ }
24686
+ }
24157
24687
 
24158
24688
  // src/memory/gateway.ts
24159
24689
  function createConfiguredMemoryProvider(directory, config) {
@@ -24256,7 +24786,8 @@ async function evaluateMemoryRecallFixtures(options) {
24256
24786
  } finally {
24257
24787
  await provider.close?.();
24258
24788
  if (!options.keepTempRoots) {
24259
- await fs19.rm(tempRoot, { recursive: true, force: true });
24789
+ evictAndClose(tempRoot);
24790
+ await rmTempRoot(tempRoot);
24260
24791
  }
24261
24792
  }
24262
24793
  }
@@ -24271,6 +24802,18 @@ async function evaluateMemoryRecallFixtures(options) {
24271
24802
  runs
24272
24803
  };
24273
24804
  }
24805
+ async function rmTempRoot(tempRoot) {
24806
+ for (let attempt = 0;attempt < 10; attempt++) {
24807
+ try {
24808
+ await fs19.rm(tempRoot, { recursive: true, force: true });
24809
+ return;
24810
+ } catch (err) {
24811
+ if (attempt === 9)
24812
+ throw err;
24813
+ await new Promise((resolve13) => setTimeout(resolve13, 50));
24814
+ }
24815
+ }
24816
+ }
24274
24817
  async function loadRecallEvaluationFixtures(fixtureDirectory) {
24275
24818
  const entries = await fs19.readdir(fixtureDirectory, { withFileTypes: true });
24276
24819
  const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
@@ -24585,6 +25128,7 @@ async function handleMemoryCommand(_directory, _args) {
24585
25128
  "- `/swarm memory status` - show provider, SQLite path, JSONL files, and last migration report",
24586
25129
  "- `/swarm memory pending` - show pending proposals and recent rejection reasons",
24587
25130
  "- `/swarm memory recall-log` - summarize recall usage by agent role and memory ID",
25131
+ "- `/swarm memory value-log` - show recent Q-value, reward, suppression, and promotion signals",
24588
25132
  "- `/swarm memory stale` - list expired scratch, superseded, deleted, and low-utility memories",
24589
25133
  "- `/swarm memory compact` - dry-run compaction; pass `--confirm` to remove deleted, superseded, and expired scratch records",
24590
25134
  "- `/swarm memory export` - export current memory and proposals to `.swarm/memory/export/*.jsonl`",
@@ -24679,10 +25223,12 @@ async function handleMemoryPendingCommand(directory, args) {
24679
25223
  "## Swarm Memory Pending",
24680
25224
  "",
24681
25225
  `- Pending proposals shown: \`${report.pendingProposals.length}\``,
24682
- `- Rejected proposal reasons shown: \`${report.rejectedProposalReasons.length}\``
25226
+ `- Rejected proposal reasons shown: \`${report.rejectedProposalReasons.length}\``,
25227
+ `- Promotion candidates shown: \`${report.promotionCandidates.length}\``
24683
25228
  ];
24684
25229
  appendProposalLines(lines, "Pending proposals", report.pendingProposals);
24685
25230
  appendProposalLines(lines, "Rejected proposal reasons", report.rejectedProposalReasons);
25231
+ appendValueLogLines(lines, "Promotion candidates", report.promotionCandidates);
24686
25232
  return lines.join(`
24687
25233
  `);
24688
25234
  } finally {
@@ -24719,6 +25265,39 @@ async function handleMemoryRecallLogCommand(directory, args) {
24719
25265
  await provider.close?.();
24720
25266
  }
24721
25267
  }
25268
+ async function handleMemoryValueLogCommand(directory, args) {
25269
+ const parsed = parseMaintenanceArgs(args, {
25270
+ usage: "Usage: /swarm memory value-log [--limit <n>]",
25271
+ allowConfirm: false
25272
+ });
25273
+ if ("error" in parsed)
25274
+ return parsed.error;
25275
+ const config = resolveCommandMemoryConfig(directory);
25276
+ const provider = createMaintenanceProvider(directory, config);
25277
+ try {
25278
+ await provider.initialize?.();
25279
+ if (!provider.listMemoryValueLog) {
25280
+ return "Memory provider does not support value-log reporting.";
25281
+ }
25282
+ const valueLog = await provider.listMemoryValueLog({ limit: parsed.limit });
25283
+ const promotionCandidates = valueLog.filter((entry) => entry.promotionCandidate);
25284
+ const suppressionCandidates = valueLog.filter((entry) => entry.suppressionCandidate);
25285
+ const lines = [
25286
+ "## Swarm Memory Value Log",
25287
+ "",
25288
+ `- Entries shown: \`${valueLog.length}\``,
25289
+ `- Promotion candidates shown: \`${promotionCandidates.length}\``,
25290
+ `- Suppression candidates shown: \`${suppressionCandidates.length}\``
25291
+ ];
25292
+ appendValueLogLines(lines, "Recent Q-value updates", valueLog);
25293
+ appendValueLogLines(lines, "Promotion candidates", promotionCandidates);
25294
+ appendValueLogLines(lines, "Suppression candidates", suppressionCandidates);
25295
+ return lines.join(`
25296
+ `);
25297
+ } finally {
25298
+ await provider.close?.();
25299
+ }
25300
+ }
24722
25301
  async function handleMemoryStaleCommand(directory, args) {
24723
25302
  const parsed = parseMaintenanceArgs(args, {
24724
25303
  usage: "Usage: /swarm memory stale [--limit <n>]",
@@ -24740,12 +25319,14 @@ async function handleMemoryStaleCommand(directory, args) {
24740
25319
  `- Expired scratch memories shown: \`${report.expiredScratchMemories.length}\``,
24741
25320
  `- Deleted tombstones shown: \`${report.deletedMemories.length}\``,
24742
25321
  `- Superseded memories shown: \`${report.supersededMemories.length}\``,
24743
- `- Low-utility memories shown: \`${report.lowUtilityMemories.length}\``
25322
+ `- Low-utility memories shown: \`${report.lowUtilityMemories.length}\``,
25323
+ `- Low-Q memories shown: \`${report.lowQValueMemories.length}\``
24744
25324
  ];
24745
25325
  appendMemoryLines(lines, "Expired scratch memories", report.expiredScratchMemories);
24746
25326
  appendMemoryLines(lines, "Deleted tombstones", report.deletedMemories);
24747
25327
  appendSupersededChains(lines, report);
24748
25328
  appendMemoryLines(lines, "Low-utility memories", report.lowUtilityMemories);
25329
+ appendValueLogLines(lines, "Low-Q memories", report.lowQValueMemories);
24749
25330
  return lines.join(`
24750
25331
  `);
24751
25332
  } finally {
@@ -25022,6 +25603,19 @@ function appendRecallMemoryLines(lines, memories) {
25022
25603
  lines.push(`- \`${memory.memoryId}\`: ${memory.count} hit(s), last=${memory.lastRecalledAt}, avgScore=${memory.averageScore.toFixed(3)}`);
25023
25604
  }
25024
25605
  }
25606
+ function appendValueLogLines(lines, title, entries) {
25607
+ lines.push("", `### ${title}`);
25608
+ if (entries.length === 0) {
25609
+ lines.push("- none");
25610
+ return;
25611
+ }
25612
+ for (const entry of entries) {
25613
+ const outcome = entry.taskOutcome ? ` outcome=${entry.taskOutcome}` : "";
25614
+ const reward = typeof entry.lastReward === "number" ? ` reward=${entry.lastReward.toFixed(2)}` : "";
25615
+ const last = entry.lastRecalledAt ? ` last=${entry.lastRecalledAt}` : "";
25616
+ lines.push(`- \`${entry.memoryId}\` ${entry.kind} q=${entry.qValue.toFixed(3)} recalls=${entry.recallCount}${reward}${outcome}${last} - ${entry.textPreview}`);
25617
+ }
25618
+ }
25025
25619
  function appendSupersededChains(lines, report) {
25026
25620
  lines.push("", "### Superseded chains");
25027
25621
  if (report.supersededChains.length === 0) {
@@ -25475,7 +26069,7 @@ async function handlePrSubscribeCommand(directory, args, sessionID) {
25475
26069
  return [
25476
26070
  "Usage: /swarm pr subscribe <pr-url|owner/repo#N|N>",
25477
26071
  "",
25478
- "Subscribes the current session to receive advisory notifications",
26072
+ "Subscribes the current session to PR state-change events",
25479
26073
  "for the specified PR. Requires pr_monitor.enabled: true in config.",
25480
26074
  "",
25481
26075
  " /swarm pr subscribe https://github.com/owner/repo/pull/42",
@@ -25534,15 +26128,19 @@ async function handlePrSubscribeCommand(directory, args, sessionID) {
25534
26128
  prUrl,
25535
26129
  maxSubscriptions: prMonitorConfig?.max_subscriptions
25536
26130
  });
26131
+ const deliveryMode = prMonitorConfig?.event_delivery === "advisory" ? "advisory" : "prompt";
26132
+ const deliveryLine = deliveryMode === "prompt" ? "Delivery mode: prompt \u2014 events wake this session with a <pr-activity> message." : "Delivery mode: advisory \u2014 events queue as session advisories for the next turn.";
25537
26133
  return [
25538
26134
  `Subscribed to ${prUrl}`,
25539
26135
  `Session: ${sessionID}`,
25540
26136
  `PR: ${repoFullName}#${prInfo.number}`,
25541
26137
  "",
25542
- "The background PR monitor will now check this PR for",
25543
- "CI failures, new comments, review state changes, and",
25544
- "merge/close events. Notifications appear as session-scoped",
25545
- "advisories with dedup tokens."
26138
+ "The background PR monitor will now watch this PR and deliver",
26139
+ "the events enabled by your pr_monitor config: CI failures, new",
26140
+ "comments, review activity (changes requested / approved), merge",
26141
+ "conflicts and resolutions, merge/close, and (off by default)",
26142
+ "CI success. Each event type is gated by its notify_* flag.",
26143
+ deliveryLine
25546
26144
  ].join(`
25547
26145
  `);
25548
26146
  } catch (err) {
@@ -33373,7 +33971,7 @@ function buildDetailedHelp(commandName, entry) {
33373
33971
  async function handleHelpCommand(ctx) {
33374
33972
  const targetCommand = ctx.args.join(" ");
33375
33973
  if (!targetCommand) {
33376
- const { buildHelpText } = await import("./index-fhn62ce4.js");
33974
+ const { buildHelpText } = await import("./index-8mj10cds.js");
33377
33975
  return buildHelpText();
33378
33976
  }
33379
33977
  const tokens = targetCommand.split(/\s+/);
@@ -33382,7 +33980,7 @@ async function handleHelpCommand(ctx) {
33382
33980
  return _internals47.buildDetailedHelp(resolved.key, resolved.entry);
33383
33981
  }
33384
33982
  const similar = _internals47.findSimilarCommands(targetCommand);
33385
- const { buildHelpText: fullHelp } = await import("./index-fhn62ce4.js");
33983
+ const { buildHelpText: fullHelp } = await import("./index-8mj10cds.js");
33386
33984
  if (similar.length > 0) {
33387
33985
  return `Command '/swarm ${targetCommand}' not found.
33388
33986
 
@@ -33515,7 +34113,7 @@ var COMMAND_REGISTRY = {
33515
34113
  },
33516
34114
  "guardrail explain": {
33517
34115
  handler: async (ctx) => {
33518
- const { handleGuardrailExplain } = await import("./guardrail-explain-7c857j83.js");
34116
+ const { handleGuardrailExplain } = await import("./guardrail-explain-9br76zh5.js");
33519
34117
  return handleGuardrailExplain(ctx.directory, ctx.args);
33520
34118
  },
33521
34119
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -33525,7 +34123,7 @@ var COMMAND_REGISTRY = {
33525
34123
  },
33526
34124
  "guardrail-log": {
33527
34125
  handler: async (ctx) => {
33528
- const { handleGuardrailLog } = await import("./guardrail-log-vg0atkrx.js");
34126
+ const { handleGuardrailLog } = await import("./guardrail-log-peftzya3.js");
33529
34127
  return handleGuardrailLog(ctx.directory, ctx.args);
33530
34128
  },
33531
34129
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -33870,9 +34468,9 @@ Subcommands:
33870
34468
  handler: (ctx) => handlePrSubscribeCommand(ctx.directory, ctx.args, ctx.sessionID),
33871
34469
  description: "Subscribe the current session to PR state-change notifications",
33872
34470
  args: "<pr-url|owner/repo#N|N>",
33873
- details: "Subscribes the current session to receive advisory notifications for the specified PR. When pr_monitor.enabled is true, the background polling worker will detect CI failures, new comments, merge conflicts, review state changes, and merge/close events. Notifications are delivered as session-scoped advisories with dedup tokens. Supports full GitHub URL, owner/repo#N shorthand, or bare PR number (resolved against origin). Requires pr_monitor.enabled: true in config.",
34471
+ details: 'Subscribes the current session to PR state-change events for the specified PR. When pr_monitor.enabled is true, the background polling worker detects CI failures, new comments, review state changes (changes requested / approved), merge conflicts and conflict resolutions, and merge/close events \u2014 each gated by its pr_monitor notify_* config flag (notify_ci_success defaults to false). Delivery follows pr_monitor.event_delivery: "prompt" (default) wakes the subscribed session with a structured <pr-activity> message; "advisory" queues session-scoped advisories with dedup tokens for the next turn. Subscriptions are idempotent, capped by pr_monitor.max_subscriptions, and agent-callable. Supports full GitHub URL, owner/repo#N shorthand, or bare PR number (resolved against origin). Requires pr_monitor.enabled: true in config.',
33874
34472
  category: "agent",
33875
- toolPolicy: "human-only"
34473
+ toolPolicy: "agent"
33876
34474
  },
33877
34475
  "pr-subscribe": {
33878
34476
  handler: (ctx) => handlePrSubscribeCommand(ctx.directory, ctx.args, ctx.sessionID),
@@ -33884,9 +34482,9 @@ Subcommands:
33884
34482
  handler: (ctx) => handlePrUnsubscribeCommand(ctx.directory, ctx.args, ctx.sessionID),
33885
34483
  description: "Unsubscribe the current session from PR state-change notifications",
33886
34484
  args: "<pr-url|owner/repo#N|N>",
33887
- details: "Unsubscribes the current session from receiving advisory notifications for the specified PR. Removes the active subscription record. Supports full GitHub URL, owner/repo#N shorthand, or bare PR number (resolved against origin).",
34485
+ details: "Unsubscribes the current session from PR state-change events for the specified PR. Removes the active subscription record (idempotent; agent-callable). Supports full GitHub URL, owner/repo#N shorthand, or bare PR number (resolved against origin).",
33888
34486
  category: "agent",
33889
- toolPolicy: "human-only"
34487
+ toolPolicy: "agent"
33890
34488
  },
33891
34489
  "pr-unsubscribe": {
33892
34490
  handler: (ctx) => handlePrUnsubscribeCommand(ctx.directory, ctx.args, ctx.sessionID),
@@ -34184,6 +34782,14 @@ Subcommands:
34184
34782
  category: "diagnostics",
34185
34783
  toolPolicy: "agent"
34186
34784
  },
34785
+ "memory value-log": {
34786
+ handler: (ctx) => handleMemoryValueLogCommand(ctx.directory, ctx.args),
34787
+ description: "Show Swarm memory Q-value and reward updates",
34788
+ subcommandOf: "memory",
34789
+ args: "--limit <n>",
34790
+ category: "diagnostics",
34791
+ toolPolicy: "agent"
34792
+ },
34187
34793
  "memory compact": {
34188
34794
  handler: (ctx) => handleMemoryCompactCommand(ctx.directory, ctx.args),
34189
34795
  description: "Compact deleted, superseded, and expired scratch memories",
@@ -34459,4 +35065,4 @@ ${text}`;
34459
35065
  };
34460
35066
  }
34461
35067
 
34462
- export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCostsCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleLinkCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleUnlinkCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals47 as _internals, resolveCommand };
35068
+ export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCostsCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleLinkCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryValueLogCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleUnlinkCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals47 as _internals, resolveCommand };