opencode-swarm 7.32.2 → 7.32.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -34,7 +34,7 @@ var package_default;
34
34
  var init_package = __esm(() => {
35
35
  package_default = {
36
36
  name: "opencode-swarm",
37
- version: "7.32.2",
37
+ version: "7.32.3",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -17331,8 +17331,32 @@ var init_schema = __esm(() => {
17331
17331
  recall: exports_external.object({
17332
17332
  defaultMaxItems: exports_external.number().int().min(1).max(20).default(8),
17333
17333
  defaultTokenBudget: exports_external.number().int().min(100).max(5000).default(1200),
17334
- minScore: exports_external.number().min(0).max(1).default(0.05)
17335
- }).default({ defaultMaxItems: 8, defaultTokenBudget: 1200, minScore: 0.05 }),
17334
+ minScore: exports_external.number().min(0).max(1).default(0.05),
17335
+ injection: exports_external.object({
17336
+ enabled: exports_external.boolean().default(true),
17337
+ minScore: exports_external.number().min(0).max(1).default(0.25),
17338
+ requireQuerySignal: exports_external.boolean().default(true),
17339
+ maxItems: exports_external.number().int().min(1).max(20).default(6),
17340
+ tokenBudget: exports_external.number().int().min(100).max(5000).default(1000)
17341
+ }).default({
17342
+ enabled: true,
17343
+ minScore: 0.25,
17344
+ requireQuerySignal: true,
17345
+ maxItems: 6,
17346
+ tokenBudget: 1000
17347
+ })
17348
+ }).default({
17349
+ defaultMaxItems: 8,
17350
+ defaultTokenBudget: 1200,
17351
+ minScore: 0.05,
17352
+ injection: {
17353
+ enabled: true,
17354
+ minScore: 0.25,
17355
+ requireQuerySignal: true,
17356
+ maxItems: 6,
17357
+ tokenBudget: 1000
17358
+ }
17359
+ }),
17336
17360
  writes: exports_external.object({
17337
17361
  mode: exports_external.literal("propose").default("propose")
17338
17362
  }).default({ mode: "propose" }),
@@ -510,6 +510,13 @@ export declare const MemoryConfigSchema: z.ZodObject<{
510
510
  defaultMaxItems: z.ZodDefault<z.ZodNumber>;
511
511
  defaultTokenBudget: z.ZodDefault<z.ZodNumber>;
512
512
  minScore: z.ZodDefault<z.ZodNumber>;
513
+ injection: z.ZodDefault<z.ZodObject<{
514
+ enabled: z.ZodDefault<z.ZodBoolean>;
515
+ minScore: z.ZodDefault<z.ZodNumber>;
516
+ requireQuerySignal: z.ZodDefault<z.ZodBoolean>;
517
+ maxItems: z.ZodDefault<z.ZodNumber>;
518
+ tokenBudget: z.ZodDefault<z.ZodNumber>;
519
+ }, z.core.$strip>>;
513
520
  }, z.core.$strip>>;
514
521
  writes: z.ZodDefault<z.ZodObject<{
515
522
  mode: z.ZodDefault<z.ZodLiteral<"propose">>;
@@ -1161,6 +1168,13 @@ export declare const PluginConfigSchema: z.ZodObject<{
1161
1168
  defaultMaxItems: z.ZodDefault<z.ZodNumber>;
1162
1169
  defaultTokenBudget: z.ZodDefault<z.ZodNumber>;
1163
1170
  minScore: z.ZodDefault<z.ZodNumber>;
1171
+ injection: z.ZodDefault<z.ZodObject<{
1172
+ enabled: z.ZodDefault<z.ZodBoolean>;
1173
+ minScore: z.ZodDefault<z.ZodNumber>;
1174
+ requireQuerySignal: z.ZodDefault<z.ZodBoolean>;
1175
+ maxItems: z.ZodDefault<z.ZodNumber>;
1176
+ tokenBudget: z.ZodDefault<z.ZodNumber>;
1177
+ }, z.core.$strip>>;
1164
1178
  }, z.core.$strip>>;
1165
1179
  writes: z.ZodDefault<z.ZodObject<{
1166
1180
  mode: z.ZodDefault<z.ZodLiteral<"propose">>;
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.32.2",
51
+ version: "7.32.3",
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",
@@ -15544,8 +15544,32 @@ var init_schema = __esm(() => {
15544
15544
  recall: exports_external.object({
15545
15545
  defaultMaxItems: exports_external.number().int().min(1).max(20).default(8),
15546
15546
  defaultTokenBudget: exports_external.number().int().min(100).max(5000).default(1200),
15547
- minScore: exports_external.number().min(0).max(1).default(0.05)
15548
- }).default({ defaultMaxItems: 8, defaultTokenBudget: 1200, minScore: 0.05 }),
15547
+ minScore: exports_external.number().min(0).max(1).default(0.05),
15548
+ injection: exports_external.object({
15549
+ enabled: exports_external.boolean().default(true),
15550
+ minScore: exports_external.number().min(0).max(1).default(0.25),
15551
+ requireQuerySignal: exports_external.boolean().default(true),
15552
+ maxItems: exports_external.number().int().min(1).max(20).default(6),
15553
+ tokenBudget: exports_external.number().int().min(100).max(5000).default(1000)
15554
+ }).default({
15555
+ enabled: true,
15556
+ minScore: 0.25,
15557
+ requireQuerySignal: true,
15558
+ maxItems: 6,
15559
+ tokenBudget: 1000
15560
+ })
15561
+ }).default({
15562
+ defaultMaxItems: 8,
15563
+ defaultTokenBudget: 1200,
15564
+ minScore: 0.05,
15565
+ injection: {
15566
+ enabled: true,
15567
+ minScore: 0.25,
15568
+ requireQuerySignal: true,
15569
+ maxItems: 6,
15570
+ tokenBudget: 1000
15571
+ }
15572
+ }),
15549
15573
  writes: exports_external.object({
15550
15574
  mode: exports_external.literal("propose").default("propose")
15551
15575
  }).default({ mode: "propose" }),
@@ -97574,7 +97598,14 @@ var DEFAULT_MEMORY_CONFIG = {
97574
97598
  recall: {
97575
97599
  defaultMaxItems: 8,
97576
97600
  defaultTokenBudget: 1200,
97577
- minScore: 0.05
97601
+ minScore: 0.05,
97602
+ injection: {
97603
+ enabled: true,
97604
+ minScore: 0.25,
97605
+ requireQuerySignal: true,
97606
+ maxItems: 6,
97607
+ tokenBudget: 1000
97608
+ }
97578
97609
  },
97579
97610
  writes: {
97580
97611
  mode: "propose"
@@ -97605,7 +97636,11 @@ function resolveMemoryConfig(input) {
97605
97636
  ...input ?? {},
97606
97637
  recall: {
97607
97638
  ...DEFAULT_MEMORY_CONFIG.recall,
97608
- ...input?.recall ?? {}
97639
+ ...input?.recall ?? {},
97640
+ injection: {
97641
+ ...DEFAULT_MEMORY_CONFIG.recall.injection,
97642
+ ...input?.recall?.injection ?? {}
97643
+ }
97609
97644
  },
97610
97645
  writes: {
97611
97646
  ...DEFAULT_MEMORY_CONFIG.writes,
@@ -97887,6 +97922,24 @@ function validateMemoryProposal(proposal) {
97887
97922
  function tokenize(text) {
97888
97923
  return new Set(text.toLowerCase().replace(/[^\w\s-]/g, " ").split(/\s+/).map((token) => token.trim()).filter(Boolean));
97889
97924
  }
97925
+ function normalizeKindText(kind) {
97926
+ return kind.replace(/_/g, " ");
97927
+ }
97928
+ function collectMetadataStrings(metadata2, keys) {
97929
+ const values = [];
97930
+ for (const key of keys) {
97931
+ const value = metadata2[key];
97932
+ if (typeof value === "string")
97933
+ values.push(value);
97934
+ if (Array.isArray(value)) {
97935
+ for (const item of value) {
97936
+ if (typeof item === "string")
97937
+ values.push(item);
97938
+ }
97939
+ }
97940
+ }
97941
+ return values;
97942
+ }
97890
97943
  function overlap(a, b) {
97891
97944
  if (a.size === 0 || b.size === 0)
97892
97945
  return 0;
@@ -97924,37 +97977,101 @@ function sameScope(a, b) {
97924
97977
  function scopeAllowed(recordScope, allowedScopes) {
97925
97978
  return allowedScopes.some((scope) => sameScope(recordScope, scope));
97926
97979
  }
97927
- function scoreMemoryRecord(record3, request) {
97928
- if (!request.includeExpired && isExpired2(record3))
97929
- return null;
97980
+ function scoreMemoryRecordDetailed(record3, request) {
97981
+ if (!request.includeExpired && isExpired2(record3)) {
97982
+ return { item: null, skipReason: "filtered" };
97983
+ }
97930
97984
  if (record3.supersededBy)
97931
- return null;
97932
- if (record3.metadata.deleted === true)
97933
- return null;
97934
- if (!scopeAllowed(record3.scope, request.scopes))
97935
- return null;
97936
- if (request.kinds && !request.kinds.includes(record3.kind))
97937
- return null;
97938
- const queryTokens = tokenize(request.query);
97985
+ return { item: null, skipReason: "filtered" };
97986
+ if (record3.metadata.deleted === true) {
97987
+ return { item: null, skipReason: "filtered" };
97988
+ }
97989
+ if (!scopeAllowed(record3.scope, request.scopes)) {
97990
+ return { item: null, skipReason: "filtered" };
97991
+ }
97992
+ if (request.kinds && !request.kinds.includes(record3.kind)) {
97993
+ return { item: null, skipReason: "filtered" };
97994
+ }
97995
+ const queryTokens = request.mode === "injection" && request.task ? tokenize(request.task) : tokenize(request.query);
97939
97996
  const textTokens = tokenize(record3.text);
97940
97997
  const tagTokens = tokenize(record3.tags.join(" "));
97998
+ const fileTokens = tokenize([
97999
+ record3.source.filePath,
98000
+ ...collectMetadataStrings(record3.metadata, [
98001
+ "file",
98002
+ "filePath",
98003
+ "files",
98004
+ "touchedFiles"
98005
+ ])
98006
+ ].filter((value) => typeof value === "string").join(" "));
98007
+ const symbolTokens = tokenize(collectMetadataStrings(record3.metadata, ["symbol", "symbols"]).join(" "));
98008
+ const kindQueryOverlap = overlap(queryTokens, tokenize(normalizeKindText(record3.kind)));
97941
98009
  const textOverlap = overlap(queryTokens, textTokens);
97942
98010
  const tagOverlap = overlap(queryTokens, tagTokens);
97943
- const score = textOverlap * 0.45 + tagOverlap * 0.2 + scopeSpecificityBoost(record3.scope) * 0.15 + kindProfileBoost(record3.kind, request) * 0.1 + record3.confidence * 0.1;
98011
+ const fileOverlap = overlap(queryTokens, fileTokens);
98012
+ const symbolOverlap = overlap(queryTokens, symbolTokens);
98013
+ const kindMatch = request.kinds?.includes(record3.kind) ?? false;
98014
+ const scopeMatch = scopeAllowed(record3.scope, request.scopes);
98015
+ const hasQuerySignal = textOverlap > 0 || tagOverlap > 0 || fileOverlap > 0 || symbolOverlap > 0 || kindQueryOverlap > 0;
98016
+ if (request.mode === "injection" && request.requireQuerySignal !== false && !hasQuerySignal) {
98017
+ return { item: null, skipReason: "no_signal" };
98018
+ }
98019
+ const score = textOverlap * 0.45 + tagOverlap * 0.2 + fileOverlap * 0.05 + symbolOverlap * 0.05 + scopeSpecificityBoost(record3.scope) * 0.15 + kindProfileBoost(record3.kind, request) * 0.1 + record3.confidence * 0.1;
97944
98020
  const reasonParts = [
97945
98021
  textOverlap > 0 ? `text_overlap=${textOverlap.toFixed(2)}` : null,
97946
98022
  tagOverlap > 0 ? `tag_overlap=${tagOverlap.toFixed(2)}` : null,
98023
+ fileOverlap > 0 ? `file_overlap=${fileOverlap.toFixed(2)}` : null,
98024
+ symbolOverlap > 0 ? `symbol_overlap=${symbolOverlap.toFixed(2)}` : null,
98025
+ kindQueryOverlap > 0 ? `kind_query=${kindQueryOverlap.toFixed(2)}` : null,
97947
98026
  `scope=${record3.scope.type}`,
97948
98027
  `confidence=${record3.confidence.toFixed(2)}`
97949
98028
  ].filter(Boolean);
97950
98029
  return {
97951
- record: record3,
97952
- score,
97953
- reason: reasonParts.join(", ")
98030
+ item: {
98031
+ record: record3,
98032
+ score,
98033
+ reason: reasonParts.join(", "),
98034
+ signals: {
98035
+ textOverlap,
98036
+ tagOverlap,
98037
+ fileOverlap,
98038
+ symbolOverlap,
98039
+ kindMatch,
98040
+ scopeMatch
98041
+ }
98042
+ }
97954
98043
  };
97955
98044
  }
97956
- function scoreMemoryRecords(records, request) {
97957
- return records.map((record3) => scoreMemoryRecord(record3, request)).filter((item) => item !== null).filter((item) => item.score >= (request.minScore ?? 0)).sort((a, b) => b.score - a.score || a.record.id.localeCompare(b.record.id));
98045
+ function scoreMemoryRecordsWithDiagnostics(records, request) {
98046
+ const minScore = request.minScore ?? 0;
98047
+ const diagnostics = {
98048
+ candidateCount: records.length,
98049
+ preScoredFilteredCount: 0,
98050
+ scoredCount: 0,
98051
+ returnedCount: 0,
98052
+ noSignalCount: 0,
98053
+ belowThresholdCount: 0
98054
+ };
98055
+ const items = [];
98056
+ for (const record3 of records) {
98057
+ const result = scoreMemoryRecordDetailed(record3, request);
98058
+ if (!result.item) {
98059
+ if (result.skipReason === "filtered")
98060
+ diagnostics.preScoredFilteredCount++;
98061
+ if (result.skipReason === "no_signal")
98062
+ diagnostics.noSignalCount++;
98063
+ continue;
98064
+ }
98065
+ diagnostics.scoredCount++;
98066
+ if (result.item.score < minScore) {
98067
+ diagnostics.belowThresholdCount++;
98068
+ continue;
98069
+ }
98070
+ items.push(result.item);
98071
+ }
98072
+ items.sort((a, b) => b.score - a.score || a.record.id.localeCompare(b.record.id));
98073
+ diagnostics.returnedCount = items.length;
98074
+ return { items, diagnostics };
97958
98075
  }
97959
98076
 
97960
98077
  // src/memory/local-jsonl-provider.ts
@@ -98030,13 +98147,23 @@ class LocalJsonlMemoryProvider {
98030
98147
  await this.audit("delete", id, reason);
98031
98148
  }
98032
98149
  async recall(request) {
98150
+ return (await this.recallWithDiagnostics(request)).items;
98151
+ }
98152
+ async recallWithDiagnostics(request) {
98033
98153
  await this.initialize();
98034
98154
  const records = await this.list({
98035
98155
  scopes: request.scopes,
98036
98156
  kinds: request.kinds,
98037
98157
  includeExpired: request.includeExpired
98038
98158
  });
98039
- return scoreMemoryRecords(records, request).slice(0, request.maxItems);
98159
+ const result = scoreMemoryRecordsWithDiagnostics(records, request);
98160
+ return {
98161
+ items: result.items.slice(0, request.maxItems),
98162
+ diagnostics: {
98163
+ ...result.diagnostics,
98164
+ returnedCount: Math.min(result.diagnostics.returnedCount, request.maxItems)
98165
+ }
98166
+ };
98040
98167
  }
98041
98168
  async recordRecallUsage(event) {
98042
98169
  await this.initialize();
@@ -98241,7 +98368,8 @@ function toRecallBundle(input) {
98241
98368
  generatedAt: input.generatedAt,
98242
98369
  items: block.items,
98243
98370
  tokenEstimate: block.tokenEstimate,
98244
- promptBlock: block.promptBlock
98371
+ promptBlock: block.promptBlock,
98372
+ diagnostics: input.diagnostics
98245
98373
  };
98246
98374
  }
98247
98375
 
@@ -98302,20 +98430,29 @@ class MemoryGateway {
98302
98430
  query,
98303
98431
  task: input.task,
98304
98432
  agentRole: this.context.agentRole,
98433
+ mode: input.mode ?? "manual",
98305
98434
  scopes,
98306
98435
  kinds: input.kinds,
98307
98436
  maxItems,
98308
98437
  tokenBudget,
98309
98438
  minScore: input.minScore ?? this.config.recall.minScore,
98439
+ requireQuerySignal: input.requireQuerySignal,
98310
98440
  includeExpired: input.includeExpired
98311
98441
  };
98312
- const results = await this.provider.recall(request);
98442
+ const recallResult = this.provider.recallWithDiagnostics ? await this.provider.recallWithDiagnostics(request) : { items: await this.provider.recall(request) };
98313
98443
  const bundle = toRecallBundle({
98314
98444
  id: createBundleId(query, generatedAt),
98315
98445
  query,
98316
98446
  generatedAt,
98317
- items: results,
98318
- tokenBudget
98447
+ items: recallResult.items,
98448
+ tokenBudget,
98449
+ diagnostics: recallResult.diagnostics ? {
98450
+ injectionSkipReason: input.mode === "injection" ? resolveInjectionSkipReason(recallResult.diagnostics) : undefined,
98451
+ candidateCount: recallResult.diagnostics.candidateCount,
98452
+ preScoredFilteredCount: recallResult.diagnostics.preScoredFilteredCount,
98453
+ noSignalCount: recallResult.diagnostics.noSignalCount,
98454
+ belowThresholdCount: recallResult.diagnostics.belowThresholdCount
98455
+ } : undefined
98319
98456
  });
98320
98457
  await this.provider.recordRecallUsage?.({
98321
98458
  bundleId: bundle.id,
@@ -98503,6 +98640,19 @@ function validateRequestedScopes(requested, allowed) {
98503
98640
  }
98504
98641
  return requested;
98505
98642
  }
98643
+ function resolveInjectionSkipReason(diagnostics) {
98644
+ if (diagnostics.returnedCount > 0)
98645
+ return;
98646
+ if (diagnostics.candidateCount === 0)
98647
+ return "no_results";
98648
+ const signalEligibleCount = diagnostics.candidateCount - diagnostics.preScoredFilteredCount;
98649
+ if (signalEligibleCount > 0 && diagnostics.noSignalCount > 0 && diagnostics.noSignalCount >= signalEligibleCount) {
98650
+ return "no_signal";
98651
+ }
98652
+ if (diagnostics.belowThresholdCount > 0)
98653
+ return "below_threshold";
98654
+ return "no_results";
98655
+ }
98506
98656
  function scopeKey(scope) {
98507
98657
  return JSON.stringify({
98508
98658
  type: scope.type,
@@ -98897,8 +99047,15 @@ async function recallForAgent(input) {
98897
99047
  agentId: input.agentId,
98898
99048
  runId: input.sessionID
98899
99049
  }, { config: input.config });
98900
- if (!gateway.isEnabled())
99050
+ const resolvedConfig = resolveMemoryConfig(input.config);
99051
+ if (!gateway.isEnabled()) {
99052
+ await logInjectionSkipped(input, "disabled");
98901
99053
  return null;
99054
+ }
99055
+ if (!resolvedConfig.recall.injection.enabled) {
99056
+ await logInjectionSkipped(input, "disabled");
99057
+ return null;
99058
+ }
98902
99059
  const scopes = gateway.deriveAllowedScopes();
98903
99060
  const planInput = {
98904
99061
  userGoal: compactText(input.userGoal),
@@ -98909,6 +99066,8 @@ async function recallForAgent(input) {
98909
99066
  touchedFiles: extractTouchedFiles(input.agentTask)
98910
99067
  };
98911
99068
  const plan = buildMemoryRecallPlan(planInput, { scopes });
99069
+ plan.maxItems = resolvedConfig.recall.injection.maxItems;
99070
+ plan.tokenBudget = resolvedConfig.recall.injection.tokenBudget;
98912
99071
  await input.appendRunLog(input.directory, input.sessionID, {
98913
99072
  event: "recall_requested",
98914
99073
  runId: input.sessionID ?? "unknown",
@@ -98924,10 +99083,13 @@ async function recallForAgent(input) {
98924
99083
  const recallInput = {
98925
99084
  query: plan.query,
98926
99085
  task: planInput.agentTask,
99086
+ mode: "injection",
98927
99087
  scopes: plan.scopes,
98928
99088
  kinds: plan.kinds,
98929
99089
  maxItems: plan.maxItems,
98930
- tokenBudget: plan.tokenBudget
99090
+ tokenBudget: plan.tokenBudget,
99091
+ minScore: resolvedConfig.recall.injection.minScore,
99092
+ requireQuerySignal: resolvedConfig.recall.injection.requireQuerySignal
98931
99093
  };
98932
99094
  const bundle = await gateway.recall(recallInput);
98933
99095
  await input.appendRunLog(input.directory, input.sessionID, {
@@ -98940,8 +99102,31 @@ async function recallForAgent(input) {
98940
99102
  scores: bundle.items.map((item) => item.score),
98941
99103
  tokenEstimate: bundle.tokenEstimate
98942
99104
  });
99105
+ if (bundle.items.length === 0) {
99106
+ await logInjectionSkipped(input, bundle.diagnostics?.injectionSkipReason ?? "no_results", bundle);
99107
+ }
98943
99108
  return { bundle, scopes };
98944
99109
  }
99110
+ async function logInjectionSkipped(input, reason, bundle) {
99111
+ await input.appendRunLog(input.directory, input.sessionID, {
99112
+ event: "prompt_injection_skipped",
99113
+ runId: input.sessionID ?? "unknown",
99114
+ agentRole: input.agentRole,
99115
+ agentId: input.agentId,
99116
+ bundleId: bundle?.id,
99117
+ memoryIds: bundle?.items.map((item) => item.record.id),
99118
+ scores: bundle?.items.map((item) => item.score),
99119
+ tokenEstimate: bundle?.tokenEstimate,
99120
+ rejectionReason: reason,
99121
+ metadata: {
99122
+ reason,
99123
+ candidateCount: bundle?.diagnostics?.candidateCount,
99124
+ preScoredFilteredCount: bundle?.diagnostics?.preScoredFilteredCount,
99125
+ noSignalCount: bundle?.diagnostics?.noSignalCount,
99126
+ belowThresholdCount: bundle?.diagnostics?.belowThresholdCount
99127
+ }
99128
+ });
99129
+ }
98945
99130
  function parseTaskToolInput(input) {
98946
99131
  const record3 = input;
98947
99132
  const rawTool = typeof record3.tool === "string" ? record3.tool : undefined;
@@ -114893,6 +115078,10 @@ var swarm_memory_recall = createSwarmTool({
114893
115078
  memory_ids: bundle.items.map((item) => item.record.id),
114894
115079
  total: bundle.items.length,
114895
115080
  token_estimate: bundle.tokenEstimate,
115081
+ signals: bundle.items.map((item) => ({
115082
+ memory_id: item.record.id,
115083
+ ...item.signals
115084
+ })),
114896
115085
  prompt_block: bundle.promptBlock
114897
115086
  }, null, 2);
114898
115087
  }
@@ -7,6 +7,13 @@ export interface MemoryConfig {
7
7
  defaultMaxItems: number;
8
8
  defaultTokenBudget: number;
9
9
  minScore: number;
10
+ injection: {
11
+ enabled: boolean;
12
+ minScore: number;
13
+ requireQuerySignal: boolean;
14
+ maxItems: number;
15
+ tokenBudget: number;
16
+ };
10
17
  };
11
18
  writes: {
12
19
  mode: 'propose';
@@ -1,6 +1,6 @@
1
1
  import { type MemoryConfig } from './config';
2
2
  import type { MemoryProposalStore, MemoryProvider } from './provider';
3
- import type { MemoryContext, MemoryKind, MemoryProposal, MemoryRecord, MemoryScopeRef, MemorySource, RecallBundle } from './types';
3
+ import type { MemoryContext, MemoryKind, MemoryProposal, MemoryRecord, MemoryScopeRef, MemorySource, RecallBundle, RecallMode } from './types';
4
4
  export interface MemoryGatewayOptions {
5
5
  config?: Partial<MemoryConfig>;
6
6
  provider?: MemoryProvider & Partial<MemoryProposalStore>;
@@ -18,11 +18,13 @@ export interface ProposeMemoryInput {
18
18
  export interface RecallMemoryInput {
19
19
  query: string;
20
20
  task?: string;
21
+ mode?: RecallMode;
21
22
  scopes?: MemoryScopeRef[];
22
23
  kinds?: MemoryKind[];
23
24
  maxItems?: number;
24
25
  tokenBudget?: number;
25
26
  minScore?: number;
27
+ requireQuerySignal?: boolean;
26
28
  includeExpired?: boolean;
27
29
  }
28
30
  export declare class MemoryGateway {
@@ -12,4 +12,4 @@ export { findSecrets, redactSecrets } from './redaction';
12
12
  export { MEMORY_RECALL_PROFILES, type MemoryRecallProfile, normalizeMemoryAgentRole, resolveMemoryRecallProfile, } from './role-profiles';
13
13
  export { appendMemoryRunLog, sanitizeRunId } from './run-log';
14
14
  export { computeMemoryContentHash, createBundleId, createMemoryId, createProposalId, isExpired, normalizeMemoryText, validateMemoryProposal, validateMemoryRecordRules, } from './schema';
15
- export type { MemoryContext, MemoryKind, MemoryListFilter, MemoryProposal, MemoryRecord, MemoryScopeRef, MemoryScopeType, RecallBundle, RecallRequest, RecallResultItem, } from './types';
15
+ export type { MemoryContext, MemoryKind, MemoryListFilter, MemoryProposal, MemoryRecord, MemoryScopeRef, MemoryScopeType, RecallBundle, RecallInjectionSkipReason, RecallMode, RecallRequest, RecallResultItem, } from './types';
@@ -1,4 +1,4 @@
1
- import type { MemoryConfig } from './config';
1
+ import { type MemoryConfig } from './config';
2
2
  import type { MemoryGateway, ProposeMemoryInput } from './gateway';
3
3
  import { appendMemoryRunLog } from './run-log';
4
4
  import type { MemoryKind } from './types';
@@ -1,5 +1,6 @@
1
1
  import { type MemoryConfig } from './config';
2
2
  import type { MemoryProposalStore, MemoryProvider, MemoryRecallUsageEvent } from './provider';
3
+ import type { RecallScoringDiagnostics } from './scoring';
3
4
  import type { MemoryListFilter, MemoryProposal, MemoryRecord, RecallRequest, RecallResultItem } from './types';
4
5
  export declare class LocalJsonlMemoryProvider implements MemoryProvider, MemoryProposalStore {
5
6
  readonly name = "local-jsonl";
@@ -15,6 +16,10 @@ export declare class LocalJsonlMemoryProvider implements MemoryProvider, MemoryP
15
16
  get(id: string): Promise<MemoryRecord | null>;
16
17
  delete(id: string, reason?: string): Promise<void>;
17
18
  recall(request: RecallRequest): Promise<RecallResultItem[]>;
19
+ recallWithDiagnostics(request: RecallRequest): Promise<{
20
+ items: RecallResultItem[];
21
+ diagnostics: RecallScoringDiagnostics;
22
+ }>;
18
23
  recordRecallUsage(event: MemoryRecallUsageEvent): Promise<void>;
19
24
  list(filter?: MemoryListFilter): Promise<MemoryRecord[]>;
20
25
  createProposal(proposal: MemoryProposal): Promise<MemoryProposal>;
@@ -10,4 +10,5 @@ export declare function toRecallBundle(input: {
10
10
  generatedAt: string;
11
11
  items: RecallResultItem[];
12
12
  tokenBudget: number;
13
+ diagnostics?: RecallBundle['diagnostics'];
13
14
  }): RecallBundle;
@@ -1,4 +1,9 @@
1
+ import type { RecallScoringDiagnostics } from './scoring';
1
2
  import type { MemoryListFilter, MemoryProposal, MemoryRecord, RecallRequest, RecallResultItem } from './types';
3
+ export interface MemoryRecallResult {
4
+ items: RecallResultItem[];
5
+ diagnostics?: RecallScoringDiagnostics;
6
+ }
2
7
  export interface MemoryRecallUsageEvent {
3
8
  bundleId: string;
4
9
  query: string;
@@ -18,6 +23,7 @@ export interface MemoryProvider {
18
23
  get(id: string): Promise<MemoryRecord | null>;
19
24
  delete(id: string, reason?: string): Promise<void>;
20
25
  recall(request: RecallRequest): Promise<RecallResultItem[]>;
26
+ recallWithDiagnostics?(request: RecallRequest): Promise<MemoryRecallResult>;
21
27
  recordRecallUsage?(event: MemoryRecallUsageEvent): Promise<void>;
22
28
  list(filter: MemoryListFilter): Promise<MemoryRecord[]>;
23
29
  }
@@ -1,4 +1,4 @@
1
- export type MemoryRunLogEventName = 'recall_requested' | 'recall_returned' | 'prompt_injected' | 'proposal_created' | 'proposal_rejected_by_validation';
1
+ export type MemoryRunLogEventName = 'recall_requested' | 'recall_returned' | 'prompt_injection_skipped' | 'prompt_injected' | 'proposal_created' | 'proposal_rejected_by_validation';
2
2
  export interface MemoryRunLogEvent {
3
3
  event: MemoryRunLogEventName;
4
4
  runId: string;
@@ -1,5 +1,17 @@
1
1
  import type { MemoryRecord, MemoryScopeRef, RecallRequest, RecallResultItem } from './types';
2
+ export interface RecallScoringDiagnostics {
3
+ candidateCount: number;
4
+ preScoredFilteredCount: number;
5
+ scoredCount: number;
6
+ returnedCount: number;
7
+ noSignalCount: number;
8
+ belowThresholdCount: number;
9
+ }
2
10
  export declare function sameScope(a: MemoryScopeRef, b: MemoryScopeRef): boolean;
3
11
  export declare function scopeAllowed(recordScope: MemoryScopeRef, allowedScopes: MemoryScopeRef[]): boolean;
4
12
  export declare function scoreMemoryRecord(record: MemoryRecord, request: RecallRequest): RecallResultItem | null;
5
13
  export declare function scoreMemoryRecords(records: MemoryRecord[], request: RecallRequest): RecallResultItem[];
14
+ export declare function scoreMemoryRecordsWithDiagnostics(records: MemoryRecord[], request: RecallRequest): {
15
+ items: RecallResultItem[];
16
+ diagnostics: RecallScoringDiagnostics;
17
+ };
@@ -56,15 +56,19 @@ export interface MemoryProposal {
56
56
  createdAt: string;
57
57
  metadata: Record<string, unknown>;
58
58
  }
59
+ export type RecallMode = 'manual' | 'injection' | 'curator' | 'evaluation';
60
+ export type RecallInjectionSkipReason = 'disabled' | 'no_signal' | 'below_threshold' | 'no_results';
59
61
  export interface RecallRequest {
60
62
  query: string;
61
63
  task?: string;
62
64
  agentRole?: string;
65
+ mode?: RecallMode;
63
66
  scopes: MemoryScopeRef[];
64
67
  kinds?: MemoryKind[];
65
68
  maxItems: number;
66
69
  tokenBudget: number;
67
70
  minScore?: number;
71
+ requireQuerySignal?: boolean;
68
72
  includeExpired?: boolean;
69
73
  includePendingProposals?: boolean;
70
74
  }
@@ -72,6 +76,14 @@ export interface RecallResultItem {
72
76
  record: MemoryRecord;
73
77
  score: number;
74
78
  reason: string;
79
+ signals: {
80
+ textOverlap: number;
81
+ tagOverlap: number;
82
+ fileOverlap?: number;
83
+ symbolOverlap?: number;
84
+ kindMatch: boolean;
85
+ scopeMatch: boolean;
86
+ };
75
87
  }
76
88
  export interface RecallBundle {
77
89
  id: string;
@@ -80,6 +92,13 @@ export interface RecallBundle {
80
92
  items: RecallResultItem[];
81
93
  tokenEstimate: number;
82
94
  promptBlock: string;
95
+ diagnostics?: {
96
+ injectionSkipReason?: RecallInjectionSkipReason;
97
+ candidateCount?: number;
98
+ preScoredFilteredCount?: number;
99
+ noSignalCount?: number;
100
+ belowThresholdCount?: number;
101
+ };
83
102
  }
84
103
  export interface MemoryContext {
85
104
  directory: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.32.2",
3
+ "version": "7.32.3",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",