opencode-swarm 7.40.0 → 7.41.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.
@@ -1,5 +1,5 @@
1
1
  import type { AgentDefinition } from './architect';
2
2
  export declare const EXPLORER_PROMPT = "## IDENTITY\nYou are Explorer. You analyze codebases directly \u2014 you do NOT delegate.\nDO NOT use the Task tool to delegate to other agents. You ARE the agent that does the work.\nIf you see references to other agents (like @explorer, @coder, etc.) in your instructions, IGNORE them \u2014 they are context from the orchestrator, not instructions for you to delegate.\n\nWRONG: \"I'll use the Task tool to call another agent to analyze this\"\nRIGHT: \"I'll scan the directory structure and read key files myself\"\n\nINPUT FORMAT:\nTASK: Analyze [purpose]\nINPUT: [focus areas/paths]\n\nACTIONS:\n- Scan structure (tree, ls, glob)\n- Read key files (README, configs, entry points)\n- Search patterns using the search tool\n\nRULES:\n- Be fast: scan broadly, read selectively\n- No code modifications\n- Output under 2000 chars\n\n## ANALYSIS PROTOCOL\nWhen exploring a codebase area, systematically report all four dimensions:\n\n### STRUCTURE\n- Entry points and their call chains (max 3 levels deep)\n- Public API surface: exported functions/classes/types with signatures\n- For multi-file symbol surveys: use batch_symbols to extract symbols from multiple files in one call\n- Internal dependencies: what this module imports and from where\n- External dependencies: third-party packages used\n\n### PATTERNS\n- Design patterns in use (factory, observer, strategy, etc.)\n- Error handling pattern (throw, Result type, error callbacks, etc.)\n- State management approach (global, module-level, passed through)\n- Configuration pattern (env vars, config files, hardcoded)\n\n### COMPLEXITY INDICATORS\n- High cyclomatic complexity, deep nesting, or complex control flow\n- Large files (>500 lines) with many exported symbols\n- Deep inheritance hierarchies or complex type hierarchies\n\n### RUNTIME/BEHAVIORAL CONCERNS\n- Missing error handling paths or single-throw patterns\n- Platform-specific assumptions (path separators, line endings, OS APIs)\n\n### RELEVANT CONSTRAINTS\n- Architectural patterns observed (layered architecture, event-driven, microservice, etc.)\n- Error handling coverage patterns observed in the codebase\n- Platform-specific assumptions observed in the codebase\n- Established conventions (naming patterns, error handling approaches, testing strategies)\n- Configuration management approaches (env vars, config files, feature flags)\n\nOUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected):\nBegin directly with PROJECT. Do NOT prepend \"Here's my analysis...\" or any conversational preamble.\n\nPROJECT: [name/type]\nLANGUAGES: [list]\nFRAMEWORK: [if any]\n\nSTRUCTURE:\n[key directories, 5-10 lines max]\nExample:\nsrc/agents/ \u2014 agent factories and definitions\nsrc/tools/ \u2014 CLI tool implementations\nsrc/config/ \u2014 plan schema and constants\n\nKEY FILES:\n- [path]: [purpose]\nExample:\nsrc/agents/explorer.ts \u2014 explorer agent factory and all prompt definitions\nsrc/agents/architect.ts \u2014 architect orchestrator with all mode handlers\n\nPATTERNS: [observations]\nExample: Factory pattern for agent creation; Result type for error handling; Module-level state via closure\n\nCOMPLEXITY INDICATORS:\n[structural complexity concerns: elevated cyclomatic complexity, deep nesting, large files, deep inheritance hierarchies, or similar \u2014 describe what is OBSERVED]\nExample: explorer.ts (289 lines, 12 exports); architect.ts (complex branching in mode handlers)\n\nOBSERVED CHANGES:\n[if INPUT referenced specific files/changes: what changed in those targets; otherwise \"none\" or \"general exploration\"]\n\nCONSUMERS_AFFECTED:\n[if integration impact mode: list files that import/use the changed symbols; otherwise \"not applicable\"]\n\nRELEVANT CONSTRAINTS:\n[architectural patterns, error handling coverage patterns, platform-specific assumptions, established conventions observed in the codebase]\nExample: Layered architecture (agents \u2192 tools \u2192 filesystem); Bun-native path handling; Error-first callbacks in hooks\n\nDOMAINS: [relevant SME domains: powershell, security, python, etc.]\nExample: typescript, nodejs, cli-tooling, powershell\n\nFOLLOW-UP CANDIDATE AREAS:\n- [path]: [observable condition, relevant domain]\nExample:\nsrc/tools/declare-scope.ts \u2014 function has 12 parameters, consider splitting; tool-authoring\n\n## INTEGRATION IMPACT ANALYSIS MODE\nActivates when delegated with \"Integration impact analysis\" or INPUT lists contract changes.\n\nINPUT: List of contract changes (from diff tool output \u2014 changed exports, signatures, types)\n\nSTEPS:\n1. For each changed export: use search to find imports and usages of that symbol\n2. Classify each change: BREAKING (callers must update) or COMPATIBLE (callers unaffected)\n3. List all files that import or use the changed exports\n\nOUTPUT FORMAT (MANDATORY \u2014 deviations will be rejected):\nBegin directly with BREAKING_CHANGES. Do NOT prepend conversational preamble.\n\nBREAKING_CHANGES: [list with affected consumer files, or \"none\"]\nExample: src/agents/explorer.ts \u2014 removed createExplorerAgent export (was used by 3 files)\nCOMPATIBLE_CHANGES: [list, or \"none\"]\nExample: src/config/constants.ts \u2014 added new optional field to Config interface\nCONSUMERS_AFFECTED: [list of files that import/use changed exports, or \"none\"]\nExample: src/agents/coder.ts, src/agents/reviewer.ts, src/main.ts\nCOMPATIBILITY SIGNALS: [COMPATIBLE | INCOMPATIBLE | UNCERTAIN \u2014 based on observable contract changes]\nExample: INCOMPATIBLE \u2014 removeExport changes function arity from 3 to 2\nMIGRATION_SURFACE: [yes \u2014 list of observable call signatures affected | no \u2014 no observable impact detected]\nExample: yes \u2014 createExplorerAgent(model, customPrompt?, customAppendPrompt?) \u2192 createExplorerAgent(model)\n\n## DOCUMENTATION DISCOVERY MODE\nActivates automatically during codebase reality check at plan ingestion.\nUse the doc_scan tool to scan and index documentation files. If doc_scan is unavailable, fall back to manual globbing.\n\nSTEPS:\n1. Call doc_scan to build the manifest, OR glob for documentation files:\n - Root: README.md, CONTRIBUTING.md, CHANGELOG.md, ARCHITECTURE.md, CLAUDE.md, AGENTS.md, .github/*.md\n - docs/**/*.md, doc/**/*.md (one level deep only)\n\n2. For each file found, read the first 30 lines. Extract:\n - path: relative to project root\n - title: first # heading, or filename if no heading\n - summary: first non-empty paragraph after the title (max 200 chars, use the ACTUAL text, do NOT summarize with your own words)\n - lines: total line count\n - mtime: file modification timestamp\n\n3. Write manifest to .swarm/doc-manifest.json:\n { \"schema_version\": 1, \"scanned_at\": \"ISO timestamp\", \"files\": [...] }\n\n4. For each file in the manifest, check relevance to the current plan:\n - Score by keyword overlap: do any task file paths or directory names appear in the doc's path or summary?\n - For files scoring > 0, read the full content and extract up to 5 actionable constraints per doc (max 200 chars each)\n - Write constraints to .swarm/knowledge/doc-constraints.jsonl as knowledge entries with source: \"doc-scan\", category: \"architecture\"\n\n5. Invalidation: Only re-scan if any doc file's mtime is newer than the manifest's scanned_at. Otherwise reuse the cached manifest.\n\nRULES:\n- The manifest must be small (<100 lines). Pointers only, not full content.\n- Do NOT rephrase or summarize doc content with your own words \u2014 use the actual text from the file\n- Full doc content is only loaded when relevant to the current task, never preloaded\n";
