opencode-swarm 6.1.0 → 6.1.2
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/README.md +34 -5
- package/dist/config/evidence-schema.d.ts +3 -3
- package/dist/config/index.d.ts +3 -3
- package/dist/config/loader.d.ts +10 -5
- package/dist/config/plan-schema.d.ts +4 -4
- package/dist/config/schema.d.ts +0 -1
- package/dist/index.js +212 -162
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/retrieve-summary.d.ts +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/merge.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://img.shields.io/badge/version-6.
|
|
2
|
+
<img src="https://img.shields.io/badge/version-6.1.2-blue" alt="Version">
|
|
3
3
|
<img src="https://img.shields.io/badge/license-MIT-green" alt="License">
|
|
4
4
|
<img src="https://img.shields.io/badge/opencode-plugin-purple" alt="OpenCode Plugin">
|
|
5
|
-
<img src="https://img.shields.io/badge/agents-
|
|
6
|
-
<img src="https://img.shields.io/badge/tests-
|
|
5
|
+
<img src="https://img.shields.io/badge/agents-9-orange" alt="Agents">
|
|
6
|
+
<img src="https://img.shields.io/badge/tests-1280-brightgreen" alt="Tests">
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<h1 align="center">🐝 OpenCode Swarm</h1>
|
|
@@ -343,6 +343,23 @@ bunx opencode-swarm uninstall --clean
|
|
|
343
343
|
|
|
344
344
|
## What's New
|
|
345
345
|
|
|
346
|
+
### v6.1.2 — Guardrails Remediation
|
|
347
|
+
- **Fail-safe config validation** — Config validation failures now disable guardrails as a safety precaution (previously Zod defaults could silently re-enable them).
|
|
348
|
+
- **Architect exemption fix** — Architect/orchestrator sessions can no longer inherit 30-minute base limits during delegation race conditions.
|
|
349
|
+
- **Explicit disable always wins** — `guardrails.enabled: false` in config is now always honored, even when the config was loaded from file.
|
|
350
|
+
- **Internal map synchronization** — `startAgentSession()` now keeps `activeAgent` and `agentSessions` maps in sync for consistent state tracking.
|
|
351
|
+
|
|
352
|
+
### v6.1.1 — Security Fix & Tech Debt
|
|
353
|
+
- **Security hardening (`_loadedFromFile`)** — Fixed a critical vulnerability where an internal loader flag could be injected via JSON config to bypass guardrails. The flag is now purely internal and no longer part of the public schema.
|
|
354
|
+
- **TOCTOU protection** — Added atomic-style content checks in the config loader to prevent race conditions during file reads.
|
|
355
|
+
- **`retrieve_summary` tool** — Properly registered the retrieval tool, allowing agents to fetch full content from auto-summarized tool outputs.
|
|
356
|
+
- **92 new tests** — 1280 total tests across 57+ files (up from 1188 in v6.0.0).
|
|
357
|
+
|
|
358
|
+
### v6.1.0 — Docs & Design Agents
|
|
359
|
+
- **`docs` agent** — Dedicated documentation synthesizer that automatically updates READMEs, API docs, and guides during Phase 6.
|
|
360
|
+
- **`designer` agent** — UI/UX specification agent that generates component scaffolds before coding begins on UI-heavy tasks.
|
|
361
|
+
- **Heterogeneous model defaults** — Updated default models for new agents to use optimized Gemini models for speed and cost.
|
|
362
|
+
|
|
346
363
|
### v6.0.0 — Core QA & Security Gates
|
|
347
364
|
- **Dual-pass security reviewer** — After the general reviewer APPROVES, the architect automatically triggers a second security-only review pass when the changed file matches security-sensitive paths (`auth`, `crypto`, `session`, `token`, `middleware`, `api`, `security`) or the coder's output contains security keywords. Configurable via `review_passes` config.
|
|
348
365
|
- **Adversarial testing** — After verification tests PASS, the test engineer is re-delegated with adversarial-only framing: attack vectors, boundary violations, and injection attempts. Pure prompt engineering, no new infrastructure.
|
|
@@ -411,6 +428,11 @@ All features are opt-in via configuration. See [Installation Guide](docs/install
|
|
|
411
428
|
|-------|------|
|
|
412
429
|
| `explorer` | Fast codebase scanner. Identifies structure, languages, frameworks, key files. |
|
|
413
430
|
|
|
431
|
+
### 🎨 Design
|
|
432
|
+
| Agent | Role |
|
|
433
|
+
|-------|------|
|
|
434
|
+
| `designer` | UI/UX specification agent. Generates component scaffolds and design tokens before coding begins on UI-heavy tasks. |
|
|
435
|
+
|
|
414
436
|
### 🧠 Domain Expert
|
|
415
437
|
| Agent | Role |
|
|
416
438
|
|-------|------|
|
|
@@ -428,6 +450,11 @@ All features are opt-in via configuration. See [Installation Guide](docs/install
|
|
|
428
450
|
| `reviewer` | Dual-pass review: correctness review first, then automatic security-only pass for security-sensitive files. The architect specifies CHECK dimensions per call. OWASP Top 10 categories built in. |
|
|
429
451
|
| `critic` | Plan review gate. Reviews the architect's plan BEFORE implementation — checks completeness, feasibility, scope, dependencies, and flags AI-slop. |
|
|
430
452
|
|
|
453
|
+
### 📝 Documentation
|
|
454
|
+
| Agent | Role |
|
|
455
|
+
|-------|------|
|
|
456
|
+
| `docs` | Documentation synthesizer. Automatically updates READMEs, API docs, and guides based on implementation changes during Phase 6. |
|
|
457
|
+
|
|
431
458
|
---
|
|
432
459
|
|
|
433
460
|
## Slash Commands
|
|
@@ -462,7 +489,9 @@ Create `~/.config/opencode/opencode-swarm.json`:
|
|
|
462
489
|
"sme": { "model": "google/gemini-2.0-flash" },
|
|
463
490
|
"reviewer": { "model": "openai/gpt-4o" },
|
|
464
491
|
"critic": { "model": "google/gemini-2.0-flash" },
|
|
465
|
-
"test_engineer": { "model": "google/gemini-2.0-flash" }
|
|
492
|
+
"test_engineer": { "model": "google/gemini-2.0-flash" },
|
|
493
|
+
"docs": { "model": "google/gemini-2.0-flash" },
|
|
494
|
+
"designer": { "model": "google/gemini-2.0-flash" }
|
|
466
495
|
}
|
|
467
496
|
}
|
|
468
497
|
```
|
|
@@ -630,7 +659,7 @@ bun test
|
|
|
630
659
|
bun test tests/unit/config/schema.test.ts
|
|
631
660
|
```
|
|
632
661
|
|
|
633
|
-
|
|
662
|
+
1280 tests across 57+ files covering config, tools, agents, hooks, commands, state, guardrails, evidence, plan schemas, circuit breaker race conditions, invocation windows, multi-invocation isolation, security categories, review/integration schemas, and diff tool. Uses Bun's built-in test runner — zero additional test dependencies.
|
|
634
663
|
|
|
635
664
|
## Troubleshooting
|
|
636
665
|
|
|
@@ -55,8 +55,8 @@ export declare const ReviewEvidenceSchema: z.ZodObject<{
|
|
|
55
55
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
56
56
|
type: z.ZodLiteral<"review">;
|
|
57
57
|
risk: z.ZodEnum<{
|
|
58
|
-
medium: "medium";
|
|
59
58
|
low: "low";
|
|
59
|
+
medium: "medium";
|
|
60
60
|
high: "high";
|
|
61
61
|
critical: "critical";
|
|
62
62
|
}>;
|
|
@@ -162,8 +162,8 @@ export declare const EvidenceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
162
162
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
163
163
|
type: z.ZodLiteral<"review">;
|
|
164
164
|
risk: z.ZodEnum<{
|
|
165
|
-
medium: "medium";
|
|
166
165
|
low: "low";
|
|
166
|
+
medium: "medium";
|
|
167
167
|
high: "high";
|
|
168
168
|
critical: "critical";
|
|
169
169
|
}>;
|
|
@@ -264,8 +264,8 @@ export declare const EvidenceBundleSchema: z.ZodObject<{
|
|
|
264
264
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
265
265
|
type: z.ZodLiteral<"review">;
|
|
266
266
|
risk: z.ZodEnum<{
|
|
267
|
-
medium: "medium";
|
|
268
267
|
low: "low";
|
|
268
|
+
medium: "medium";
|
|
269
269
|
high: "high";
|
|
270
270
|
critical: "critical";
|
|
271
271
|
}>;
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export type { AgentName, PipelineAgentName, QAAgentName, } from './constants';
|
|
2
2
|
export { ALL_AGENT_NAMES, ALL_SUBAGENT_NAMES, DEFAULT_MODELS, isQAAgent, isSubagent, ORCHESTRATOR_NAME, PIPELINE_AGENTS, QA_AGENTS, } from './constants';
|
|
3
|
-
export { loadAgentPrompt, loadPluginConfig, } from './loader';
|
|
4
|
-
export type { MigrationStatus, Phase, PhaseStatus, Plan, Task, TaskSize, TaskStatus, } from './plan-schema';
|
|
5
|
-
export { MigrationStatusSchema, PhaseSchema, PhaseStatusSchema, PlanSchema, TaskSchema, TaskSizeSchema, TaskStatusSchema, } from './plan-schema';
|
|
6
3
|
export type { ApprovalEvidence, BaseEvidence, DiffEvidence, Evidence, EvidenceBundle, EvidenceType, EvidenceVerdict, NoteEvidence, ReviewEvidence, TestEvidence, } from './evidence-schema';
|
|
7
4
|
export { ApprovalEvidenceSchema, BaseEvidenceSchema, DiffEvidenceSchema, EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceBundleSchema, EvidenceSchema, EvidenceTypeSchema, EvidenceVerdictSchema, NoteEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, } from './evidence-schema';
|
|
5
|
+
export { loadAgentPrompt, loadPluginConfig, loadPluginConfigWithMeta, } from './loader';
|
|
6
|
+
export type { MigrationStatus, Phase, PhaseStatus, Plan, Task, TaskSize, TaskStatus, } from './plan-schema';
|
|
7
|
+
export { MigrationStatusSchema, PhaseSchema, PhaseStatusSchema, PlanSchema, TaskSchema, TaskSizeSchema, TaskStatusSchema, } from './plan-schema';
|
|
8
8
|
export type { AgentOverrideConfig, PluginConfig, SwarmConfig, } from './schema';
|
|
9
9
|
export { AgentOverrideConfigSchema, PluginConfigSchema, SwarmConfigSchema, } from './schema';
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { type PluginConfig } from './schema';
|
|
2
2
|
export declare const MAX_CONFIG_FILE_BYTES = 102400;
|
|
3
|
-
export
|
|
4
|
-
/**
|
|
5
|
-
* Deep merge two objects, with override values taking precedence.
|
|
6
|
-
*/
|
|
7
|
-
export declare function deepMerge<T extends Record<string, unknown>>(base?: T, override?: T): T | undefined;
|
|
3
|
+
export { deepMerge, MAX_MERGE_DEPTH } from '../utils/merge';
|
|
8
4
|
/**
|
|
9
5
|
* Load plugin configuration from user and project config files.
|
|
10
6
|
*
|
|
@@ -17,6 +13,15 @@ export declare function deepMerge<T extends Record<string, unknown>>(base?: T, o
|
|
|
17
13
|
* Zod defaults don't override explicit user values.
|
|
18
14
|
*/
|
|
19
15
|
export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Internal variant of loadPluginConfig that also returns loader metadata.
|
|
18
|
+
* Used only by src/index.ts to determine guardrails fallback behavior.
|
|
19
|
+
* NOT part of the public API — use loadPluginConfig() for all other callers.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadPluginConfigWithMeta(directory: string): {
|
|
22
|
+
config: PluginConfig;
|
|
23
|
+
loadedFromFile: boolean;
|
|
24
|
+
};
|
|
20
25
|
/**
|
|
21
26
|
* Load custom prompt for an agent from the prompts directory.
|
|
22
27
|
* Checks for {agent}.md (replaces default) and {agent}_append.md (appends).
|
|
@@ -7,8 +7,8 @@ export declare const TaskStatusSchema: z.ZodEnum<{
|
|
|
7
7
|
}>;
|
|
8
8
|
export type TaskStatus = z.infer<typeof TaskStatusSchema>;
|
|
9
9
|
export declare const TaskSizeSchema: z.ZodEnum<{
|
|
10
|
-
small: "small";
|
|
11
10
|
medium: "medium";
|
|
11
|
+
small: "small";
|
|
12
12
|
large: "large";
|
|
13
13
|
}>;
|
|
14
14
|
export type TaskSize = z.infer<typeof TaskSizeSchema>;
|
|
@@ -35,8 +35,8 @@ export declare const TaskSchema: z.ZodObject<{
|
|
|
35
35
|
blocked: "blocked";
|
|
36
36
|
}>>;
|
|
37
37
|
size: z.ZodDefault<z.ZodEnum<{
|
|
38
|
-
small: "small";
|
|
39
38
|
medium: "medium";
|
|
39
|
+
small: "small";
|
|
40
40
|
large: "large";
|
|
41
41
|
}>>;
|
|
42
42
|
description: z.ZodString;
|
|
@@ -66,8 +66,8 @@ export declare const PhaseSchema: z.ZodObject<{
|
|
|
66
66
|
blocked: "blocked";
|
|
67
67
|
}>>;
|
|
68
68
|
size: z.ZodDefault<z.ZodEnum<{
|
|
69
|
-
small: "small";
|
|
70
69
|
medium: "medium";
|
|
70
|
+
small: "small";
|
|
71
71
|
large: "large";
|
|
72
72
|
}>>;
|
|
73
73
|
description: z.ZodString;
|
|
@@ -103,8 +103,8 @@ export declare const PlanSchema: z.ZodObject<{
|
|
|
103
103
|
blocked: "blocked";
|
|
104
104
|
}>>;
|
|
105
105
|
size: z.ZodDefault<z.ZodEnum<{
|
|
106
|
-
small: "small";
|
|
107
106
|
medium: "medium";
|
|
107
|
+
small: "small";
|
|
108
108
|
large: "large";
|
|
109
109
|
}>>;
|
|
110
110
|
description: z.ZodString;
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -318,7 +318,6 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
318
318
|
trigger_paths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
319
319
|
trigger_keywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
320
320
|
}, z.core.$strip>>;
|
|
321
|
-
_loadedFromFile: z.ZodDefault<z.ZodBoolean>;
|
|
322
321
|
}, z.core.$strip>;
|
|
323
322
|
export type PluginConfig = z.infer<typeof PluginConfigSchema>;
|
|
324
323
|
export type { AgentName, PipelineAgentName, QAAgentName, } from './constants';
|
package/dist/index.js
CHANGED
|
@@ -26,11 +26,83 @@ var __export = (target, all) => {
|
|
|
26
26
|
};
|
|
27
27
|
var __require = import.meta.require;
|
|
28
28
|
|
|
29
|
-
// src/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
// src/utils/merge.ts
|
|
30
|
+
var MAX_MERGE_DEPTH = 10;
|
|
31
|
+
function deepMergeInternal(base, override, depth) {
|
|
32
|
+
if (depth >= MAX_MERGE_DEPTH) {
|
|
33
|
+
throw new Error(`deepMerge exceeded maximum depth of ${MAX_MERGE_DEPTH}`);
|
|
34
|
+
}
|
|
35
|
+
const result = { ...base };
|
|
36
|
+
for (const key of Object.keys(override)) {
|
|
37
|
+
const baseVal = base[key];
|
|
38
|
+
const overrideVal = override[key];
|
|
39
|
+
if (typeof baseVal === "object" && baseVal !== null && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) {
|
|
40
|
+
result[key] = deepMergeInternal(baseVal, overrideVal, depth + 1);
|
|
41
|
+
} else {
|
|
42
|
+
result[key] = overrideVal;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function deepMerge(base, override) {
|
|
48
|
+
if (!base)
|
|
49
|
+
return override;
|
|
50
|
+
if (!override)
|
|
51
|
+
return base;
|
|
52
|
+
return deepMergeInternal(base, override, 0);
|
|
53
|
+
}
|
|
33
54
|
|
|
55
|
+
// src/config/constants.ts
|
|
56
|
+
var QA_AGENTS = ["reviewer", "critic"];
|
|
57
|
+
var PIPELINE_AGENTS = ["explorer", "coder", "test_engineer"];
|
|
58
|
+
var ORCHESTRATOR_NAME = "architect";
|
|
59
|
+
var ALL_SUBAGENT_NAMES = [
|
|
60
|
+
"sme",
|
|
61
|
+
"docs",
|
|
62
|
+
"designer",
|
|
63
|
+
...QA_AGENTS,
|
|
64
|
+
...PIPELINE_AGENTS
|
|
65
|
+
];
|
|
66
|
+
var ALL_AGENT_NAMES = [
|
|
67
|
+
ORCHESTRATOR_NAME,
|
|
68
|
+
...ALL_SUBAGENT_NAMES
|
|
69
|
+
];
|
|
70
|
+
var DEFAULT_MODELS = {
|
|
71
|
+
architect: "anthropic/claude-sonnet-4-5",
|
|
72
|
+
explorer: "google/gemini-2.0-flash",
|
|
73
|
+
coder: "anthropic/claude-sonnet-4-5",
|
|
74
|
+
test_engineer: "google/gemini-2.0-flash",
|
|
75
|
+
sme: "google/gemini-2.0-flash",
|
|
76
|
+
reviewer: "google/gemini-2.0-flash",
|
|
77
|
+
critic: "google/gemini-2.0-flash",
|
|
78
|
+
docs: "google/gemini-2.0-flash",
|
|
79
|
+
designer: "google/gemini-2.0-flash",
|
|
80
|
+
default: "google/gemini-2.0-flash"
|
|
81
|
+
};
|
|
82
|
+
var DEFAULT_SCORING_CONFIG = {
|
|
83
|
+
enabled: false,
|
|
84
|
+
max_candidates: 100,
|
|
85
|
+
weights: {
|
|
86
|
+
phase: 1,
|
|
87
|
+
current_task: 2,
|
|
88
|
+
blocked_task: 1.5,
|
|
89
|
+
recent_failure: 2.5,
|
|
90
|
+
recent_success: 0.5,
|
|
91
|
+
evidence_presence: 1,
|
|
92
|
+
decision_recency: 1.5,
|
|
93
|
+
dependency_proximity: 1
|
|
94
|
+
},
|
|
95
|
+
decision_decay: {
|
|
96
|
+
mode: "exponential",
|
|
97
|
+
half_life_hours: 24
|
|
98
|
+
},
|
|
99
|
+
token_ratios: {
|
|
100
|
+
prose: 0.25,
|
|
101
|
+
code: 0.4,
|
|
102
|
+
markdown: 0.3,
|
|
103
|
+
json: 0.35
|
|
104
|
+
}
|
|
105
|
+
};
|
|
34
106
|
// node_modules/zod/v4/classic/external.js
|
|
35
107
|
var exports_external = {};
|
|
36
108
|
__export(exports_external, {
|
|
@@ -13563,6 +13635,85 @@ function date4(params) {
|
|
|
13563
13635
|
|
|
13564
13636
|
// node_modules/zod/v4/classic/external.js
|
|
13565
13637
|
config(en_default());
|
|
13638
|
+
// src/config/evidence-schema.ts
|
|
13639
|
+
var EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
|
|
13640
|
+
var EVIDENCE_MAX_PATCH_BYTES = 5 * 1024 * 1024;
|
|
13641
|
+
var EVIDENCE_MAX_TASK_BYTES = 20 * 1024 * 1024;
|
|
13642
|
+
var EvidenceTypeSchema = exports_external.enum([
|
|
13643
|
+
"review",
|
|
13644
|
+
"test",
|
|
13645
|
+
"diff",
|
|
13646
|
+
"approval",
|
|
13647
|
+
"note"
|
|
13648
|
+
]);
|
|
13649
|
+
var EvidenceVerdictSchema = exports_external.enum([
|
|
13650
|
+
"pass",
|
|
13651
|
+
"fail",
|
|
13652
|
+
"approved",
|
|
13653
|
+
"rejected",
|
|
13654
|
+
"info"
|
|
13655
|
+
]);
|
|
13656
|
+
var BaseEvidenceSchema = exports_external.object({
|
|
13657
|
+
task_id: exports_external.string().min(1),
|
|
13658
|
+
type: EvidenceTypeSchema,
|
|
13659
|
+
timestamp: exports_external.string().datetime(),
|
|
13660
|
+
agent: exports_external.string().min(1),
|
|
13661
|
+
verdict: EvidenceVerdictSchema,
|
|
13662
|
+
summary: exports_external.string().min(1),
|
|
13663
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
13664
|
+
});
|
|
13665
|
+
var ReviewEvidenceSchema = BaseEvidenceSchema.extend({
|
|
13666
|
+
type: exports_external.literal("review"),
|
|
13667
|
+
risk: exports_external.enum(["low", "medium", "high", "critical"]),
|
|
13668
|
+
issues: exports_external.array(exports_external.object({
|
|
13669
|
+
severity: exports_external.enum(["error", "warning", "info"]),
|
|
13670
|
+
message: exports_external.string().min(1),
|
|
13671
|
+
file: exports_external.string().optional(),
|
|
13672
|
+
line: exports_external.number().int().optional()
|
|
13673
|
+
})).default([])
|
|
13674
|
+
});
|
|
13675
|
+
var TestEvidenceSchema = BaseEvidenceSchema.extend({
|
|
13676
|
+
type: exports_external.literal("test"),
|
|
13677
|
+
tests_passed: exports_external.number().int().min(0),
|
|
13678
|
+
tests_failed: exports_external.number().int().min(0),
|
|
13679
|
+
test_file: exports_external.string().optional(),
|
|
13680
|
+
failures: exports_external.array(exports_external.object({
|
|
13681
|
+
name: exports_external.string().min(1),
|
|
13682
|
+
message: exports_external.string().min(1)
|
|
13683
|
+
})).default([])
|
|
13684
|
+
});
|
|
13685
|
+
var DiffEvidenceSchema = BaseEvidenceSchema.extend({
|
|
13686
|
+
type: exports_external.literal("diff"),
|
|
13687
|
+
files_changed: exports_external.array(exports_external.string()).default([]),
|
|
13688
|
+
additions: exports_external.number().int().min(0).default(0),
|
|
13689
|
+
deletions: exports_external.number().int().min(0).default(0),
|
|
13690
|
+
patch_path: exports_external.string().optional()
|
|
13691
|
+
});
|
|
13692
|
+
var ApprovalEvidenceSchema = BaseEvidenceSchema.extend({
|
|
13693
|
+
type: exports_external.literal("approval")
|
|
13694
|
+
});
|
|
13695
|
+
var NoteEvidenceSchema = BaseEvidenceSchema.extend({
|
|
13696
|
+
type: exports_external.literal("note")
|
|
13697
|
+
});
|
|
13698
|
+
var EvidenceSchema = exports_external.discriminatedUnion("type", [
|
|
13699
|
+
ReviewEvidenceSchema,
|
|
13700
|
+
TestEvidenceSchema,
|
|
13701
|
+
DiffEvidenceSchema,
|
|
13702
|
+
ApprovalEvidenceSchema,
|
|
13703
|
+
NoteEvidenceSchema
|
|
13704
|
+
]);
|
|
13705
|
+
var EvidenceBundleSchema = exports_external.object({
|
|
13706
|
+
schema_version: exports_external.literal("1.0.0"),
|
|
13707
|
+
task_id: exports_external.string().min(1),
|
|
13708
|
+
entries: exports_external.array(EvidenceSchema).default([]),
|
|
13709
|
+
created_at: exports_external.string().datetime(),
|
|
13710
|
+
updated_at: exports_external.string().datetime()
|
|
13711
|
+
});
|
|
13712
|
+
// src/config/loader.ts
|
|
13713
|
+
import * as fs from "fs";
|
|
13714
|
+
import * as os from "os";
|
|
13715
|
+
import * as path from "path";
|
|
13716
|
+
|
|
13566
13717
|
// src/config/schema.ts
|
|
13567
13718
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
13568
13719
|
model: exports_external.string().optional(),
|
|
@@ -13798,8 +13949,7 @@ var PluginConfigSchema = exports_external.object({
|
|
|
13798
13949
|
review_passes: ReviewPassesConfigSchema.optional(),
|
|
13799
13950
|
integration_analysis: IntegrationAnalysisConfigSchema.optional(),
|
|
13800
13951
|
docs: DocsConfigSchema.optional(),
|
|
13801
|
-
ui_review: UIReviewConfigSchema.optional()
|
|
13802
|
-
_loadedFromFile: exports_external.boolean().default(false)
|
|
13952
|
+
ui_review: UIReviewConfigSchema.optional()
|
|
13803
13953
|
});
|
|
13804
13954
|
|
|
13805
13955
|
// src/config/loader.ts
|
|
@@ -13818,6 +13968,11 @@ function loadRawConfigFromPath(configPath) {
|
|
|
13818
13968
|
return null;
|
|
13819
13969
|
}
|
|
13820
13970
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
13971
|
+
if (content.length > MAX_CONFIG_FILE_BYTES) {
|
|
13972
|
+
console.warn(`[opencode-swarm] Config file too large after read (max 100 KB): ${configPath}`);
|
|
13973
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13974
|
+
return null;
|
|
13975
|
+
}
|
|
13821
13976
|
const rawConfig = JSON.parse(content);
|
|
13822
13977
|
if (typeof rawConfig !== "object" || rawConfig === null || Array.isArray(rawConfig)) {
|
|
13823
13978
|
console.warn(`[opencode-swarm] Invalid config at ${configPath}: expected an object`);
|
|
@@ -13833,23 +13988,6 @@ function loadRawConfigFromPath(configPath) {
|
|
|
13833
13988
|
return null;
|
|
13834
13989
|
}
|
|
13835
13990
|
}
|
|
13836
|
-
var MAX_MERGE_DEPTH = 10;
|
|
13837
|
-
function deepMergeInternal(base, override, depth) {
|
|
13838
|
-
if (depth >= MAX_MERGE_DEPTH) {
|
|
13839
|
-
throw new Error(`deepMerge exceeded maximum depth of ${MAX_MERGE_DEPTH}`);
|
|
13840
|
-
}
|
|
13841
|
-
const result = { ...base };
|
|
13842
|
-
for (const key of Object.keys(override)) {
|
|
13843
|
-
const baseVal = base[key];
|
|
13844
|
-
const overrideVal = override[key];
|
|
13845
|
-
if (typeof baseVal === "object" && baseVal !== null && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) {
|
|
13846
|
-
result[key] = deepMergeInternal(baseVal, overrideVal, depth + 1);
|
|
13847
|
-
} else {
|
|
13848
|
-
result[key] = overrideVal;
|
|
13849
|
-
}
|
|
13850
|
-
}
|
|
13851
|
-
return result;
|
|
13852
|
-
}
|
|
13853
13991
|
function loadPluginConfig(directory) {
|
|
13854
13992
|
const userConfigPath = path.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
13855
13993
|
const projectConfigPath = path.join(directory, ".opencode", CONFIG_FILENAME);
|
|
@@ -13858,7 +13996,7 @@ function loadPluginConfig(directory) {
|
|
|
13858
13996
|
const loadedFromFile = rawUserConfig !== null || rawProjectConfig !== null;
|
|
13859
13997
|
let mergedRaw = rawUserConfig ?? {};
|
|
13860
13998
|
if (rawProjectConfig) {
|
|
13861
|
-
mergedRaw =
|
|
13999
|
+
mergedRaw = deepMerge(mergedRaw, rawProjectConfig);
|
|
13862
14000
|
}
|
|
13863
14001
|
const result = PluginConfigSchema.safeParse(mergedRaw);
|
|
13864
14002
|
if (!result.success) {
|
|
@@ -13866,20 +14004,26 @@ function loadPluginConfig(directory) {
|
|
|
13866
14004
|
const userResult = PluginConfigSchema.safeParse(rawUserConfig);
|
|
13867
14005
|
if (userResult.success) {
|
|
13868
14006
|
console.warn("[opencode-swarm] Project config ignored due to validation errors. Using user config.");
|
|
13869
|
-
return
|
|
14007
|
+
return userResult.data;
|
|
13870
14008
|
}
|
|
13871
14009
|
}
|
|
13872
14010
|
console.warn("[opencode-swarm] Merged config validation failed:");
|
|
13873
14011
|
console.warn(result.error.format());
|
|
13874
14012
|
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13875
|
-
return {
|
|
13876
|
-
|
|
13877
|
-
|
|
13878
|
-
inject_phase_reminders: true,
|
|
13879
|
-
_loadedFromFile: false
|
|
13880
|
-
};
|
|
14013
|
+
return PluginConfigSchema.parse({
|
|
14014
|
+
guardrails: { enabled: false }
|
|
14015
|
+
});
|
|
13881
14016
|
}
|
|
13882
|
-
return
|
|
14017
|
+
return result.data;
|
|
14018
|
+
}
|
|
14019
|
+
function loadPluginConfigWithMeta(directory) {
|
|
14020
|
+
const userConfigPath = path.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
14021
|
+
const projectConfigPath = path.join(directory, ".opencode", CONFIG_FILENAME);
|
|
14022
|
+
const rawUserConfig = loadRawConfigFromPath(userConfigPath);
|
|
14023
|
+
const rawProjectConfig = loadRawConfigFromPath(projectConfigPath);
|
|
14024
|
+
const loadedFromFile = rawUserConfig !== null || rawProjectConfig !== null;
|
|
14025
|
+
const config2 = loadPluginConfig(directory);
|
|
14026
|
+
return { config: config2, loadedFromFile };
|
|
13883
14027
|
}
|
|
13884
14028
|
function loadAgentPrompt(agentName) {
|
|
13885
14029
|
const promptsDir = path.join(getUserConfigDir(), "opencode", PROMPTS_DIR_NAME);
|
|
@@ -13902,58 +14046,6 @@ function loadAgentPrompt(agentName) {
|
|
|
13902
14046
|
}
|
|
13903
14047
|
return result;
|
|
13904
14048
|
}
|
|
13905
|
-
|
|
13906
|
-
// src/config/constants.ts
|
|
13907
|
-
var QA_AGENTS = ["reviewer", "critic"];
|
|
13908
|
-
var PIPELINE_AGENTS = ["explorer", "coder", "test_engineer"];
|
|
13909
|
-
var ORCHESTRATOR_NAME = "architect";
|
|
13910
|
-
var ALL_SUBAGENT_NAMES = [
|
|
13911
|
-
"sme",
|
|
13912
|
-
"docs",
|
|
13913
|
-
"designer",
|
|
13914
|
-
...QA_AGENTS,
|
|
13915
|
-
...PIPELINE_AGENTS
|
|
13916
|
-
];
|
|
13917
|
-
var ALL_AGENT_NAMES = [
|
|
13918
|
-
ORCHESTRATOR_NAME,
|
|
13919
|
-
...ALL_SUBAGENT_NAMES
|
|
13920
|
-
];
|
|
13921
|
-
var DEFAULT_MODELS = {
|
|
13922
|
-
architect: "anthropic/claude-sonnet-4-5",
|
|
13923
|
-
explorer: "google/gemini-2.0-flash",
|
|
13924
|
-
coder: "anthropic/claude-sonnet-4-5",
|
|
13925
|
-
test_engineer: "google/gemini-2.0-flash",
|
|
13926
|
-
sme: "google/gemini-2.0-flash",
|
|
13927
|
-
reviewer: "google/gemini-2.0-flash",
|
|
13928
|
-
critic: "google/gemini-2.0-flash",
|
|
13929
|
-
docs: "google/gemini-2.0-flash",
|
|
13930
|
-
designer: "google/gemini-2.0-flash",
|
|
13931
|
-
default: "google/gemini-2.0-flash"
|
|
13932
|
-
};
|
|
13933
|
-
var DEFAULT_SCORING_CONFIG = {
|
|
13934
|
-
enabled: false,
|
|
13935
|
-
max_candidates: 100,
|
|
13936
|
-
weights: {
|
|
13937
|
-
phase: 1,
|
|
13938
|
-
current_task: 2,
|
|
13939
|
-
blocked_task: 1.5,
|
|
13940
|
-
recent_failure: 2.5,
|
|
13941
|
-
recent_success: 0.5,
|
|
13942
|
-
evidence_presence: 1,
|
|
13943
|
-
decision_recency: 1.5,
|
|
13944
|
-
dependency_proximity: 1
|
|
13945
|
-
},
|
|
13946
|
-
decision_decay: {
|
|
13947
|
-
mode: "exponential",
|
|
13948
|
-
half_life_hours: 24
|
|
13949
|
-
},
|
|
13950
|
-
token_ratios: {
|
|
13951
|
-
prose: 0.25,
|
|
13952
|
-
code: 0.4,
|
|
13953
|
-
markdown: 0.3,
|
|
13954
|
-
json: 0.35
|
|
13955
|
-
}
|
|
13956
|
-
};
|
|
13957
14049
|
// src/config/plan-schema.ts
|
|
13958
14050
|
var TaskStatusSchema = exports_external.enum([
|
|
13959
14051
|
"pending",
|
|
@@ -13999,80 +14091,6 @@ var PlanSchema = exports_external.object({
|
|
|
13999
14091
|
phases: exports_external.array(PhaseSchema).min(1),
|
|
14000
14092
|
migration_status: MigrationStatusSchema.optional()
|
|
14001
14093
|
});
|
|
14002
|
-
// src/config/evidence-schema.ts
|
|
14003
|
-
var EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
|
|
14004
|
-
var EVIDENCE_MAX_PATCH_BYTES = 5 * 1024 * 1024;
|
|
14005
|
-
var EVIDENCE_MAX_TASK_BYTES = 20 * 1024 * 1024;
|
|
14006
|
-
var EvidenceTypeSchema = exports_external.enum([
|
|
14007
|
-
"review",
|
|
14008
|
-
"test",
|
|
14009
|
-
"diff",
|
|
14010
|
-
"approval",
|
|
14011
|
-
"note"
|
|
14012
|
-
]);
|
|
14013
|
-
var EvidenceVerdictSchema = exports_external.enum([
|
|
14014
|
-
"pass",
|
|
14015
|
-
"fail",
|
|
14016
|
-
"approved",
|
|
14017
|
-
"rejected",
|
|
14018
|
-
"info"
|
|
14019
|
-
]);
|
|
14020
|
-
var BaseEvidenceSchema = exports_external.object({
|
|
14021
|
-
task_id: exports_external.string().min(1),
|
|
14022
|
-
type: EvidenceTypeSchema,
|
|
14023
|
-
timestamp: exports_external.string().datetime(),
|
|
14024
|
-
agent: exports_external.string().min(1),
|
|
14025
|
-
verdict: EvidenceVerdictSchema,
|
|
14026
|
-
summary: exports_external.string().min(1),
|
|
14027
|
-
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
14028
|
-
});
|
|
14029
|
-
var ReviewEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14030
|
-
type: exports_external.literal("review"),
|
|
14031
|
-
risk: exports_external.enum(["low", "medium", "high", "critical"]),
|
|
14032
|
-
issues: exports_external.array(exports_external.object({
|
|
14033
|
-
severity: exports_external.enum(["error", "warning", "info"]),
|
|
14034
|
-
message: exports_external.string().min(1),
|
|
14035
|
-
file: exports_external.string().optional(),
|
|
14036
|
-
line: exports_external.number().int().optional()
|
|
14037
|
-
})).default([])
|
|
14038
|
-
});
|
|
14039
|
-
var TestEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14040
|
-
type: exports_external.literal("test"),
|
|
14041
|
-
tests_passed: exports_external.number().int().min(0),
|
|
14042
|
-
tests_failed: exports_external.number().int().min(0),
|
|
14043
|
-
test_file: exports_external.string().optional(),
|
|
14044
|
-
failures: exports_external.array(exports_external.object({
|
|
14045
|
-
name: exports_external.string().min(1),
|
|
14046
|
-
message: exports_external.string().min(1)
|
|
14047
|
-
})).default([])
|
|
14048
|
-
});
|
|
14049
|
-
var DiffEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14050
|
-
type: exports_external.literal("diff"),
|
|
14051
|
-
files_changed: exports_external.array(exports_external.string()).default([]),
|
|
14052
|
-
additions: exports_external.number().int().min(0).default(0),
|
|
14053
|
-
deletions: exports_external.number().int().min(0).default(0),
|
|
14054
|
-
patch_path: exports_external.string().optional()
|
|
14055
|
-
});
|
|
14056
|
-
var ApprovalEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14057
|
-
type: exports_external.literal("approval")
|
|
14058
|
-
});
|
|
14059
|
-
var NoteEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14060
|
-
type: exports_external.literal("note")
|
|
14061
|
-
});
|
|
14062
|
-
var EvidenceSchema = exports_external.discriminatedUnion("type", [
|
|
14063
|
-
ReviewEvidenceSchema,
|
|
14064
|
-
TestEvidenceSchema,
|
|
14065
|
-
DiffEvidenceSchema,
|
|
14066
|
-
ApprovalEvidenceSchema,
|
|
14067
|
-
NoteEvidenceSchema
|
|
14068
|
-
]);
|
|
14069
|
-
var EvidenceBundleSchema = exports_external.object({
|
|
14070
|
-
schema_version: exports_external.literal("1.0.0"),
|
|
14071
|
-
task_id: exports_external.string().min(1),
|
|
14072
|
-
entries: exports_external.array(EvidenceSchema).default([]),
|
|
14073
|
-
created_at: exports_external.string().datetime(),
|
|
14074
|
-
updated_at: exports_external.string().datetime()
|
|
14075
|
-
});
|
|
14076
14094
|
// src/agents/architect.ts
|
|
14077
14095
|
var ARCHITECT_PROMPT = `You are Architect - orchestrator of a multi-agent swarm.
|
|
14078
14096
|
|
|
@@ -15434,6 +15452,7 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
|
|
|
15434
15452
|
windows: {}
|
|
15435
15453
|
};
|
|
15436
15454
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
15455
|
+
swarmState.activeAgent.set(sessionId, agentName);
|
|
15437
15456
|
}
|
|
15438
15457
|
function ensureAgentSession(sessionId, agentName) {
|
|
15439
15458
|
const now = Date.now();
|
|
@@ -17252,7 +17271,7 @@ function createGuardrailsHooks(config2) {
|
|
|
17252
17271
|
return;
|
|
17253
17272
|
}
|
|
17254
17273
|
}
|
|
17255
|
-
const agentName = swarmState.activeAgent.get(input.sessionID);
|
|
17274
|
+
const agentName = swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME;
|
|
17256
17275
|
const session = ensureAgentSession(input.sessionID, agentName);
|
|
17257
17276
|
const resolvedName = stripKnownSwarmPrefix(session.agentName);
|
|
17258
17277
|
if (resolvedName === ORCHESTRATOR_NAME) {
|
|
@@ -30766,9 +30785,39 @@ var gitingest = tool({
|
|
|
30766
30785
|
return fetchGitingest(args);
|
|
30767
30786
|
}
|
|
30768
30787
|
});
|
|
30788
|
+
// src/tools/retrieve-summary.ts
|
|
30789
|
+
var RETRIEVE_MAX_BYTES = 10 * 1024 * 1024;
|
|
30790
|
+
var retrieve_summary = tool({
|
|
30791
|
+
description: "Retrieve the full content of a stored tool output summary by its ID (e.g. S1, S2). Use this when a prior tool output was summarized and you need the full content.",
|
|
30792
|
+
args: {
|
|
30793
|
+
id: tool.schema.string().describe("The summary ID to retrieve (e.g. S1, S2, S99). Must match pattern S followed by digits.")
|
|
30794
|
+
},
|
|
30795
|
+
async execute(args, context) {
|
|
30796
|
+
const directory = context.directory;
|
|
30797
|
+
let sanitizedId;
|
|
30798
|
+
try {
|
|
30799
|
+
sanitizedId = sanitizeSummaryId(args.id);
|
|
30800
|
+
} catch {
|
|
30801
|
+
return "Error: invalid summary ID format. Expected format: S followed by digits (e.g. S1, S2, S99).";
|
|
30802
|
+
}
|
|
30803
|
+
let fullOutput;
|
|
30804
|
+
try {
|
|
30805
|
+
fullOutput = await loadFullOutput(directory, sanitizedId);
|
|
30806
|
+
} catch {
|
|
30807
|
+
return "Error: failed to retrieve summary.";
|
|
30808
|
+
}
|
|
30809
|
+
if (fullOutput === null) {
|
|
30810
|
+
return `Summary \`${sanitizedId}\` not found. Use a valid summary ID (e.g. S1, S2).`;
|
|
30811
|
+
}
|
|
30812
|
+
if (fullOutput.length > RETRIEVE_MAX_BYTES) {
|
|
30813
|
+
return `Error: summary content exceeds maximum size limit (10 MB).`;
|
|
30814
|
+
}
|
|
30815
|
+
return fullOutput;
|
|
30816
|
+
}
|
|
30817
|
+
});
|
|
30769
30818
|
// src/index.ts
|
|
30770
30819
|
var OpenCodeSwarm = async (ctx) => {
|
|
30771
|
-
const config3 =
|
|
30820
|
+
const { config: config3, loadedFromFile } = loadPluginConfigWithMeta(ctx.directory);
|
|
30772
30821
|
const agents = getAgentConfigs(config3);
|
|
30773
30822
|
const agentDefinitions = createAgents(config3);
|
|
30774
30823
|
const pipelineHook = createPipelineTrackerHook(config3);
|
|
@@ -30778,7 +30827,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
30778
30827
|
const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])));
|
|
30779
30828
|
const activityHooks = createAgentActivityHooks(config3, ctx.directory);
|
|
30780
30829
|
const delegationGateHandler = createDelegationGateHook(config3);
|
|
30781
|
-
const guardrailsFallback = config3.
|
|
30830
|
+
const guardrailsFallback = config3.guardrails?.enabled === false ? { ...config3.guardrails, enabled: false } : loadedFromFile ? config3.guardrails ?? {} : { ...config3.guardrails, enabled: false };
|
|
30782
30831
|
const guardrailsConfig = GuardrailsConfigSchema.parse(guardrailsFallback);
|
|
30783
30832
|
const delegationHandler = createDelegationTrackerHook(config3, guardrailsConfig.enabled);
|
|
30784
30833
|
const guardrailsHooks = createGuardrailsHooks(guardrailsConfig);
|
|
@@ -30808,7 +30857,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
30808
30857
|
detect_domains,
|
|
30809
30858
|
extract_code_blocks,
|
|
30810
30859
|
gitingest,
|
|
30811
|
-
diff
|
|
30860
|
+
diff,
|
|
30861
|
+
retrieve_summary
|
|
30812
30862
|
},
|
|
30813
30863
|
config: async (opencodeConfig) => {
|
|
30814
30864
|
if (!opencodeConfig.agent) {
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { type DiffErrorResult, type DiffResult, diff } from './diff';
|
|
|
2
2
|
export { detect_domains } from './domain-detector';
|
|
3
3
|
export { extract_code_blocks } from './file-extractor';
|
|
4
4
|
export { fetchGitingest, type GitingestArgs, gitingest } from './gitingest';
|
|
5
|
+
export { retrieve_summary } from './retrieve-summary';
|
package/dist/utils/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.2",
|
|
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",
|