opencode-swarm 7.21.5 → 7.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +17 -2
- package/dist/config/schema.d.ts +1 -0
- package/dist/index.js +141 -5
- package/package.json +1 -1
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.
|
|
37
|
+
version: "7.22.1",
|
|
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",
|
|
@@ -17455,6 +17455,14 @@ var init_schema = __esm(() => {
|
|
|
17455
17455
|
const trimmed = v.trim();
|
|
17456
17456
|
return trimmed === "" ? undefined : trimmed;
|
|
17457
17457
|
}),
|
|
17458
|
+
auto_select_architect: exports_external.union([exports_external.boolean(), exports_external.string()]).optional().transform((v) => {
|
|
17459
|
+
if (v === undefined)
|
|
17460
|
+
return;
|
|
17461
|
+
if (typeof v === "boolean")
|
|
17462
|
+
return v;
|
|
17463
|
+
const trimmed = v.trim();
|
|
17464
|
+
return trimmed === "" ? false : trimmed;
|
|
17465
|
+
}),
|
|
17458
17466
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
17459
17467
|
max_iterations: exports_external.number().min(1).max(10).default(5),
|
|
17460
17468
|
pipeline: PipelineConfigSchema.optional(),
|
|
@@ -21015,9 +21023,10 @@ var init_guardrails = __esm(() => {
|
|
|
21015
21023
|
function clearPendingCoderScope() {
|
|
21016
21024
|
pendingCoderScopeByTaskId.clear();
|
|
21017
21025
|
}
|
|
21018
|
-
var pendingCoderScopeByTaskId;
|
|
21026
|
+
var pendingCoderScopeByTaskId, ACTIVE_PARALLEL_TASK_STATES;
|
|
21019
21027
|
var init_delegation_gate = __esm(() => {
|
|
21020
21028
|
init_schema();
|
|
21029
|
+
init_manager();
|
|
21021
21030
|
init_state();
|
|
21022
21031
|
init_telemetry();
|
|
21023
21032
|
init_logger();
|
|
@@ -21026,6 +21035,12 @@ var init_delegation_gate = __esm(() => {
|
|
|
21026
21035
|
init_normalize_tool_name();
|
|
21027
21036
|
init_utils2();
|
|
21028
21037
|
pendingCoderScopeByTaskId = new Map;
|
|
21038
|
+
ACTIVE_PARALLEL_TASK_STATES = new Set([
|
|
21039
|
+
"coder_delegated",
|
|
21040
|
+
"pre_check_passed",
|
|
21041
|
+
"reviewer_run",
|
|
21042
|
+
"tests_run"
|
|
21043
|
+
]);
|
|
21029
21044
|
});
|
|
21030
21045
|
|
|
21031
21046
|
// src/state/agent-run-context.ts
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -828,6 +828,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
828
828
|
fallback_models: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
829
829
|
}, z.core.$strip>>>;
|
|
830
830
|
default_agent: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
|
|
831
|
+
auto_select_architect: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>, z.ZodTransform<string | boolean | undefined, string | boolean | undefined>>;
|
|
831
832
|
swarms: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
832
833
|
name: z.ZodOptional<z.ZodString>;
|
|
833
834
|
agents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var package_default;
|
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "opencode-swarm",
|
|
36
|
-
version: "7.
|
|
36
|
+
version: "7.22.1",
|
|
37
37
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
38
38
|
main: "dist/index.js",
|
|
39
39
|
types: "dist/index.d.ts",
|
|
@@ -15668,6 +15668,14 @@ var init_schema = __esm(() => {
|
|
|
15668
15668
|
const trimmed = v.trim();
|
|
15669
15669
|
return trimmed === "" ? undefined : trimmed;
|
|
15670
15670
|
}),
|
|
15671
|
+
auto_select_architect: exports_external.union([exports_external.boolean(), exports_external.string()]).optional().transform((v) => {
|
|
15672
|
+
if (v === undefined)
|
|
15673
|
+
return;
|
|
15674
|
+
if (typeof v === "boolean")
|
|
15675
|
+
return v;
|
|
15676
|
+
const trimmed = v.trim();
|
|
15677
|
+
return trimmed === "" ? false : trimmed;
|
|
15678
|
+
}),
|
|
15671
15679
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
15672
15680
|
max_iterations: exports_external.number().min(1).max(10).default(5),
|
|
15673
15681
|
pipeline: PipelineConfigSchema.optional(),
|
|
@@ -26766,6 +26774,70 @@ function extractPlanTaskId(text) {
|
|
|
26766
26774
|
function getSeedTaskId(session) {
|
|
26767
26775
|
return session.currentTaskId ?? session.lastCoderDelegationTaskId;
|
|
26768
26776
|
}
|
|
26777
|
+
function isTaskCompletedForParallelGuidance(task) {
|
|
26778
|
+
const status = task.status ?? "pending";
|
|
26779
|
+
return status === "completed" || status === "closed";
|
|
26780
|
+
}
|
|
26781
|
+
async function buildParallelExecutionGuidance(directory, sessionID, session) {
|
|
26782
|
+
if (!directory)
|
|
26783
|
+
return null;
|
|
26784
|
+
const plan = await loadPlanJsonOnly(directory);
|
|
26785
|
+
if (!plan) {
|
|
26786
|
+
return null;
|
|
26787
|
+
}
|
|
26788
|
+
const profile = plan.execution_profile;
|
|
26789
|
+
const enabled = profile?.parallelization_enabled === true;
|
|
26790
|
+
const maxConcurrent = profile?.max_concurrent_tasks ?? 1;
|
|
26791
|
+
if (!enabled || maxConcurrent <= 1)
|
|
26792
|
+
return null;
|
|
26793
|
+
if (hasActiveLeanTurbo(sessionID)) {
|
|
26794
|
+
return "[NEXT] Lean Turbo is active; use lean_turbo_run_phase and Lean Turbo lane guidance instead of standard execution-profile slot filling.";
|
|
26795
|
+
}
|
|
26796
|
+
const currentPhase = plan.current_phase !== undefined ? plan.phases.find((phase) => phase.id === plan.current_phase) : plan.phases.find((phase) => !isParallelGuidancePhaseComplete(phase));
|
|
26797
|
+
if (!currentPhase)
|
|
26798
|
+
return null;
|
|
26799
|
+
const tasks = currentPhase.tasks;
|
|
26800
|
+
if (tasks.length === 0)
|
|
26801
|
+
return null;
|
|
26802
|
+
const allTasks = plan.phases.flatMap((phase) => phase.tasks);
|
|
26803
|
+
const completed = new Set;
|
|
26804
|
+
for (const task of allTasks) {
|
|
26805
|
+
const taskId = task.id;
|
|
26806
|
+
if (isTaskCompletedForParallelGuidance(task))
|
|
26807
|
+
completed.add(taskId);
|
|
26808
|
+
if (getTaskState(session, taskId) === "complete")
|
|
26809
|
+
completed.add(taskId);
|
|
26810
|
+
}
|
|
26811
|
+
const occupied = new Set;
|
|
26812
|
+
for (const task of allTasks) {
|
|
26813
|
+
const taskId = task.id;
|
|
26814
|
+
if (task.status === "in_progress")
|
|
26815
|
+
occupied.add(taskId);
|
|
26816
|
+
const state = getTaskState(session, taskId);
|
|
26817
|
+
if (ACTIVE_PARALLEL_TASK_STATES.has(state))
|
|
26818
|
+
occupied.add(taskId);
|
|
26819
|
+
}
|
|
26820
|
+
const availableSlots = Math.max(0, maxConcurrent - occupied.size);
|
|
26821
|
+
if (availableSlots <= 0) {
|
|
26822
|
+
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; all standard execution slots are occupied. Continue current active task gates before starting more coder work.`;
|
|
26823
|
+
}
|
|
26824
|
+
const eligible = tasks.filter((task) => {
|
|
26825
|
+
const taskId = task.id;
|
|
26826
|
+
const status = task.status ?? "pending";
|
|
26827
|
+
if (status !== "pending")
|
|
26828
|
+
return false;
|
|
26829
|
+
if (occupied.has(taskId))
|
|
26830
|
+
return false;
|
|
26831
|
+
return task.depends.every((dep) => completed.has(dep));
|
|
26832
|
+
}).map((task) => task.id).slice(0, availableSlots);
|
|
26833
|
+
if (eligible.length === 0) {
|
|
26834
|
+
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; no dependency-ready pending tasks are available for a new coder slot. Continue the current task/gate.`;
|
|
26835
|
+
}
|
|
26836
|
+
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; ${occupied.size} slot(s) occupied. Eligible now: ${eligible.join(", ")}. [NEXT] dispatch up to ${availableSlots} eligible coder task(s) before waiting; preserve ONE task per coder call and call declare_scope for each task.`;
|
|
26837
|
+
}
|
|
26838
|
+
function isParallelGuidancePhaseComplete(phase) {
|
|
26839
|
+
return phase.status === "complete" || phase.status === "completed" || phase.status === "closed";
|
|
26840
|
+
}
|
|
26769
26841
|
async function getEvidenceTaskId(session, directory) {
|
|
26770
26842
|
const primary = session.currentTaskId ?? session.lastCoderDelegationTaskId;
|
|
26771
26843
|
if (primary)
|
|
@@ -27271,15 +27343,16 @@ ${trimComment}${after}`;
|
|
|
27271
27343
|
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(deliberationSessionID)) {} else {
|
|
27272
27344
|
const deliberationSession = ensureAgentSession(deliberationSessionID);
|
|
27273
27345
|
const lastGate = deliberationSession.lastGateOutcome;
|
|
27346
|
+
const parallelGuidance = await buildParallelExecutionGuidance(directory, deliberationSessionID, deliberationSession);
|
|
27274
27347
|
let guidance;
|
|
27275
27348
|
if (lastGate?.taskId) {
|
|
27276
27349
|
const gateResult = lastGate.passed ? "PASSED" : "FAILED";
|
|
27277
27350
|
const sanitizedGate = lastGate.gate.replace(/</g, "<").replace(/>/g, ">").replace(/\[ \]/g, "()").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
|
|
27278
27351
|
const sanitizedTaskId = lastGate.taskId.replace(/</g, "<").replace(/>/g, ">").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 32);
|
|
27279
27352
|
guidance = `[Last gate: ${sanitizedGate} ${gateResult} for task ${sanitizedTaskId}]
|
|
27280
|
-
[NEXT] Execute the next gate for the current task
|
|
27353
|
+
${parallelGuidance ?? "[NEXT] Execute the next gate for the current task."}`;
|
|
27281
27354
|
} else {
|
|
27282
|
-
guidance = "[NEXT] Begin the first plan task and run gates sequentially.";
|
|
27355
|
+
guidance = parallelGuidance ?? "[NEXT] Begin the first plan task and run gates sequentially.";
|
|
27283
27356
|
}
|
|
27284
27357
|
const systemMsgIdx = messages.findIndex((m) => m && m.info?.role === "system");
|
|
27285
27358
|
const insertIdx = systemMsgIdx >= 0 ? systemMsgIdx + 1 : 0;
|
|
@@ -27372,9 +27445,10 @@ ${warningLines.join(`
|
|
|
27372
27445
|
toolAfter
|
|
27373
27446
|
};
|
|
27374
27447
|
}
|
|
27375
|
-
var pendingCoderScopeByTaskId;
|
|
27448
|
+
var pendingCoderScopeByTaskId, ACTIVE_PARALLEL_TASK_STATES;
|
|
27376
27449
|
var init_delegation_gate = __esm(() => {
|
|
27377
27450
|
init_schema();
|
|
27451
|
+
init_manager();
|
|
27378
27452
|
init_state();
|
|
27379
27453
|
init_telemetry();
|
|
27380
27454
|
init_logger();
|
|
@@ -27383,6 +27457,12 @@ var init_delegation_gate = __esm(() => {
|
|
|
27383
27457
|
init_normalize_tool_name();
|
|
27384
27458
|
init_utils2();
|
|
27385
27459
|
pendingCoderScopeByTaskId = new Map;
|
|
27460
|
+
ACTIVE_PARALLEL_TASK_STATES = new Set([
|
|
27461
|
+
"coder_delegated",
|
|
27462
|
+
"pre_check_passed",
|
|
27463
|
+
"reviewer_run",
|
|
27464
|
+
"tests_run"
|
|
27465
|
+
]);
|
|
27386
27466
|
});
|
|
27387
27467
|
|
|
27388
27468
|
// src/state/agent-run-context.ts
|
|
@@ -62845,6 +62925,8 @@ If a tool modifies a file, it is a CODER tool. Delegate.
|
|
|
62845
62925
|
- Rationale: declare_scope persists the allowed set to disk (.swarm/scopes/scope-\${taskId}.json) so it survives cross-process delegation. Without a call, the coder process reads an empty scope and every Edit/Write is denied.
|
|
62846
62926
|
<!-- BEHAVIORAL_GUIDANCE_END -->
|
|
62847
62927
|
2. ONE agent per message. Send, STOP, wait for response.
|
|
62928
|
+
Exception: Stage B reviewer/test_engineer gate agents for the SAME completed coder task may be dispatched together before waiting when both gates are required.
|
|
62929
|
+
This exception NEVER applies to coder delegations. Preserve ONE task per coder call.
|
|
62848
62930
|
3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
|
|
62849
62931
|
3a. PRE-DELEGATION SCOPE CALL (required): BEFORE every {{AGENT_PREFIX}}coder delegation, you MUST call \`declare_scope\` with { taskId, files } listing the exact file(s) this task will modify (including generated/lockfile paths). No \`declare_scope\` call → no coder delegation. See Rule 1a.
|
|
62850
62932
|
<!-- BEHAVIORAL_GUIDANCE_START -->
|
|
@@ -62985,7 +63067,7 @@ VERIFICATION PROTOCOL: After the coder reports DONE, and before running Stage B
|
|
|
62985
63067
|
The reviewer's verdict MUST include a REUSE_RE_VERIFICATION field — do NOT accept an APPROVED verdict without it. Validate the field value against context: if the coder's EXPORTS_ADDED was non-empty, REUSE_RE_VERIFICATION must be VERIFIED or DUPLICATION_DETECTED (not SKIPPED). If EXPORTS_ADDED was "none", REUSE_RE_VERIFICATION must be SKIPPED.
|
|
62986
63068
|
Stage B runs by default for TIER 1-3 classifications. Stage A passing does not satisfy Stage B.
|
|
62987
63069
|
Stage B is where logic errors, security flaws, edge cases, and behavioral bugs are caught.
|
|
62988
|
-
You MUST delegate to each Stage B agent
|
|
63070
|
+
You MUST delegate to each required Stage B agent. For the standard reviewer + test_engineer pair, dispatch both before waiting so Stage B actually runs in parallel.
|
|
62989
63071
|
|
|
62990
63072
|
Stage B (reviewer + test_engineer) **always runs per-task** regardless of council mode — it is never replaced, never omitted, never deferred. When \`council_mode\` is enabled in the QA gate profile, a **phase-level** council review is additionally required before calling \`phase_complete\`: dispatch all 5 council members, collect their verdicts, call \`submit_phase_council_verdicts\`, then call \`phase_complete\` (Gate 5 validates the resulting \`phase-council.json\` evidence). Stage A (\`pre_check_batch\`) still runs as the pre-review gate for each task.
|
|
62991
63073
|
|
|
@@ -105323,11 +105405,65 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
105323
105405
|
swarm_command: createSwarmCommandTool(agentDefinitionMap)
|
|
105324
105406
|
},
|
|
105325
105407
|
config: async (opencodeConfig) => {
|
|
105408
|
+
if (!opencodeConfig.agent || typeof opencodeConfig.agent !== "object") {
|
|
105409
|
+
opencodeConfig.agent = {};
|
|
105410
|
+
}
|
|
105326
105411
|
if (!opencodeConfig.agent) {
|
|
105327
105412
|
opencodeConfig.agent = { ...agents };
|
|
105328
105413
|
} else {
|
|
105329
105414
|
Object.assign(opencodeConfig.agent, agents);
|
|
105330
105415
|
}
|
|
105416
|
+
const autoSelect = config3?.auto_select_architect;
|
|
105417
|
+
if (autoSelect) {
|
|
105418
|
+
const hasArchitect = Object.keys(agents).some((name2) => stripKnownSwarmPrefix(name2) === "architect");
|
|
105419
|
+
if (hasArchitect) {
|
|
105420
|
+
for (const builtin of ["build", "plan"]) {
|
|
105421
|
+
const existing = opencodeConfig.agent?.[builtin];
|
|
105422
|
+
if (existing && typeof existing === "object" && existing.disable === true) {
|
|
105423
|
+
continue;
|
|
105424
|
+
}
|
|
105425
|
+
opencodeConfig.agent[builtin] = {
|
|
105426
|
+
...existing && typeof existing === "object" ? existing : {},
|
|
105427
|
+
disable: true
|
|
105428
|
+
};
|
|
105429
|
+
}
|
|
105430
|
+
if (autoSelect === true) {
|
|
105431
|
+
const primaryArchitects = Object.entries(agents).filter(([name2, cfg]) => stripKnownSwarmPrefix(name2) === "architect" && cfg.mode === "primary");
|
|
105432
|
+
if (primaryArchitects.length > 1) {
|
|
105433
|
+
const names = primaryArchitects.map(([n]) => n).join(", ");
|
|
105434
|
+
addDeferredWarning(`[swarm] auto_select_architect is true but ${primaryArchitects.length} architect agents are primary (${names}). Consider setting auto_select_architect to a specific agent name.`);
|
|
105435
|
+
}
|
|
105436
|
+
}
|
|
105437
|
+
if (typeof autoSelect === "string" && autoSelect !== "") {
|
|
105438
|
+
const targetName = autoSelect;
|
|
105439
|
+
const targetIsArchitect = Object.hasOwn(agents, targetName) && stripKnownSwarmPrefix(targetName) === "architect";
|
|
105440
|
+
if (targetIsArchitect) {
|
|
105441
|
+
for (const [name2, cfg] of Object.entries(agents)) {
|
|
105442
|
+
if (stripKnownSwarmPrefix(name2) === "architect" && name2 !== targetName) {
|
|
105443
|
+
if (opencodeConfig.agent && typeof opencodeConfig.agent === "object") {
|
|
105444
|
+
opencodeConfig.agent[name2] = {
|
|
105445
|
+
...cfg && typeof cfg === "object" ? cfg : {},
|
|
105446
|
+
mode: "subagent"
|
|
105447
|
+
};
|
|
105448
|
+
}
|
|
105449
|
+
}
|
|
105450
|
+
}
|
|
105451
|
+
if (opencodeConfig.agent && typeof opencodeConfig.agent === "object") {
|
|
105452
|
+
const targetExisting = opencodeConfig.agent[targetName];
|
|
105453
|
+
opencodeConfig.agent[targetName] = {
|
|
105454
|
+
...targetExisting && typeof targetExisting === "object" ? targetExisting : {},
|
|
105455
|
+
...agents[targetName] && typeof agents[targetName] === "object" ? agents[targetName] : {},
|
|
105456
|
+
mode: "primary"
|
|
105457
|
+
};
|
|
105458
|
+
}
|
|
105459
|
+
} else {
|
|
105460
|
+
addDeferredWarning(`[swarm] auto_select_architect is set to "${targetName}" but that is not a known architect agent. No architect demotion applied.`);
|
|
105461
|
+
}
|
|
105462
|
+
}
|
|
105463
|
+
} else {
|
|
105464
|
+
addDeferredWarning("[swarm] auto_select_architect is enabled but no architect agents were found in the generated set. The option has no effect.");
|
|
105465
|
+
}
|
|
105466
|
+
}
|
|
105331
105467
|
opencodeConfig.command = {
|
|
105332
105468
|
...opencodeConfig.command || {},
|
|
105333
105469
|
swarm: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.22.1",
|
|
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",
|