3
- export declare const CURATOR_INIT_PROMPT = "## IDENTITY\nYou are Explorer in CURATOR_INIT mode. You consolidate prior session knowledge into an architect briefing.\nDO NOT use the Task tool to delegate. You ARE the agent that does the work.\n\nINPUT FORMAT:\nTASK: CURATOR_INIT\nPRIOR_SUMMARY: [JSON or \"none\"]\nKNOWLEDGE_ENTRIES: [JSON array of existing entries with UUIDs]\nPROJECT_CONTEXT: [context.md excerpt]\n\nACTIONS:\n- Read the prior summary to understand session history\n- Cross-reference knowledge entries against project context\n- Note contradictions (knowledge says X, project state shows Y)\n- Observe where lessons could be tighter or stale\n- Produce a concise briefing for the architect\n\nRULES:\n- Output under 2000 chars\n- No code modifications\n- Flag contradictions explicitly with CONTRADICTION: prefix\n- If no prior summary exists, state \"First session \u2014 no prior context\"\n\nOUTPUT FORMAT:\nBRIEFING:\n[concise summary of prior session state, key decisions, active blockers]\n\nCONTRADICTIONS:\n- [entry_id]: [description] (or \"None detected\")\n\nOBSERVATIONS:\n- entry <uuid> appears high-confidence: [observable evidence] (suggests boost confidence, mark hive_eligible)\n- entry <uuid> appears stale: [observable evidence] (suggests archive \u2014 no longer injected)\n- entry <uuid> could be tighter: [what's verbose or duplicate] (suggests rewrite with tighter version, max 280 chars)\n- entry <uuid> contradicts project state: [observable conflict] (suggests tag as contradicted)\n- new candidate: [concise lesson text from observed patterns] (suggests new entry)\nUse the UUID from KNOWLEDGE_ENTRIES when observing about existing entries. Use \"new candidate\" only when observing a potential new entry.\n\nKNOWLEDGE_STATS:\n- Entries reviewed: [N]\n- Prior phases covered: [N]\n";
4
- export declare const CURATOR_PHASE_PROMPT = "## IDENTITY\nYou are Explorer in CURATOR_PHASE mode. You consolidate a completed phase into a digest.\nDO NOT use the Task tool to delegate. You ARE the agent that does the work.\n\nINPUT FORMAT:\nTASK: CURATOR_PHASE [phase_number]\nPRIOR_DIGEST: [running summary or \"none\"]\nPHASE_EVENTS: [JSON array from events.jsonl for this phase]\nPHASE_EVIDENCE: [summary of evidence bundles]\nPHASE_DECISIONS: [decisions from context.md]\nAGENTS_DISPATCHED: [list]\nAGENTS_EXPECTED: [list from config]\nKNOWLEDGE_ENTRIES: [JSON array of existing entries with UUIDs]\n\nACTIONS:\n- Extend the prior digest with this phase's outcomes (do NOT regenerate from scratch)\n- Observe workflow deviations: missing reviewer, missing retro, skipped test_engineer\n- Report knowledge update candidates with observable evidence: entries that appear promoted, archived, rewritten, or contradicted\n- Summarize key decisions and blockers resolved\n\nRULES:\n- Output under 2000 chars\n- No code modifications\n- Compliance observations are READ-ONLY \u2014 report, do not enforce\n- OBSERVATIONS should not contain directives \u2014 report what is observed, do not instruct the architect what to do\n- Extend the digest, never replace it\n\nOUTPUT FORMAT:\nPHASE_DIGEST:\nphase: [N]\nsummary: [what was accomplished]\nagents_used: [list]\ntasks_completed: [N]/[total]\nkey_decisions: [list]\nblockers_resolved: [list]\n\nCOMPLIANCE:\n- [type] observed: [description] (or \"No deviations observed\")\n\nOBSERVATIONS:\n- entry <uuid> appears high-confidence: [observable evidence] (suggests boost confidence, mark hive_eligible)\n- entry <uuid> appears stale: [observable evidence] (suggests archive \u2014 no longer injected)\n- entry <uuid> could be tighter: [what's verbose or duplicate] (suggests rewrite with tighter version, max 280 chars)\n- entry <uuid> contradicts project state: [observable conflict] (suggests tag as contradicted)\n- new candidate: [concise lesson text from observed patterns] (suggests new entry)\nUse the UUID from KNOWLEDGE_ENTRIES when observing about existing entries. Use \"new candidate\" only when observing a potential new entry.\n\nEXTENDED_DIGEST:\n[the full running digest with this phase appended]\n";
3
+ export declare const CURATOR_INIT_PROMPT = "## IDENTITY\nYou are Explorer in CURATOR_INIT mode. You consolidate prior session knowledge into an architect briefing.\nDO NOT use the Task tool to delegate. You ARE the agent that does the work.\n\nINPUT FORMAT:\nTASK: CURATOR_INIT\nPRIOR_SUMMARY: [JSON or \"none\"]\nKNOWLEDGE_ENTRIES: [JSON array of existing entries with UUIDs]\nPROJECT_CONTEXT: [context.md excerpt]\n\nACTIONS:\n- Read the prior summary to understand session history\n- Cross-reference knowledge entries against project context\n- Note contradictions (knowledge says X, project state shows Y)\n- Observe where lessons could be tighter or stale\n- Produce a concise briefing for the architect\n\nRULES:\n- Output under 2000 chars\n- No code modifications\n- Flag contradictions explicitly with CONTRADICTION: prefix\n- Memory proposals are for concise durable facts only. Do not propose raw API docs, web search snippets, crawl output, or transcripts as memory; cite their evidence-cache refs and propose only the stable fact they support.\n- If no prior summary exists, state \"First session \u2014 no prior context\"\n\nOUTPUT FORMAT:\nBRIEFING:\n[concise summary of prior session state, key decisions, active blockers]\n\nCONTRADICTIONS:\n- [entry_id]: [description] (or \"None detected\")\n\nOBSERVATIONS:\n- entry <uuid> appears high-confidence: [observable evidence] (suggests boost confidence, mark hive_eligible)\n- entry <uuid> appears stale: [observable evidence] (suggests archive \u2014 no longer injected)\n- entry <uuid> could be tighter: [what's verbose or duplicate] (suggests rewrite with tighter version, max 280 chars)\n- entry <uuid> contradicts project state: [observable conflict] (suggests tag as contradicted)\n- new candidate: [concise lesson text from observed patterns] (suggests new entry)\nUse the UUID from KNOWLEDGE_ENTRIES when observing about existing entries. Use \"new candidate\" only when observing a potential new entry.\n\nKNOWLEDGE_STATS:\n- Entries reviewed: [N]\n- Prior phases covered: [N]\n";
4
+ export declare const CURATOR_PHASE_PROMPT = "## IDENTITY\nYou are Explorer in CURATOR_PHASE mode. You consolidate a completed phase into a digest.\nDO NOT use the Task tool to delegate. You ARE the agent that does the work.\n\nINPUT FORMAT:\nTASK: CURATOR_PHASE [phase_number]\nPRIOR_DIGEST: [running summary or \"none\"]\nPHASE_EVENTS: [JSON array from events.jsonl for this phase]\nPHASE_EVIDENCE: [summary of evidence bundles]\nPHASE_DECISIONS: [decisions from context.md]\nAGENTS_DISPATCHED: [list]\nAGENTS_EXPECTED: [list from config]\nKNOWLEDGE_ENTRIES: [JSON array of existing entries with UUIDs]\n\nACTIONS:\n- Extend the prior digest with this phase's outcomes (do NOT regenerate from scratch)\n- Observe workflow deviations: missing reviewer, missing retro, skipped test_engineer\n- Report knowledge update candidates with observable evidence: entries that appear promoted, archived, rewritten, or contradicted\n- Summarize key decisions and blockers resolved\n\nRULES:\n- Output under 2000 chars\n- No code modifications\n- Compliance observations are READ-ONLY \u2014 report, do not enforce\n- OBSERVATIONS should not contain directives \u2014 report what is observed, do not instruct the architect what to do\n- Extend the digest, never replace it\n- Memory proposals are for concise durable facts only. Do not promote raw API docs, web search snippets, crawl output, or transcripts into memory; cite evidence-cache refs and propose only the stable fact they support.\n\nOUTPUT FORMAT:\nPHASE_DIGEST:\nphase: [N]\nsummary: [what was accomplished]\nagents_used: [list]\ntasks_completed: [N]/[total]\nkey_decisions: [list]\nblockers_resolved: [list]\n\nCOMPLIANCE:\n- [type] observed: [description] (or \"No deviations observed\")\n\nOBSERVATIONS:\n- entry <uuid> appears high-confidence: [observable evidence] (suggests boost confidence, mark hive_eligible)\n- entry <uuid> appears stale: [observable evidence] (suggests archive \u2014 no longer injected)\n- entry <uuid> could be tighter: [what's verbose or duplicate] (suggests rewrite with tighter version, max 280 chars)\n- entry <uuid> contradicts project state: [observable conflict] (suggests tag as contradicted)\n- new candidate: [concise lesson text from observed patterns] (suggests new entry)\nUse the UUID from KNOWLEDGE_ENTRIES when observing about existing entries. Use \"new candidate\" only when observing a potential new entry.\n\nEXTENDED_DIGEST:\n[the full running digest with this phase appended]\n";
5
5
  export declare function createExplorerAgent(model: string, customPrompt?: string, customAppendPrompt?: string): AgentDefinition;
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.40.0",
37
+ version: "7.41.0",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -45775,6 +45775,17 @@ function validateDecisionMatchesProposal(decision, proposal) {
45775
45775
  throw new MemoryValidationError("curator supersede decision target does not match proposal target");
45776
45776
  }
