opencode-swarm 7.79.6 → 7.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.79.6",
55
+ version: "7.80.0",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -17760,6 +17760,14 @@ var init_tool_metadata = __esm(() => {
17760
17760
  description: "dispatch multiple read-only exploration/review lanes concurrently and return a structured join result",
17761
17761
  agents: ["architect"]
17762
17762
  },
17763
+ dispatch_lanes_async: {
17764
+ description: "launch multiple read-only advisory lanes asynchronously and return a batch id for later collection",
17765
+ agents: ["architect"]
17766
+ },
17767
+ collect_lane_results: {
17768
+ description: "collect or poll results for a dispatch_lanes_async advisory batch without advancing workflow gates",
17769
+ agents: ["architect"]
17770
+ },
17763
17771
  summarize_work: {
17764
17772
  description: "emit a short structured summary of completed work (key decisions, assumptions, risks, constraints) at task completion; rolls up per phase for architecture-supervisor review. Advisory, never blocks.",
17765
17773
  agents: [
@@ -18787,7 +18795,8 @@ var init_schema = __esm(() => {
18787
18795
  }).optional(),
18788
18796
  enrichment: exports_external.object({
18789
18797
  max_calls_per_day: exports_external.number().int().min(0).max(1000).default(30),
18790
- quota_window: exports_external.enum(["utc", "local"]).default("utc")
18798
+ quota_window: exports_external.enum(["utc", "local"]).default("utc"),
18799
+ batch_size: exports_external.number().int().min(1).max(100).optional()
18791
18800
  }).default({ max_calls_per_day: 30, quota_window: "utc" })
18792
18801
  });
18793
18802
  MemoryConfigSchema = exports_external.object({
@@ -40490,10 +40499,12 @@ function isSkillMaturityEligible(entry, opts, outcomes = entry.retrieval_outcome
40490
40499
  if (outcomeSignal < 0)
40491
40500
  return false;
40492
40501
  const strongOutcomes = hasStrongSkillOutcomeRecord(outcomes);
40502
+ if (outcomeSignal > 0 && strongOutcomes)
40503
+ return true;
40493
40504
  if (entry.confidence < opts.minConfidence && !strongOutcomes)
40494
40505
  return false;
40495
- const confirmations = (entry.confirmed_by ?? []).length;
40496
- return confirmations >= opts.minConfirmations || strongOutcomes;
40506
+ const distinctPhases = new Set((entry.confirmed_by ?? []).map((c) => c.phase_number).filter((p) => typeof p === "number")).size;
40507
+ return distinctPhases >= opts.minConfirmations || strongOutcomes;
40497
40508
  }
40498
40509
  function jaccardSimilarity(setA, setB) {
40499
40510
  const normA = setA.map((s) => s.toLowerCase());
@@ -43827,6 +43838,35 @@ function buildV3EnrichmentPrompt(lesson, category, tags) {
43827
43838
  ].join(`
43828
43839
  `);
43829
43840
  }
43841
+ function buildV3BatchEnrichmentPrompt(lessons) {
43842
+ const lessonLines = lessons.map((item, idx) => `${idx + 1}. LESSON: ${item.lesson}
43843
+ CATEGORY: ${item.category}
43844
+ TAGS: ${item.tags.join(", ")}`).join(`
43845
+ `);
43846
+ return [
43847
+ "Convert each prose lesson below into an actionable knowledge directive.",
43848
+ "Output ONLY a JSON array (no code fences, no commentary).",
43849
+ `The array length MUST be exactly ${lessons.length}.`,
43850
+ "Each array element at position i maps to lesson i (1-indexed above).",
43851
+ "",
43852
+ "For EACH element, mandatory requirements:",
43853
+ "- At least ONE scope field non-empty:",
43854
+ ' "applies_to_agents": string[] \u2014 roles from: architect, coder, reviewer, test_engineer, sme, docs, designer, critic, curator',
43855
+ ' "applies_to_tools": string[] \u2014 tool names from: edit, write, patch, bash, read, grep, glob',
43856
+ "- At least ONE predicate field non-empty:",
43857
+ ' "forbidden_actions": string[] | "required_actions": string[] | "verification_checks": string[]',
43858
+ "",
43859
+ "Optional per element:",
43860
+ '"triggers": string[], "directive_priority": "low" | "medium" | "high" | "critical"',
43861
+ "",
43862
+ "Example array:",
43863
+ '[{"applies_to_agents":["coder"],"required_actions":["run focused tests before commit"],"directive_priority":"high"}]',
43864
+ "",
43865
+ "LESSONS:",
43866
+ lessonLines
43867
+ ].join(`
43868
+ `);
43869
+ }
43830
43870
  function parseV3EnrichmentResponse(text) {
43831
43871
  if (!text || typeof text !== "string") {
43832
43872
  return { missing: ["valid JSON object"] };
@@ -43867,6 +43907,109 @@ function parseV3EnrichmentResponse(text) {
43867
43907
  }
43868
43908
  return { fields };
43869
43909
  }
43910
+ function parseV3BatchEnrichmentResponse(text, expectedLength) {
43911
+ const empty = Array.from({ length: expectedLength }, () => null);
43912
+ if (!text || typeof text !== "string") {
43913
+ return { fields: empty, missing: ["valid JSON array"] };
43914
+ }
43915
+ const start = text.indexOf("[");
43916
+ const end = text.lastIndexOf("]");
43917
+ if (start < 0 || end <= start) {
43918
+ return { fields: empty, missing: ["valid JSON array"] };
43919
+ }
43920
+ let parsed;
43921
+ try {
43922
+ parsed = JSON.parse(text.slice(start, end + 1));
43923
+ } catch {
43924
+ return { fields: empty, missing: ["valid JSON array"] };
43925
+ }
43926
+ if (!Array.isArray(parsed)) {
43927
+ return { fields: empty, missing: ["valid JSON array"] };
43928
+ }
43929
+ const fields = Array.from({ length: expectedLength }, () => null);
43930
+ const missing = [];
43931
+ for (let i = 0;i < expectedLength; i++) {
43932
+ const item = parsed[i];
43933
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
43934
+ missing.push(`item ${i + 1}: valid JSON object`);
43935
+ continue;
43936
+ }
43937
+ const raw = item;
43938
+ const candidate = {};
43939
+ for (const key of ENRICHMENT_ALLOWED_FIELDS) {
43940
+ if (raw[key] !== undefined) {
43941
+ candidate[key] = raw[key];
43942
+ }
43943
+ }
43944
+ const shape = validateActionableFields(candidate);
43945
+ if (!shape.valid) {
43946
+ missing.push(`item ${i + 1}: ${shape.errors.join("; ")}`);
43947
+ continue;
43948
+ }
43949
+ const actionability = validateActionability(candidate);
43950
+ if (!actionability.actionable) {
43951
+ const expected = [];
43952
+ if (actionability.reason === "missing_predicate" || actionability.reason === "missing_predicate_and_scope") {
43953
+ expected.push("a non-empty predicate field (forbidden_actions, required_actions, or verification_checks)");
43954
+ }
43955
+ if (actionability.reason === "missing_scope" || actionability.reason === "missing_predicate_and_scope") {
43956
+ expected.push("a non-empty scope field (applies_to_agents or applies_to_tools)");
43957
+ }
43958
+ missing.push(`item ${i + 1}: ${expected.join("; ")}`);
43959
+ continue;
43960
+ }
43961
+ fields[i] = candidate;
43962
+ }
43963
+ if (parsed.length < expectedLength) {
43964
+ missing.push(`expected ${expectedLength} items but got ${parsed.length}`);
43965
+ } else if (parsed.length > expectedLength) {
43966
+ warn(`[knowledge-curator] parseV3BatchEnrichmentResponse received ${parsed.length} items but expected ${expectedLength}; extras will be discarded`);
43967
+ missing.push(`got ${parsed.length} items but only first ${expectedLength} will be used; extras discarded`);
43968
+ }
43969
+ return { fields, missing };
43970
+ }
43971
+ async function enrichLessonsToV3Batched(params) {
43972
+ const quota = params.quota ?? { maxCalls: 10, window: "utc" };
43973
+ const out = Array.from({ length: params.lessons.length }, () => null);
43974
+ const batchSize = params.batchSize ?? ENRICHMENT_BATCH_SIZE;
43975
+ for (let start = 0;start < params.lessons.length; start += batchSize) {
43976
+ const batch = params.lessons.slice(start, start + batchSize);
43977
+ const prompt = buildV3BatchEnrichmentPrompt(batch);
43978
+ let userInput = prompt;
43979
+ let best = Array.from({ length: batch.length }, () => null);
43980
+ let retryHint = "";
43981
+ for (let attempt = 0;attempt < 2; attempt++) {
43982
+ try {
43983
+ const reservation = await reserveQuota(params.directory, {
43984
+ nCalls: 1,
43985
+ maxCalls: quota.maxCalls,
43986
+ window: quota.window,
43987
+ scope: "knowledge-enrichment"
43988
+ });
43989
+ if (!reservation.allowed)
43990
+ break;
43991
+ const response = await params.llmDelegate("", userInput, AbortSignal.timeout(ENRICHMENT_LLM_TIMEOUT_MS));
43992
+ const parsed = parseV3BatchEnrichmentResponse(response, batch.length);
43993
+ best = best.map((current, idx) => current ?? parsed.fields[idx]);
43994
+ const unresolved = best.map((fields, idx) => ({ fields, idx })).filter((item) => item.fields === null).map((item) => item.idx + 1);
43995
+ if (unresolved.length === 0)
43996
+ break;
43997
+ retryHint = parsed.missing.join("; ");
43998
+ const resolvedList = best.map((fields, idx) => ({ fields, idx })).filter((item) => item.fields !== null).map((item) => item.idx + 1);
43999
+ const preserveClause = resolvedList.length > 0 ? `Preserve the already-valid entries for items ${resolvedList.join(", ")} exactly as you returned them previously. ` : "";
44000
+ userInput = `${prompt}
44001
+
44002
+ RETRY: your last output still missed valid directives for items ${unresolved.join(", ")}. ${retryHint} ${preserveClause}Return a full JSON array with valid entries for every item.`;
44003
+ } catch (err) {
44004
+ warn(`[knowledge-curator] v3 batch enrichment attempt ${attempt + 1} failed: ${err instanceof Error ? err.message : String(err)}`);
44005
+ }
44006
+ }
44007
+ for (let i = 0;i < best.length; i++) {
44008
+ out[start + i] = best[i];
44009
+ }
44010
+ }
44011
+ return out;
44012
+ }
43870
44013
  async function enrichLessonToV3(params) {
43871
44014
  const quota = params.quota ?? { maxCalls: 10, window: "utc" };
43872
44015
  const prompt = buildV3EnrichmentPrompt(params.lesson, params.category, params.tags);
@@ -44004,6 +44147,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
44004
44147
  const snapshotPlusNew = [...snapshot];
44005
44148
  const toAdd = [];
44006
44149
  const pendingReinforcementIds = new Set;
44150
+ const pendingBatchEnrichment = [];
44007
44151
  for (const lesson of lessons) {
44008
44152
  const tags = inferTags(lesson);
44009
44153
  let category = "process";
@@ -44066,20 +44210,10 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
44066
44210
  project_name: projectName,
44067
44211
  auto_generated: true
44068
44212
  };
44069
- let actionability = validateActionability(entry);
44213
+ const actionability = validateActionability(entry);
44070
44214
  if (!actionability.actionable && options?.llmDelegate) {
44071
- const enriched = await enrichLessonToV3({
44072
- directory,
44073
- llmDelegate: options.llmDelegate,
44074
- lesson,
44075
- category,
44076
- tags,
44077
- quota: options.enrichmentQuota
44078
- });
44079
- if (enriched) {
44080
- Object.assign(entry, enriched);
44081
- actionability = validateActionability(entry);
44082
- }
44215
+ pendingBatchEnrichment.push({ entry, lesson, category, tags });
44216
+ continue;
44083
44217
  }
44084
44218
  if (!actionability.actionable) {
44085
44219
  quarantined++;
@@ -44096,6 +44230,41 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
44096
44230
  toAdd.push(entry);
44097
44231
  snapshotPlusNew.push(entry);
44098
44232
  }
44233
+ if (pendingBatchEnrichment.length > 0 && options?.llmDelegate) {
44234
+ const enrichedBatch = await enrichLessonsToV3Batched({
44235
+ directory,
44236
+ llmDelegate: options.llmDelegate,
44237
+ lessons: pendingBatchEnrichment.map((item) => ({
44238
+ lesson: item.lesson,
44239
+ category: item.category,
44240
+ tags: item.tags
44241
+ })),
44242
+ quota: options.enrichmentQuota,
44243
+ batchSize: config3.enrichment?.batch_size
44244
+ });
44245
+ for (let i = 0;i < pendingBatchEnrichment.length; i++) {
44246
+ const pending = pendingBatchEnrichment[i];
44247
+ const enriched = enrichedBatch[i];
44248
+ if (enriched) {
44249
+ Object.assign(pending.entry, enriched);
44250
+ }
44251
+ const actionability = validateActionability(pending.entry);
44252
+ if (!actionability.actionable) {
44253
+ quarantined++;
44254
+ try {
44255
+ await appendUnactionable(directory, pending.entry, actionability.reason ?? "unactionable");
44256
+ } catch {}
44257
+ await appendCuratorSkippedEvent(directory, {
44258
+ entry_id: pending.entry.id,
44259
+ lesson: pending.lesson,
44260
+ reason: actionability.reason ?? "unactionable"
44261
+ });
44262
+ continue;
44263
+ }
44264
+ toAdd.push(pending.entry);
44265
+ snapshotPlusNew.push(pending.entry);
44266
+ }
44267
+ }
44099
44268
  try {
44100
44269
  const insights = await consumeInsightCandidates(directory);
44101
44270
  for (const cand of insights) {
@@ -44299,7 +44468,7 @@ function createKnowledgeCuratorHook(directory, config3, options = {}) {
44299
44468
  };
44300
44469
  return safeHook(handler);
44301
44470
  }
44302
- var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals20;
44471
+ var seenRetroSections, MAX_TRACKED_RETRO_SECTIONS = 500, ENRICHMENT_ALLOWED_FIELDS, ENRICHMENT_LLM_TIMEOUT_MS = 60000, ENRICHMENT_BATCH_SIZE = 6, MESO_INSIGHT_BATCH_LIMIT = 20, KNOWLEDGE_CATEGORIES, OUTCOME_PROMOTION_BLOCK = -0.3, _internals20;
44303
44472
  var init_knowledge_curator = __esm(() => {
44304
44473
  init_skill_improver_quota();
44305
44474
  init_synonym_map();
@@ -592,6 +592,7 @@ export declare const KnowledgeConfigSchema: z.ZodObject<{
592
592
  utc: "utc";
593
593
  local: "local";
594
594
  }>>;
595
+ batch_size: z.ZodOptional<z.ZodNumber>;
595
596
  }, z.core.$strip>>;
596
597
  }, z.core.$strip>;
597
598
  export type KnowledgeConfig = z.infer<typeof KnowledgeConfigSchema>;
@@ -1597,6 +1598,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
1597
1598
  utc: "utc";
1598
1599
  local: "local";
1599
1600
  }>>;
1601
+ batch_size: z.ZodOptional<z.ZodNumber>;
1600
1602
  }, z.core.$strip>>;
1601
1603
  }, z.core.$strip>>;
1602
1604
  memory: z.ZodOptional<z.ZodObject<{
@@ -35,6 +35,8 @@ export declare function parseV3EnrichmentResponse(text: string): {
35
35
  } | {
36
36
  missing: string[];
37
37
  };
38
+ /** Max lessons enriched per LLM call (batch mode). */
39
+ export declare const ENRICHMENT_BATCH_SIZE = 6;
38
40
  export interface EnrichmentQuotaOptions {
39
41
  maxCalls: number;
40
42
  window: 'utc' | 'local';
@@ -216,6 +216,7 @@ export interface KnowledgeConfig {
216
216
  enrichment: {
217
217
  max_calls_per_day: number;
218
218
  quota_window: 'utc' | 'local';
219
+ batch_size?: number;
219
220
  };
220
221
  }
221
222
  export interface MessageInfo {