opencode-swarm 7.63.0 → 7.64.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.
@@ -213,9 +213,9 @@ export declare const MemoryProposalSchema: z.ZodObject<{
213
213
  rationale: z.ZodString;
214
214
  evidenceRefs: z.ZodArray<z.ZodString>;
215
215
  status: z.ZodEnum<{
216
- approved: "approved";
217
- rejected: "rejected";
218
216
  pending: "pending";
217
+ rejected: "rejected";
218
+ approved: "approved";
219
219
  applied: "applied";
220
220
  superseded: "superseded";
221
221
  }>;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * External skill candidate quarantine store.
3
+ *
4
+ * Manages external skill candidates persisted as individual JSON files under
5
+ * `.swarm/skills/candidates/<uuid>.json`. Each candidate goes through a
6
+ * quarantine lifecycle (pending → in_review → quarantined → passed/rejected →
7
+ * promoted/revoked) before it can be activated as a generated skill.
8
+ *
9
+ * All writes are atomic (temp-file + rename) via `atomicWriteFile` from the
10
+ * evidence subsystem. File-system I/O is funnelled through `_internals` so
11
+ * that tests can replace individual operations without cross-module mock
12
+ * leakage (Bun's `mock.module` is intentionally avoided for I/O seams).
13
+ *
14
+ * Invariants:
15
+ * - Candidate IDs are UUID v4 (cryptographically random), never derived from
16
+ * user input, to prevent path-traversal attacks.
17
+ * - `passed`, `promoted`, and `revoked` candidates are NEVER evicted.
18
+ * - The store directory is derived from the injected `directory` parameter
19
+ * (typically `ctx.directory`); no `process.cwd()` calls.
20
+ */
21
+ import * as crypto from 'node:crypto';
22
+ import * as fs from 'node:fs/promises';
23
+ import type { ExternalSkillCandidate, ExternalSkillCandidateEvaluationVerdict } from '../config/schema';
24
+ import { atomicWriteFile } from '../evidence/task-file';
25
+ /** Configuration for the store. */
26
+ export interface ExternalSkillStoreConfig {
27
+ /** Maximum number of candidates before FIFO eviction kicks in. */
28
+ max_candidates: number;
29
+ }
30
+ /** Optional filters for listing candidates. */
31
+ export interface ExternalSkillListFilter {
32
+ /** Restrict to candidates with this evaluation verdict. */
33
+ verdict?: ExternalSkillCandidateEvaluationVerdict;
34
+ /** Restrict to candidates from this source type (e.g. 'github'). */
35
+ source_type?: string;
36
+ /** Restrict to candidates with this exact source URL. */
37
+ source_url?: string;
38
+ /** ISO datetime — only return candidates fetched at or after this time. */
39
+ since?: string;
40
+ }
41
+ /** Patch fields accepted by `update`. */
42
+ export type ExternalSkillCandidatePatch = Partial<Pick<ExternalSkillCandidate, 'evaluation_verdict' | 'risk_flags' | 'evaluation_history' | 'skill_name' | 'skill_description'>>;
43
+ /**
44
+ * Public interface returned by the factory function.
45
+ *
46
+ * Every method is scoped to the store directory derived at creation time.
47
+ */
48
+ export interface ExternalSkillStore {
49
+ /** Create a new candidate and persist it atomically. */
50
+ add(candidate: Omit<ExternalSkillCandidate, 'id'>): Promise<ExternalSkillCandidate>;
51
+ /** Read a single candidate by UUID. Returns `null` if not found. */
52
+ get(id: string): Promise<ExternalSkillCandidate | null>;
53
+ /** List candidates with optional filters, sorted by `fetched_at` descending. */
54
+ list(filter?: ExternalSkillListFilter): Promise<ExternalSkillCandidate[]>;
55
+ /** Patch an existing candidate (read-modify-write). Appends to `evaluation_history`. */
56
+ update(id: string, patch: ExternalSkillCandidatePatch): Promise<ExternalSkillCandidate | null>;
57
+ /** Remove a candidate file. Returns `true` if the file existed and was deleted. */
58
+ delete(id: string): Promise<boolean>;
59
+ /**
60
+ * Evict the oldest `pending` or `rejected` candidates when the store
61
+ * exceeds `max_candidates`. Never evicts `passed`, `promoted`, or
62
+ * `revoked` candidates. Returns the number of evicted files.
63
+ */
64
+ evictIfNeeded(): Promise<number>;
65
+ }
66
+ /**
67
+ * Dependency-injection seam for testing.
68
+ *
69
+ * Tests can temporarily replace individual entries to exercise failure paths
70
+ * (e.g. file-not-found, permission errors) without `mock.module` leakage.
71
+ * Restore each entry in `afterEach` via the saved original reference.
72
+ */
73
+ export declare const _internals: {
74
+ /** UUID generator — default is `crypto.randomUUID()`. */
75
+ randomUUID: typeof crypto.randomUUID;
76
+ /** Async filesystem operations. */
77
+ fs: {
78
+ mkdir: typeof fs.mkdir;
79
+ readFile: typeof fs.readFile;
80
+ readdir: typeof fs.readdir;
81
+ unlink: typeof fs.unlink;
82
+ };
83
+ /** Atomic write primitive (temp-file + rename). */
84
+ atomicWriteFile: typeof atomicWriteFile;
85
+ };
86
+ /**
87
+ * Create an `ExternalSkillStore` scoped to the given project root directory.
88
+ *
89
+ * The store persists candidate files under
90
+ * `<directory>/.swarm/skills/candidates/<uuid>.json`.
91
+ *
92
+ * @param directory — Project root (typically `ctx.directory`). Must NOT contain
93
+ * user-controlled path components.
94
+ * @param config — Store configuration including capacity limits.
95
+ */
96
+ export declare function createExternalSkillStore(directory: string, config: ExternalSkillStoreConfig): ExternalSkillStore;
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Validation gates for external skill candidates.
3
+ *
4
+ * Provides shared types and individual gate functions that scan candidate
5
+ * fields for security threats (prompt injection, unsafe instructions,
6
+ * provenance integrity). Each gate returns a structured `ValidationGateResult`
7
+ * that the curation pipeline can use to block, warn, or pass a candidate.
8
+ *
9
+ * Gate 1 — `scanPromptInjection`: static regex-based detection of prompt-
10
+ * injection patterns, prototype pollution, script injection, and obfuscated
11
+ * content in candidate fields. Severity is modulated by the candidate's trust
12
+ * level (FR-004).
13
+ *
14
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
15
+ */
16
+ import type { ExternalSkillCandidate } from '../config/schema';
17
+ /** Result from a single validation gate scan. */
18
+ export interface ValidationGateResult {
19
+ /** Which gate produced this result. */
20
+ gate: 'prompt_injection' | 'unsafe_instructions' | 'provenance_integrity';
21
+ /** Overall pass/fail/warn verdict. */
22
+ verdict: 'pass' | 'fail' | 'warn';
23
+ /** Individual findings from the scan. */
24
+ findings: ValidationFinding[];
25
+ /** The candidate fields that were scanned. */
26
+ fields_scanned: string[];
27
+ }
28
+ /** A single finding from a validation gate. */
29
+ export interface ValidationFinding {
30
+ /** What was detected. */
31
+ pattern: string;
32
+ /** Which candidate field triggered the finding. */
33
+ field: string;
34
+ /** Human-readable description. */
35
+ description: string;
36
+ /**
37
+ * Severity: 'error' blocks promotion, 'warning' is advisory
38
+ * (unless trust_level=low).
39
+ */
40
+ severity: 'error' | 'warning';
41
+ /** The matched text snippet (truncated to 100 chars for safety). */
42
+ match: string;
43
+ }
44
+ /** Result of running all validation gates against a candidate. */
45
+ export interface CandidateEvaluationResult {
46
+ /** Individual gate results. */
47
+ gate_results: ValidationGateResult[];
48
+ /** Aggregated verdict across all gates. */
49
+ overall_verdict: 'passed' | 'quarantined';
50
+ /** All findings from all gates combined. */
51
+ all_findings: ValidationFinding[];
52
+ /** Risk flags derived from findings (unique pattern names). */
53
+ risk_flags: string[];
54
+ }
55
+ /** Describes a single detection pattern used by the prompt-injection gate. */
56
+ export interface PromptInjectionPattern {
57
+ /** Regex to test against field text. */
58
+ pattern: RegExp;
59
+ /** Human-readable name for the pattern. */
60
+ name: string;
61
+ /** Description shown in findings. */
62
+ description: string;
63
+ /** Base severity before trust-level modulation. */
64
+ severity: 'error' | 'warning';
65
+ }
66
+ /**
67
+ * Static regex patterns for the prompt-injection gate (FR-004).
68
+ *
69
+ * ERROR-severity patterns always block promotion. WARNING-severity patterns
70
+ * are modulated by the candidate's trust level:
71
+ * - trust_level='low' → warnings promoted to errors
72
+ * - trust_level='medium'/'high' → warnings stay warnings
73
+ */
74
+ export declare const PROMPT_INJECTION_PATTERNS: PromptInjectionPattern[];
75
+ /** Describes a single detection pattern used by the unsafe-instruction gate. */
76
+ export interface UnsafeInstructionPattern {
77
+ /** Regex to test against field text. */
78
+ pattern: RegExp;
79
+ /** Human-readable name for the pattern. */
80
+ name: string;
81
+ /** Description shown in findings. */
82
+ description: string;
83
+ /** Base severity before trust-level modulation. */
84
+ severity: 'error' | 'warning';
85
+ }
86
+ /**
87
+ * Static regex patterns for the unsafe-instruction gate.
88
+ *
89
+ * Extends the DANGEROUS_COMMAND_PATTERNS and SECURITY_DEGRADING_PATTERNS from
90
+ * knowledge-validator.ts with additional destructive command, privilege
91
+ * escalation, shell execution, security bypass, and data exfiltration patterns.
92
+ *
93
+ * ERROR-severity patterns always block promotion. WARNING-severity patterns
94
+ * are modulated by the candidate's trust level:
95
+ * - trust_level='low' → warnings promoted to errors
96
+ * - trust_level='medium'/'high' → warnings stay warnings
97
+ */
98
+ export declare const UNSAFE_INSTRUCTION_PATTERNS: UnsafeInstructionPattern[];
99
+ /** Rate limit defaults for validation operations (FR-007). */
100
+ export declare const VALIDATION_RATE_LIMITS: {
101
+ /** Maximum candidates per discovery invocation. */
102
+ readonly max_candidates_per_discovery: 50;
103
+ /** Maximum concurrent fetch operations. */
104
+ readonly max_concurrent_fetches: 5;
105
+ /** Timeout for individual fetch operations in milliseconds. */
106
+ readonly fetch_timeout_ms: 30000;
107
+ };
108
+ /**
109
+ * Scan an external skill candidate for prompt-injection patterns.
110
+ *
111
+ * Returns a `ValidationGateResult` with gate=`'prompt_injection'`.
112
+ * The verdict is modulated by `trustLevel`:
113
+ * - `'low'`: warnings promoted to errors → verdict is `'fail'` if any finding.
114
+ * - `'medium'`/`'high'`: warnings stay warnings → verdict is `'warn'` if only
115
+ * warnings, `'fail'` if any error-severity finding.
116
+ */
117
+ export declare function scanPromptInjection(candidate: ExternalSkillCandidate, trustLevel?: 'low' | 'medium' | 'high'): ValidationGateResult;
118
+ /**
119
+ * Scan an external skill candidate for unsafe instruction patterns.
120
+ *
121
+ * Covers destructive commands, privilege escalation, shell execution
122
+ * vectors, security bypass instructions, and data exfiltration indicators.
123
+ *
124
+ * Returns a `ValidationGateResult` with gate=`'unsafe_instructions'`.
125
+ * The verdict is modulated by `trustLevel`:
126
+ * - `'low'`: warnings promoted to errors → verdict is `'fail'` if any finding.
127
+ * - `'medium'`/`'high'`: warnings stay warnings → verdict is `'warn'` if only
128
+ * warnings, `'fail'` if any error-severity finding.
129
+ */
130
+ export declare function scanUnsafeInstructions(candidate: ExternalSkillCandidate, trustLevel?: 'low' | 'medium' | 'high'): ValidationGateResult;
131
+ /**
132
+ * Scan an external skill candidate for provenance field integrity.
133
+ *
134
+ * Validates SHA-256 hash format, fetched_at timing (not in future, not stale),
135
+ * source_url validity, publisher presence, and content-hash verification.
136
+ *
137
+ * Returns a `ValidationGateResult` with gate=`'provenance_integrity'`.
138
+ * The verdict is modulated by `trustLevel`:
139
+ * - `'low'`: warnings promoted to errors → verdict is `'fail'` if any finding.
140
+ * - `'medium'`/`'high'`: warnings stay warnings → verdict is `'warn'` if only
141
+ * warnings, `'fail'` if any error-severity finding.
142
+ */
143
+ export declare function scanProvenanceIntegrity(candidate: ExternalSkillCandidate, trustLevel?: 'low' | 'medium' | 'high', ttlDays?: number): ValidationGateResult;
144
+ /**
145
+ * Run all three validation gates against a candidate and produce an
146
+ * aggregated evaluation result (FR-004, FR-007).
147
+ *
148
+ * Gates are run sequentially: prompt-injection → unsafe-instructions →
149
+ * provenance-integrity. Any gate that returns `'fail'` causes the overall
150
+ * verdict to be `'quarantined'`. Warnings are advisory unless trust_level
151
+ * is `'low'` (which promotes them to errors inside each gate).
152
+ */
153
+ export declare function evaluateCandidate(candidate: ExternalSkillCandidate, options?: {
154
+ trust_level?: 'low' | 'medium' | 'high';
155
+ ttl_days?: number;
156
+ }): CandidateEvaluationResult;
157
+ export declare const _internals: {
158
+ getTimestamp: () => string;
159
+ computeSha256: (content: string) => string;
160
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * external_skill_delete — Delete an external skill candidate from the quarantine store.
3
+ *
4
+ * Removes a candidate by ID. If the candidate was previously promoted, the
5
+ * promoted skill in `.opencode/skills/generated/` is NOT affected — it must be
6
+ * separately retired or revoked. Returns a disabled message when
7
+ * external_skills.curation_enabled is false.
8
+ *
9
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
10
+ */
11
+ import type { ExternalSkillsConfig } from '../config/schema.js';
12
+ import { createSwarmTool } from './create-tool.js';
13
+ export declare const _internals: {
14
+ loadConfig: (directory: string) => ExternalSkillsConfig | undefined;
15
+ };
16
+ export declare const external_skill_delete: ReturnType<typeof createSwarmTool>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * external_skill_discover — Discover external skill candidates from configured sources.
3
+ *
4
+ * Fetches skill content from URLs or accepts manual imports, validates them
5
+ * through the security gates (prompt-injection, unsafe-instructions,
6
+ * provenance-integrity), and stores them as quarantined candidates in the
7
+ * external skill store.
8
+ *
9
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
10
+ */
11
+ import { createSwarmTool } from './create-tool.js';
12
+ export declare const _internals: {
13
+ fetchContent: (_url: string, _timeoutMs: number) => Promise<{
14
+ content: string;
15
+ finalUrl: string;
16
+ }>;
17
+ getTimestamp: () => string;
18
+ computeSha256: (content: string) => string;
19
+ uuid: () => string;
20
+ };
21
+ export declare const external_skill_discover: ReturnType<typeof createSwarmTool>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * external_skill_inspect — Inspect a specific external skill candidate by ID.
3
+ *
4
+ * Read-only tool that returns the full candidate record including provenance,
5
+ * skill_body, and evaluation_history. Returns a disabled message when
6
+ * external_skills.curation_enabled is false.
7
+ *
8
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
9
+ */
10
+ import type { ExternalSkillsConfig } from '../config/schema.js';
11
+ import { createSwarmTool } from './create-tool.js';
12
+ export declare const _internals: {
13
+ loadConfig: (directory: string) => ExternalSkillsConfig | undefined;
14
+ };
15
+ export declare const external_skill_inspect: ReturnType<typeof createSwarmTool>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * external_skill_list — List external skill candidates in the quarantine store.
3
+ *
4
+ * Read-only tool that returns candidate summaries filtered by evaluation verdict,
5
+ * source type, or date range. Returns a disabled message when
6
+ * external_skills.curation_enabled is false.
7
+ *
8
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
9
+ */
10
+ import type { ExternalSkillsConfig } from '../config/schema.js';
11
+ import { createSwarmTool } from './create-tool.js';
12
+ export declare const _internals: {
13
+ loadConfig: (directory: string) => ExternalSkillsConfig | undefined;
14
+ };
15
+ export declare const external_skill_list: ReturnType<typeof createSwarmTool>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * external_skill_promote — Promote a validated external skill candidate to an
3
+ * active generated skill.
4
+ *
5
+ * Re-runs all three validation gates (TOCTOU re-validation). Requires explicit
6
+ * user approval (`approver='user'`). Writes SKILL.md to
7
+ * `.opencode/skills/generated/<slug>/` with provenance frontmatter. Stamps the
8
+ * candidate as promoted and creates an audit record.
9
+ *
10
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
11
+ */
12
+ import type { ExternalSkillsConfig } from '../config/schema.js';
13
+ import { createSwarmTool } from './create-tool.js';
14
+ export declare const _internals: {
15
+ loadConfig: (directory: string) => ExternalSkillsConfig | undefined;
16
+ getTimestamp: () => string;
17
+ fileExists: (filePath: string) => Promise<boolean>;
18
+ writeSkillFile: (filePath: string, content: string) => Promise<void>;
19
+ };
20
+ export declare const external_skill_promote: ReturnType<typeof createSwarmTool>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * external_skill_reject — Reject an external skill candidate after evaluation.
3
+ *
4
+ * Marks a candidate as rejected with a user-provided reason. Records the
5
+ * state transition in evaluation_history with timestamp, actor, and reason.
6
+ * Returns a disabled message when external_skills.curation_enabled is false.
7
+ *
8
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
9
+ */
10
+ import type { ExternalSkillsConfig } from '../config/schema.js';
11
+ import { createSwarmTool } from './create-tool.js';
12
+ export declare const _internals: {
13
+ loadConfig: (directory: string) => ExternalSkillsConfig | undefined;
14
+ };
15
+ export declare const external_skill_reject: ReturnType<typeof createSwarmTool>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * external_skill_revoke — Revoke a previously promoted external skill.
3
+ *
4
+ * Atomically retires the SKILL.md from `.opencode/skills/generated/<slug>/`
5
+ * and stamps the candidate with evaluation_verdict: 'revoked'. The candidate
6
+ * stays in quarantine for forensic audit.
7
+ *
8
+ * Uses an `_internals` DI seam for testability — no `mock.module` leakage.
9
+ */
10
+ import type { ExternalSkillsConfig } from '../config/schema.js';
11
+ import { createSwarmTool } from './create-tool.js';
12
+ export declare const _internals: {
13
+ loadConfig: (directory: string) => ExternalSkillsConfig | undefined;
14
+ getTimestamp: () => string;
15
+ retireSkillFile: (filePath: string) => Promise<boolean>;
16
+ };
17
+ export declare const external_skill_revoke: ReturnType<typeof createSwarmTool>;
@@ -18,6 +18,13 @@ export { diff_summary } from './diff-summary';
18
18
  export { doc_extract, doc_scan } from './doc-scan';
19
19
  export { detect_domains } from './domain-detector';
20
20
  export { evidence_check } from './evidence-check';
21
+ export { external_skill_delete } from './external-skill-delete';
22
+ export { external_skill_discover } from './external-skill-discover';
23
+ export { external_skill_inspect } from './external-skill-inspect';
24
+ export { external_skill_list } from './external-skill-list';
25
+ export { external_skill_promote } from './external-skill-promote';
26
+ export { external_skill_reject } from './external-skill-reject';
27
+ export { external_skill_revoke } from './external-skill-revoke';
21
28
  export { extract_code_blocks } from './file-extractor';
22
29
  export { get_approved_plan } from './get-approved-plan';
23
30
  export { get_qa_gate_profile } from './get-qa-gate-profile';
@@ -108,4 +108,11 @@ export declare const TOOL_MANIFEST: {
108
108
  lean_turbo_run_phase: () => ToolDefinition;
109
109
  lean_turbo_status: () => ToolDefinition;
110
110
  apply_patch: () => ToolDefinition;
111
+ external_skill_discover: () => ToolDefinition;
112
+ external_skill_list: () => ToolDefinition;
113
+ external_skill_inspect: () => ToolDefinition;
114
+ external_skill_promote: () => ToolDefinition;
115
+ external_skill_reject: () => ToolDefinition;
116
+ external_skill_delete: () => ToolDefinition;
117
+ external_skill_revoke: () => ToolDefinition;
111
118
  };
@@ -363,6 +363,34 @@ export declare const TOOL_METADATA: {
363
363
  description: string;
364
364
  agents: "coder"[];
365
365
  };
366
+ external_skill_discover: {
367
+ description: string;
368
+ agents: never[];
369
+ };
370
+ external_skill_list: {
371
+ description: string;
372
+ agents: never[];
373
+ };
374
+ external_skill_inspect: {
375
+ description: string;
376
+ agents: never[];
377
+ };
378
+ external_skill_promote: {
379
+ description: string;
380
+ agents: never[];
381
+ };
382
+ external_skill_reject: {
383
+ description: string;
384
+ agents: never[];
385
+ };
386
+ external_skill_delete: {
387
+ description: string;
388
+ agents: never[];
389
+ };
390
+ external_skill_revoke: {
391
+ description: string;
392
+ agents: never[];
393
+ };
366
394
  };
367
395
  /** Union type of all valid tool names (the metadata keys). */
368
396
  export type ToolName = keyof typeof TOOL_METADATA;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.63.0",
3
+ "version": "7.64.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",