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 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.21.5",
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
@@ -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.21.5",
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, "&lt;").replace(/>/g, "&gt;").replace(/\[ \]/g, "()").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
27278
27351
  const sanitizedTaskId = lastGate.taskId.replace(/</g, "&lt;").replace(/>/g, "&gt;").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 and wait for their response.
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.21.5",
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",