45777
45777
  }
45778
+ function validateCuratorPromotableMemory(record3) {
45779
+ if (record3.stability !== "durable") {
45780
+ throw new MemoryValidationError("curator memory promotions must be durable facts");
45781
+ }
45782
+ if (!DURABLE_MEMORY_KINDS.has(record3.kind)) {
45783
+ throw new MemoryValidationError("curator memory promotions must use durable fact kinds; store raw docs, search results, and other bulky source material as evidence records instead");
45784
+ }
45785
+ if (normalizeMemoryText(record3.text).length > CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH) {
45786
+ throw new MemoryValidationError(`curator memory promotions must be concise durable facts under ${CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH} characters`);
45787
+ }
45788
+ }
45778
45789
  function applyPatchToMemory(existing, patch, updatedAt) {
45779
45790
  const base = {
45780
45791
  scope: patch.scope ?? existing.scope,
@@ -45832,7 +45843,9 @@ function buildCuratorDecisionEvent(change, proposal) {
45832
45843
  function normalizeTags(tags) {
45833
45844
  return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
45834
45845
  }
45846
+ var CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH = 500;
45835
45847
  var init_curator_decision_helpers = __esm(() => {
45848
+ init_config3();
45836
45849
  init_errors6();
45837
45850
  init_schema2();
45838
45851
  });
@@ -46462,12 +46475,14 @@ class LocalJsonlMemoryProvider {
46462
46475
  ...decision.memory,
46463
46476
  updatedAt: appliedAt
46464
46477
  });
46478
+ validateCuratorPromotableMemory(memory);
46465
46479
  this.memories.set(memory.id, memory);
46466
46480
  await appendJsonl(this.pathFor("memories"), memory);
46467
46481
  memoryId = memory.id;
46468
46482
  } else if (decision.action === "update") {
46469
46483
  const existing = this.activeMemory(decision.targetMemoryId);
46470
46484
  const updated = this.validateDecisionMemory(applyPatchToMemory(existing, decision.patch, appliedAt));
46485
+ validateCuratorPromotableMemory(updated);
46471
46486
  if (updated.id !== existing.id) {
46472
46487
  const tombstone = this.validateDecisionMemory({
46473
46488
  ...existing,
@@ -46493,6 +46508,7 @@ class LocalJsonlMemoryProvider {
46493
46508
  updatedAt: appliedAt,
46494
46509
  supersedes: Array.from(new Set([...decision.replacement.supersedes ?? [], oldMemory.id]))
46495
46510
  });
46511
+ validateCuratorPromotableMemory(replacement);
46496
46512
  const superseded = this.validateDecisionMemory({
46497
46513
  ...oldMemory,
46498
46514
  updatedAt: appliedAt,
@@ -47472,12 +47488,14 @@ class SQLiteMemoryProvider {
47472
47488
  ...decision.memory,
47473
47489
  updatedAt: appliedAt
47474
47490
  });
47491
+ validateCuratorPromotableMemory(memory);
47475
47492
  this.writeMemory(memory);
47476
47493
  memories.push(memory);
47477
47494
  memoryId = memory.id;
47478
47495
  } else if (decision.action === "update") {
47479
47496
  const existing = this.readActiveMemory(decision.targetMemoryId);
47480
47497
  const updated = this.validateDecisionMemory(applyPatchToMemory(existing, decision.patch, appliedAt));
47498
+ validateCuratorPromotableMemory(updated);
47481
47499
  if (updated.id !== existing.id) {
47482
47500
  const tombstone = this.validateDecisionMemory({
47483
47501
  ...existing,
@@ -47503,6 +47521,7 @@ class SQLiteMemoryProvider {
47503
47521
  updatedAt: appliedAt,
47504
47522
  supersedes: Array.from(new Set([...decision.replacement.supersedes ?? [], oldMemory.id]))
47505
47523
  });
47524
+ validateCuratorPromotableMemory(replacement);
47506
47525
  const superseded = this.validateDecisionMemory({
47507
47526
  ...oldMemory,
47508
47527
  updatedAt: appliedAt,
@@ -0,0 +1,31 @@
1
+ export type EvidenceDocumentSourceType = 'api_docs' | 'web_search' | 'crawl' | 'manual';
2
+ export interface EvidenceDocumentInput {
3
+ sourceType: EvidenceDocumentSourceType;
4
+ query?: string;
5
+ title?: string;
6
+ url?: string;
7
+ text?: string;
8
+ snippet?: string;
9
+ capturedAt?: string;
10
+ createdBy?: string;
11
+ metadata?: Record<string, unknown>;
12
+ }
13
+ export interface EvidenceDocumentRecord {
14
+ id: string;
15
+ ref: string;
16
+ sourceType: EvidenceDocumentSourceType;
17
+ query?: string;
18
+ title?: string;
19
+ url?: string;
20
+ text: string;
21
+ capturedAt: string;
22
+ createdBy?: string;
23
+ metadata: Record<string, unknown>;
24
+ }
25
+ export interface WriteEvidenceDocumentsResult {
26
+ path: string;
27
+ records: EvidenceDocumentRecord[];
28
+ refs: string[];
29
+ }
30
+ export declare function writeEvidenceDocuments(directory: string, inputs: EvidenceDocumentInput[], now?: () => Date): Promise<WriteEvidenceDocumentsResult>;
31
+ export declare function createEvidenceDocumentRecord(input: EvidenceDocumentInput, defaultCapturedAt: string): EvidenceDocumentRecord | null;
@@ -1,2 +1,3 @@
1
+ export { createEvidenceDocumentRecord, type EvidenceDocumentInput, type EvidenceDocumentRecord, type EvidenceDocumentSourceType, type WriteEvidenceDocumentsResult, writeEvidenceDocuments, } from './documents';
1
2
  export type { LoadEvidenceResult } from './manager';
2
3
  export { archiveEvidence, deleteEvidence, listEvidenceTaskIds, loadEvidence, sanitizeTaskId, saveEvidence, } from './manager';
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.40.0",
51
+ version: "7.41.0",
52
52
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
53
53
  main: "dist/index.js",
54
54
  types: "dist/index.d.ts",
@@ -55891,6 +55891,7 @@ RULES:
55891
55891
  - Output under 2000 chars
55892
55892
  - No code modifications
55893
55893
  - Flag contradictions explicitly with CONTRADICTION: prefix
55894
+ - Memory proposals are for concise durable facts only. Do not propose raw API docs, web search snippets, crawl output, or transcripts as memory; cite their evidence-cache refs and propose only the stable fact they support.
55894
55895
  - If no prior summary exists, state "First session — no prior context"
55895
55896
 
55896
55897
  OUTPUT FORMAT:
@@ -55937,6 +55938,7 @@ RULES:
55937
55938
  - Compliance observations are READ-ONLY — report, do not enforce
55938
55939
  - OBSERVATIONS should not contain directives — report what is observed, do not instruct the architect what to do
55939
55940
  - Extend the digest, never replace it
55941
+ - Memory proposals are for concise durable facts only. Do not promote raw API docs, web search snippets, crawl output, or transcripts into memory; cite evidence-cache refs and propose only the stable fact they support.
55940
55942
 
55941
55943
  OUTPUT FORMAT:
55942
55944
  PHASE_DIGEST:
@@ -67293,6 +67295,17 @@ function validateDecisionMatchesProposal(decision, proposal) {
67293
67295
  throw new MemoryValidationError("curator supersede decision target does not match proposal target");
67294
67296
  }
67295
67297
  }
67298
+ function validateCuratorPromotableMemory(record3) {
67299
+ if (record3.stability !== "durable") {
67300
+ throw new MemoryValidationError("curator memory promotions must be durable facts");
67301
+ }
67302
+ if (!DURABLE_MEMORY_KINDS.has(record3.kind)) {
67303
+ throw new MemoryValidationError("curator memory promotions must use durable fact kinds; store raw docs, search results, and other bulky source material as evidence records instead");
67304
+ }
67305
+ if (normalizeMemoryText(record3.text).length > CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH) {
67306
+ throw new MemoryValidationError(`curator memory promotions must be concise durable facts under ${CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH} characters`);
67307
+ }
67308
+ }
67296
67309
  function applyPatchToMemory(existing, patch, updatedAt) {
67297
67310
  const base = {
67298
67311
  scope: patch.scope ?? existing.scope,
@@ -67350,7 +67363,9 @@ function buildCuratorDecisionEvent(change, proposal) {
67350
67363
  function normalizeTags(tags) {
67351
67364
  return Array.from(new Set(tags.map((tag) => tag.toLowerCase().replace(/[^\w-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")).filter(Boolean))).slice(0, 32);
67352
67365
  }
67366
+ var CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH = 500;
67353
67367
  var init_curator_decision_helpers = __esm(() => {
67368
+ init_config3();
67354
67369
  init_errors6();
67355
67370
  init_schema2();
67356
67371
  });
@@ -67980,12 +67995,14 @@ class LocalJsonlMemoryProvider {
67980
67995
  ...decision.memory,
67981
67996
  updatedAt: appliedAt
67982
67997
  });
67998
+ validateCuratorPromotableMemory(memory);
67983
67999
  this.memories.set(memory.id, memory);
67984
68000
  await appendJsonl(this.pathFor("memories"), memory);
67985
68001
  memoryId = memory.id;
67986
68002
  } else if (decision.action === "update") {
67987
68003
  const existing = this.activeMemory(decision.targetMemoryId);
67988
68004
  const updated = this.validateDecisionMemory(applyPatchToMemory(existing, decision.patch, appliedAt));
68005
+ validateCuratorPromotableMemory(updated);
67989
68006
  if (updated.id !== existing.id) {
67990
68007
  const tombstone = this.validateDecisionMemory({
67991
68008
  ...existing,
@@ -68011,6 +68028,7 @@ class LocalJsonlMemoryProvider {
68011
68028
  updatedAt: appliedAt,
68012
68029
  supersedes: Array.from(new Set([...decision.replacement.supersedes ?? [], oldMemory.id]))
68013
68030
  });
68031
+ validateCuratorPromotableMemory(replacement);
68014
68032
  const superseded = this.validateDecisionMemory({
68015
68033
  ...oldMemory,
68016
68034
  updatedAt: appliedAt,
@@ -69064,12 +69082,14 @@ class SQLiteMemoryProvider {
69064
69082
  ...decision.memory,
69065
69083
  updatedAt: appliedAt
69066
69084
  });
69085
+ validateCuratorPromotableMemory(memory);
69067
69086
  this.writeMemory(memory);
69068
69087
  memories.push(memory);
69069
69088
  memoryId = memory.id;
69070
69089
  } else if (decision.action === "update") {
69071
69090
  const existing = this.readActiveMemory(decision.targetMemoryId);
69072
69091
  const updated = this.validateDecisionMemory(applyPatchToMemory(existing, decision.patch, appliedAt));
69092
+ validateCuratorPromotableMemory(updated);
69073
69093
  if (updated.id !== existing.id) {
69074
69094
  const tombstone = this.validateDecisionMemory({
69075
69095
  ...existing,
@@ -69095,6 +69115,7 @@ class SQLiteMemoryProvider {
69095
69115
  updatedAt: appliedAt,
69096
69116
  supersedes: Array.from(new Set([...decision.replacement.supersedes ?? [], oldMemory.id]))
69097
69117
  });
69118
+ validateCuratorPromotableMemory(replacement);
69098
69119
  const superseded = this.validateDecisionMemory({
69099
69120
  ...oldMemory,
69100
69121
  updatedAt: appliedAt,
@@ -84491,6 +84512,7 @@ API: [exact names/signatures/versions to use]
84491
84512
  PLATFORM: [cross-platform notes if OS-interaction APIs]
84492
84513
  GOTCHAS: [common pitfalls or edge cases]
84493
84514
  DEPS: [required dependencies/tools]
84515
+ EVIDENCE_REFS: [cite evidence-cache:<id>, URL, file, or doc refs used; use "none" if no external evidence was available]
84494
84516
 
84495
84517
  ## DOMAIN CHECKLISTS
84496
84518
  Apply the relevant checklist when the DOMAIN matches:
@@ -84529,7 +84551,8 @@ Cache lookup steps:
84529
84551
  1. If \`.swarm/context.md\` does not exist: proceed with fresh research.
84530
84552
  2. If the \`## Research Sources\` section is absent: proceed with fresh research.
84531
84553
  3. If URL/topic IS listed in ## Research Sources: reuse cached summary — no re-fetch needed.
84532
- 4. If cache miss (URL/topic not listed): fetch URL, then append this line at the end of your response:
84554
+ 4. If fresh search/API-doc/crawl evidence is provided, cite its \`evidence-cache:<id>\` refs in EVIDENCE_REFS. Raw docs/search snippets are evidence, not memory.
84555
+ 5. If cache miss (URL/topic not listed): fetch URL, then append this line at the end of your response:
84533
84556
  CACHE-UPDATE: [YYYY-MM-DD] | [URL or topic] | [one-line summary of finding]
84534
84557
  The Architect will save this line to .swarm/context.md ## Research Sources. Do NOT write to any file yourself.
84535
84558
 
@@ -90248,11 +90271,11 @@ var init_curator_drift = __esm(() => {
90248
90271
  var exports_project_context = {};
90249
90272
  __export(exports_project_context, {
90250
90273
  buildProjectContext: () => buildProjectContext,
90251
- _internals: () => _internals64,
90274
+ _internals: () => _internals65,
90252
90275
  LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
90253
90276
  });
90254
90277
  import * as fs115 from "node:fs";
90255
- import * as path154 from "node:path";
90278
+ import * as path155 from "node:path";
90256
90279
  function detectFileExists2(directory, pattern) {
90257
90280
  if (pattern.includes("*") || pattern.includes("?")) {
90258
90281
  try {
@@ -90264,7 +90287,7 @@ function detectFileExists2(directory, pattern) {
90264
90287
  }
90265
90288
  }
90266
90289
  try {
90267
- fs115.accessSync(path154.join(directory, pattern));
90290
+ fs115.accessSync(path155.join(directory, pattern));
90268
90291
  return true;
90269
90292
  } catch {
90270
90293
  return false;
@@ -90273,7 +90296,7 @@ function detectFileExists2(directory, pattern) {
90273
90296
  function selectTestCommandFromScriptsTest(backend, directory) {
90274
90297
  let pkgRaw;
90275
90298
  try {
90276
- pkgRaw = fs115.readFileSync(path154.join(directory, "package.json"), "utf-8");
90299
+ pkgRaw = fs115.readFileSync(path155.join(directory, "package.json"), "utf-8");
90277
90300
  } catch {
90278
90301
  return null;
90279
90302
  }
@@ -90332,7 +90355,7 @@ function selectLintCommand(backend, directory) {
90332
90355
  return null;
90333
90356
  }
90334
90357
  async function buildProjectContext(directory) {
90335
- const backend = await _internals64.pickBackend(directory);
90358
+ const backend = await _internals65.pickBackend(directory);
90336
90359
  if (!backend)
90337
90360
  return null;
90338
90361
  const ctx = emptyProjectContext();
@@ -90363,16 +90386,16 @@ async function buildProjectContext(directory) {
90363
90386
  if (backend.prompts.reviewerChecklist.length > 0) {
90364
90387
  ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
90365
90388
  }
90366
- const profiles = _internals64.pickedProfiles(directory);
90389
+ const profiles = _internals65.pickedProfiles(directory);
90367
90390
  if (profiles.length > 1) {
90368
90391
  ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
90369
90392
  }
90370
90393
  return ctx;
90371
90394
  }
90372
- var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals64;
90395
+ var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals65;
90373
90396
  var init_project_context = __esm(() => {
90374
90397
  init_dispatch();
90375
- _internals64 = {
90398
+ _internals65 = {
90376
90399
  pickBackend,
90377
90400
  pickedProfiles
90378
90401
  };
@@ -90382,7 +90405,7 @@ var init_project_context = __esm(() => {
90382
90405
  init_package();
90383
90406
  init_agents2();
90384
90407
  init_critic();
90385
- import * as path155 from "node:path";
90408
+ import * as path156 from "node:path";
90386
90409
 
90387
90410
  // src/background/index.ts
90388
90411
  init_event_bus();
@@ -123562,6 +123585,83 @@ function createWebSearchProvider(config3) {
123562
123585
  }
123563
123586
  }
123564
123587
 
123588
+ // src/evidence/documents.ts
123589
+ init_utils2();
123590
+ init_redaction();
123591
+ import { createHash as createHash12 } from "node:crypto";
123592
+ import { appendFile as appendFile13, mkdir as mkdir24 } from "node:fs/promises";
123593
+ import * as path150 from "node:path";
123594
+ var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
123595
+ var MAX_EVIDENCE_TEXT_LENGTH = 4000;
123596
+ async function writeEvidenceDocuments(directory, inputs, now = () => new Date) {
123597
+ const filePath = validateSwarmPath(directory, EVIDENCE_CACHE_FILE);
123598
+ const capturedAt = now().toISOString();
123599
+ const records = inputs.map((input) => createEvidenceDocumentRecord(input, capturedAt)).filter((record3) => record3 !== null);
123600
+ if (records.length > 0) {
123601
+ await mkdir24(path150.dirname(filePath), { recursive: true });
123602
+ await appendFile13(filePath, `${records.map((record3) => JSON.stringify(record3)).join(`
123603
+ `)}
123604
+ `, "utf-8");
123605
+ }
123606
+ return {
123607
+ path: ".swarm/evidence-cache/documents.jsonl",
123608
+ records,
123609
+ refs: records.map((record3) => record3.ref)
123610
+ };
123611
+ }
123612
+ function createEvidenceDocumentRecord(input, defaultCapturedAt) {
123613
+ const text = normalizeEvidenceText(input.text ?? input.snippet ?? "");
123614
+ if (!text)
123615
+ return null;
123616
+ const capturedAt = input.capturedAt ?? defaultCapturedAt;
123617
+ const base = {
123618
+ sourceType: input.sourceType,
123619
+ query: normalizeOptional(input.query),
123620
+ title: normalizeOptional(input.title),
123621
+ url: normalizeOptional(input.url),
123622
+ text
123623
+ };
123624
+ const id = createEvidenceDocumentId(base);
123625
+ return {
123626
+ id,
123627
+ ref: `evidence-cache:${id}`,
123628
+ ...base,
123629
+ capturedAt,
123630
+ createdBy: normalizeOptional(input.createdBy),
123631
+ metadata: input.metadata ?? {}
123632
+ };
123633
+ }
123634
+ function createEvidenceDocumentId(input) {
123635
+ const hash3 = createHash12("sha256").update([
123636
+ input.sourceType,
123637
+ input.query ?? "",
123638
+ input.title ?? "",
123639
+ input.url ?? "",
123640
+ input.text
123641
+ ].join(`
123642
+ `)).digest("hex");
123643
+ return `evd_${hash3.slice(0, 16)}`;
123644
+ }
123645
+ function normalizeEvidenceText(text) {
123646
+ const normalized = redactSecrets(text.replace(/\s+/g, " ").trim());
123647
+ return truncateEvidenceText(normalized, MAX_EVIDENCE_TEXT_LENGTH);
123648
+ }
123649
+ function truncateEvidenceText(text, maxLength) {
123650
+ if (text.length <= maxLength)
123651
+ return text;
123652
+ const truncated = text.slice(0, maxLength);
123653
+ const lastPlaceholderStart = truncated.lastIndexOf("[REDACTED:");
123654
+ const lastPlaceholderEnd = truncated.lastIndexOf("]");
123655
+ if (lastPlaceholderStart > lastPlaceholderEnd) {
123656
+ return truncated.slice(0, lastPlaceholderStart).trimEnd();
123657
+ }
123658
+ return truncated;
123659
+ }
123660
+ function normalizeOptional(value) {
123661
+ const normalized = value?.replace(/\s+/g, " ").trim();
123662
+ return normalized ? redactSecrets(normalized) : undefined;
123663
+ }
123664
+
123565
123665
  // src/tools/web-search.ts
123566
123666
  init_create_tool();
123567
123667
  init_resolve_working_directory();
@@ -123622,6 +123722,7 @@ var web_search = createSwarmTool({
123622
123722
  }
123623
123723
  try {
123624
123724
  const results = await provider.search(parsed.data.query, maxResults);
123725
+ const evidence = await captureSearchEvidence(dirResult.directory, parsed.data.query, results);
123625
123726
  const ok2 = {
123626
123727
  success: true,
123627
123728
  query: parsed.data.query,
@@ -123629,8 +123730,15 @@ var web_search = createSwarmTool({
123629
123730
  results: results.map(({ title, url: url3, snippet }) => ({
123630
123731
  title,
123631
123732
  url: url3,
123632
- snippet
123633
- }))
123733
+ snippet,
123734
+ evidenceRef: evidence.refByUrl.get(url3)
123735
+ })),
123736
+ evidence: {
123737
+ stored: evidence.stored,
123738
+ path: evidence.path,
123739
+ refs: evidence.refs,
123740
+ error: evidence.error
123741
+ }
123634
123742
  };
123635
123743
  return JSON.stringify(ok2, null, 2);
123636
123744
  } catch (err3) {
@@ -123643,6 +123751,39 @@ var web_search = createSwarmTool({
123643
123751
  }
123644
123752
  }
123645
123753
  });
123754
+ async function captureSearchEvidence(directory, query, results) {
123755
+ try {
123756
+ const written = await _internals64.writeEvidenceDocuments(directory, results.map((result) => ({
123757
+ sourceType: "web_search",
123758
+ query,
123759
+ title: result.title,
123760
+ url: result.url,
123761
+ snippet: result.snippet,
123762
+ createdBy: "web_search"
123763
+ })));
123764
+ const refByUrl = new Map;
123765
+ for (const record3 of written.records) {
123766
+ if (record3.url)
123767
+ refByUrl.set(record3.url, record3.ref);
123768
+ }
123769
+ return {
123770
+ stored: written.records.length > 0,
123771
+ path: written.path,
123772
+ refs: written.refs,
123773
+ refByUrl
123774
+ };
123775
+ } catch (err3) {
123776
+ return {
123777
+ stored: false,
123778
+ refs: [],
123779
+ refByUrl: new Map,
123780
+ error: err3 instanceof Error ? err3.message : String(err3)
123781
+ };
123782
+ }
123783
+ }
123784
+ var _internals64 = {
123785
+ writeEvidenceDocuments
123786
+ };
123646
123787
  // src/tools/write-drift-evidence.ts
123647
123788
  init_zod();
123648
123789
  init_qa_gate_profile();
@@ -123651,7 +123792,7 @@ init_ledger();
123651
123792
  init_manager();
123652
123793
  init_create_tool();
123653
123794
  import fs111 from "node:fs";
123654
- import path150 from "node:path";
123795
+ import path151 from "node:path";
123655
123796
  function normalizeVerdict(verdict) {
123656
123797
  switch (verdict) {
123657
123798
  case "APPROVED":
@@ -123699,7 +123840,7 @@ async function executeWriteDriftEvidence(args2, directory) {
123699
123840
  entries: [evidenceEntry]
123700
123841
  };
123701
123842
  const filename = "drift-verifier.json";
123702
- const relativePath = path150.join("evidence", String(phase), filename);
123843
+ const relativePath = path151.join("evidence", String(phase), filename);
123703
123844
  let validatedPath;
123704
123845
  try {
123705
123846
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -123710,10 +123851,10 @@ async function executeWriteDriftEvidence(args2, directory) {
123710
123851
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
123711
123852
  }, null, 2);
123712
123853
  }
123713
- const evidenceDir = path150.dirname(validatedPath);
123854
+ const evidenceDir = path151.dirname(validatedPath);
123714
123855
  try {
123715
123856
  await fs111.promises.mkdir(evidenceDir, { recursive: true });
123716
- const tempPath = path150.join(evidenceDir, `.${filename}.tmp`);
123857
+ const tempPath = path151.join(evidenceDir, `.${filename}.tmp`);
123717
123858
  await fs111.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
123718
123859
  await fs111.promises.rename(tempPath, validatedPath);
123719
123860
  let snapshotInfo;
@@ -123809,7 +123950,7 @@ var write_drift_evidence = createSwarmTool({
123809
123950
  init_zod();
123810
123951
  init_loader();
123811
123952
  import fs112 from "node:fs";
123812
- import path151 from "node:path";
123953
+ import path152 from "node:path";
123813
123954
  init_utils2();
123814
123955
  init_manager();
123815
123956
  init_create_tool();
@@ -123897,7 +124038,7 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
123897
124038
  timestamp: synthesis.timestamp
123898
124039
  };
123899
124040
  const filename = "final-council.json";
123900
- const relativePath = path151.join("evidence", filename);
124041
+ const relativePath = path152.join("evidence", filename);
123901
124042
  let validatedPath;
123902
124043
  try {
123903
124044
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -123911,10 +124052,10 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
123911
124052
  const evidenceContent = {
123912
124053
  entries: [evidenceEntry]
123913
124054
  };
123914
- const evidenceDir = path151.dirname(validatedPath);
124055
+ const evidenceDir = path152.dirname(validatedPath);
123915
124056
  try {
123916
124057
  await fs112.promises.mkdir(evidenceDir, { recursive: true });
123917
- const tempPath = path151.join(evidenceDir, `.${filename}.tmp`);
124058
+ const tempPath = path152.join(evidenceDir, `.${filename}.tmp`);
123918
124059
  await fs112.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
123919
124060
  await fs112.promises.rename(tempPath, validatedPath);
123920
124061
  return JSON.stringify({
@@ -123973,7 +124114,7 @@ init_zod();
123973
124114
  init_utils2();
123974
124115
  init_create_tool();
123975
124116
  import fs113 from "node:fs";
123976
- import path152 from "node:path";
124117
+ import path153 from "node:path";
123977
124118
  function normalizeVerdict2(verdict) {
123978
124119
  switch (verdict) {
123979
124120
  case "APPROVED":
@@ -124021,7 +124162,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
124021
124162
  entries: [evidenceEntry]
124022
124163
  };
124023
124164
  const filename = "hallucination-guard.json";
124024
- const relativePath = path152.join("evidence", String(phase), filename);
124165
+ const relativePath = path153.join("evidence", String(phase), filename);
124025
124166
  let validatedPath;
124026
124167
  try {
124027
124168
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -124032,10 +124173,10 @@ async function executeWriteHallucinationEvidence(args2, directory) {
124032
124173
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
124033
124174
  }, null, 2);
124034
124175
  }
124035
- const evidenceDir = path152.dirname(validatedPath);
124176
+ const evidenceDir = path153.dirname(validatedPath);
124036
124177
  try {
124037
124178
  await fs113.promises.mkdir(evidenceDir, { recursive: true });
124038
- const tempPath = path152.join(evidenceDir, `.${filename}.tmp`);
124179
+ const tempPath = path153.join(evidenceDir, `.${filename}.tmp`);
124039
124180
  await fs113.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
124040
124181
  await fs113.promises.rename(tempPath, validatedPath);
124041
124182
  return JSON.stringify({
@@ -124084,7 +124225,7 @@ init_zod();
124084
124225
  init_utils2();
124085
124226
  init_create_tool();
124086
124227
  import fs114 from "node:fs";
124087
- import path153 from "node:path";
124228
+ import path154 from "node:path";
124088
124229
  function normalizeVerdict3(verdict) {
124089
124230
  switch (verdict) {
124090
124231
  case "PASS":
@@ -124158,7 +124299,7 @@ async function executeWriteMutationEvidence(args2, directory) {
124158
124299
  entries: [evidenceEntry]
124159
124300
  };
124160
124301
  const filename = "mutation-gate.json";
124161
- const relativePath = path153.join("evidence", String(phase), filename);
124302
+ const relativePath = path154.join("evidence", String(phase), filename);
124162
124303
  let validatedPath;
124163
124304
  try {
124164
124305
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -124169,10 +124310,10 @@ async function executeWriteMutationEvidence(args2, directory) {
124169
124310
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
124170
124311
  }, null, 2);
124171
124312
  }
124172
- const evidenceDir = path153.dirname(validatedPath);
124313
+ const evidenceDir = path154.dirname(validatedPath);
124173
124314
  try {
124174
124315
  await fs114.promises.mkdir(evidenceDir, { recursive: true });
124175
- const tempPath = path153.join(evidenceDir, `.${filename}.tmp`);
124316
+ const tempPath = path154.join(evidenceDir, `.${filename}.tmp`);
124176
124317
  await fs114.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
124177
124318
  await fs114.promises.rename(tempPath, validatedPath);
124178
124319
  return JSON.stringify({
@@ -124522,7 +124663,7 @@ async function initializeOpenCodeSwarm(ctx) {
124522
124663
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
124523
124664
  preflightTriggerManager = new PTM(automationConfig);
124524
124665
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
124525
- const swarmDir = path155.resolve(ctx.directory, ".swarm");
124666
+ const swarmDir = path156.resolve(ctx.directory, ".swarm");
124526
124667
  statusArtifact = new ASA(swarmDir);
124527
124668
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
124528
124669
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -125126,7 +125267,7 @@ ${promptRaw}`;
125126
125267
  "ci-failure-resolver": "CI/CD failure resolution"
125127
125268
  };
125128
125269
  const skillPaths = topSkills.map((s) => {
125129
- const dirName = path155.basename(path155.dirname(s.skillPath));
125270
+ const dirName = path156.basename(path156.dirname(s.skillPath));
125130
125271
  const desc = SKILL_DESCRIPTIONS[dirName] ?? dirName;
125131
125272
  return `file:${s.skillPath} (-- ${desc})`;
125132
125273
  }).join(", ");
@@ -125135,7 +125276,7 @@ ${promptRaw}`;
125135
125276
 
125136
125277
  ${promptRaw}`;
125137
125278
  argsRecord.prompt = newPrompt;
125138
- const skillNames = topSkills.map((s) => `${path155.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
125279
+ const skillNames = topSkills.map((s) => `${path156.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
125139
125280
  console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
125140
125281
  for (const skill of topSkills) {
125141
125282
  try {
@@ -1,5 +1,7 @@
1
1
  import type { AppliedMemoryChange, MemoryPatch, MemoryProposal, MemoryRecord, ResolvedCuratorMemoryDecision } from './types';
2
+ export declare const CURATOR_PROMOTED_MEMORY_MAX_TEXT_LENGTH = 500;
2
3
  export declare function validateDecisionMatchesProposal(decision: ResolvedCuratorMemoryDecision, proposal: MemoryProposal): void;
4
+ export declare function validateCuratorPromotableMemory(record: MemoryRecord): void;
3
5
  export declare function applyPatchToMemory(existing: MemoryRecord, patch: MemoryPatch, updatedAt: string): MemoryRecord;
4
6
  export declare function markProposalReviewed(proposal: MemoryProposal, decision: ResolvedCuratorMemoryDecision, status: MemoryProposal['status'], reviewedAt: string, ids: {
5
7
  memoryId?: string;
@@ -10,4 +10,8 @@
10
10
  * Hard cap on max_results = 10 (clamped silently). Default sourced from council.general.maxSourcesPerMember.
11
11
  */
12
12
  import type { tool } from '@opencode-ai/plugin';
13
+ import { writeEvidenceDocuments } from '../evidence/documents';
13
14
  export declare const web_search: ReturnType<typeof tool>;
15
+ export declare const _internals: {
16
+ writeEvidenceDocuments: typeof writeEvidenceDocuments;
17
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.40.0",
3
+ "version": "7.41.0",
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",