opencode-swarm 7.65.3 → 7.66.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.
@@ -78,6 +78,7 @@ Present the eleven gates with their defaults (DEFAULT_QA_GATES), parallel coder
78
78
  Additionally, present these two sub-items as part of the same exchange:
79
79
  - Parallel coders (default: 1, range: 1-4) -- how many coders should run in parallel.
80
80
  - Commit frequency (default: phase-level only) -- optional per-task checkpoint commit after each task completion.
81
+ - auto_proceed (boolean, default: false) -- when true, auto-advance to the next phase without asking "Ready for Phase N+1?"; runtime toggle via /swarm auto-proceed on|off.
81
82
 
82
83
  The user answers all three (gates, parallel coders, commit frequency) in one exchange. Wait for the user's response.
83
84
 
@@ -117,7 +117,14 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
117
117
  6. Do NOT call `phase_complete` or `/swarm close` until `.swarm/evidence/final-council.json` exists with an approved, plan-bound, quorumed final-council verdict. When `final_council` is enabled, `phase_complete` will block until that evidence exists.
118
118
  If enabled but NOT the last phase, skip silently - final council only runs once, after all phases.
119
119
  6. Summarize to user
120
- 7. Ask: "Ready for Phase [N+1]?"
120
+ 7. Check the AUTO_PROCEED STATUS banner (injected into your context by the system-enhancer). The banner shows:
121
+ - `auto-proceed: <on|off>` — the current effective value
122
+ - `source: <session|plan-or-default>` — which side it came from
123
+ - `nudge: <true|false>` — whether the FR-004 first-boundary nudge has already been done
124
+ Then branch:
125
+ - If `auto-proceed: on`: call `phase_complete`, then advance to the first task of the next phase. Do NOT ask the user.
126
+ - If `auto-proceed: off` AND `nudge: false`: after the user confirms the phase transition, suggest enabling auto-proceed. Use the swarm_command tool to record the user's answer: `swarm_command({ command: "auto-proceed", args: ["on"] })` for yes, `swarm_command({ command: "auto-proceed", args: ["off"] })` for no. Either call sets nudge to true and prevents re-nudging.
127
+ - If `auto-proceed: off` AND `nudge: true`: Ask "Ready for Phase [N+1]?" and wait for user confirmation before proceeding.
121
128
 
122
129
  CATASTROPHIC VIOLATION CHECK — ask yourself at EVERY phase boundary (MODE: PHASE-WRAP):
123
130
  "Have I delegated to the active swarm's reviewer agent at least once this phase?"
@@ -49,6 +49,7 @@ Present the eleven gates with their defaults (DEFAULT_QA_GATES), parallel coder
49
49
  Additionally, present these two sub-items as part of the same exchange:
50
50
  - Parallel coders (default: 1, range: 1-4) -- how many coders should run in parallel.
51
51
  - Commit frequency (default: phase-level only) -- optional per-task checkpoint commit after each task completion.
52
+ - auto_proceed (boolean, default: false) -- when true, auto-advance to the next phase without asking "Ready for Phase N+1?"; runtime toggle via /swarm auto-proceed on|off.
52
53
 
53
54
  The user answers all three (gates, parallel coders, commit frequency) in one exchange. Wait for the user's response.
54
55
 
@@ -5,6 +5,8 @@ description: >
5
5
  Use when addressing pasted PR feedback, GitHub review comments or threads,
6
6
  requested changes, CI/check failures, merge conflicts, stale PR branches, or
7
7
  PR follow-up work that must close all known issues without dropping findings.
8
+ Supports multi-round bot reviews (the repository's bot posts a new review
9
+ after every push) via the iterative pattern documented in the body.
8
10
  ---
9
11
 
10
12
  # Swarm PR Feedback
@@ -14,6 +16,51 @@ Use this skill to close known PR feedback. This is not a fresh broad PR review.
14
16
  feedback surfaces, verifies each claim, clusters related problems, fixes confirmed
15
17
  issues, validates the branch, and reports closure status for every item.
16
18
 
19
+ ## Multi-Round Bot Reviews (Iterative Pattern)
20
+
21
+ The repository's bot reviewer (`hermes-pr-review` / Qwen3.6 + Gemma-4 dual-model)
22
+ posts a new review comment after **every push** to the PR branch, not just the
23
+ final state. Expect N rounds of review for N pushes, and budget for it.
24
+
25
+ **Round N+1 deltas vs Round N:**
26
+ - Fresh `FB-###` ledger IDs for new findings (do not reuse IDs from earlier rounds)
27
+ - Findings from prior rounds that remain unfixed will reappear with the same evidence
28
+ - Findings you marked DISPROVED with new evidence may reappear if the bot disagrees
29
+ - New findings may be introduced that the prior round did not see (the bot's read scope
30
+ is the new commit, not the full diff history)
31
+
32
+ **Operating principles for multi-round triage:**
33
+
34
+ 1. **Continue the ledger, do not start over.** Append to the same `FB-###` counter
35
+ across rounds. Track each finding's state per round (open, fixed, disproved,
36
+ awaiting-decision, repeated).
37
+ 2. **Carry forward unresolved items.** Findings you marked `PARTIAL` or `NEEDS_USER_DECISION`
38
+ in round N will still be open in round N+1. The closure ledger should show their
39
+ evolution (e.g., "PARTIAL round 1 → CONFIRMED round 2 after evidence collected").
40
+ 3. **Apply the 3-strikes-then-defense-in-depth rule.** When the same finding is
41
+ raised 3+ times across rounds, prefer to add the suggested code change with a
42
+ defense-in-depth rationale comment rather than continue to debate. One extra
43
+ condition is cheap; per-round debate is expensive. Document the parent-vs-inner
44
+ relationship inline so future readers see the rationale.
45
+ 4. **Verify bot fix-direction suggestions against actual file structure.** Bots
46
+ read files linearly and can miss parent-block guards. For any "add an X check"
47
+ suggestion, read the surrounding function/block to confirm the check is genuinely
48
+ missing or already exists at a higher scope.
49
+ 5. **Each round produces its own closure ledger as a PR comment.** Prefix with
50
+ "Round N" so the bot and reviewers can see progression. Maintain a running
51
+ summary table at the end of each comment showing totals across rounds
52
+ (confirmed+fixed / disproved / partial / awaiting-decision).
53
+ 6. **Stop the cycle deliberately.** If a finding is disproved with code evidence 3+
54
+ times and the bot keeps re-raising it, leave the comment, post the closure
55
+ ledger with the cumulative evidence, and surface the disagreement to the user
56
+ rather than continuing to push fixes. The user can resolve persistent
57
+ reviewer-AI disagreement.
58
+
59
+ **Why this matters:** Without the multi-round pattern, each round looks like
60
+ "start over, re-triage everything." With it, the rounds become incremental:
61
+ each round's work is bounded by new findings + carried-forward items only.
62
+ This matches how the bot actually behaves and avoids wasted cycles.
63
+
17
64
  ## Operating Stance
18
65
 
19
66
  Treat every review comment, CI failure, bot summary, PR body claim, and pasted note
package/README.md CHANGED
@@ -134,6 +134,8 @@ Swarm has two independent mode systems:
134
134
  | **Lean Turbo** | High | Fast | Parallel lanes for non-conflicting tasks (up to `max_parallel_coders` coders) |
135
135
  | **Full-Auto** | Deterministic policy + critic oversight | Fast | Unattended multi-interaction runs |
136
136
 
137
+ **Auto-proceed** — a session toggle (`/swarm auto-proceed [on|off]`) that skips the "Ready for Phase N+1?" prompt at phase boundaries. Defaults to `false`; set as a plan default via `execution_profile.auto_proceed` during QA GATE SELECTION. Session override always wins. Independent of Full-Auto.
138
+
137
139
  Full-Auto reduces approval friction by deterministically allowing safe operations (read-only tools, in-scope writes, safe shell) and routing every ambiguous or high-risk action (writes to plugin/build/guardrail paths, network, dependency changes, plan/phase mutations, subagent delegation) through the read-only `critic_oversight` agent before it executes. Denials are returned to the agent as structured signals so it can choose a safer path; repeated denials pause the run; phase completion requires an APPROVED oversight record. See [docs/modes.md](docs/modes.md#full-auto) for `mode`, `permission_policy`, `denials`, and `oversight` config keys, fail-closed semantics, and recovery from a paused run.
138
140
 
139
141
  **Project mode** — persistent via `execution_mode` config key:
@@ -144,7 +146,7 @@ Full-Auto reduces approval friction by deterministically allowing safe operation
144
146
  | `balanced` (default) | Standard hooks |
145
147
  | `fast` | Skips compaction service — for short sessions under context pressure |
146
148
 
147
- Switch session modes with `/swarm turbo [on|off]` or `/swarm full-auto [on|off]`. Set project mode in config. Lean Turbo is configured in `turbo.lean.*` in config and composes with all session modes. See [docs/modes.md](docs/modes.md).
149
+ Switch session modes with `/swarm turbo [on|off]` or `/swarm full-auto [on|off]`. Control phase-boundary auto-proceed with `/swarm auto-proceed [on|off]`. Set project mode in config. Lean Turbo is configured in `turbo.lean.*` in config and composes with all session modes. See [docs/modes.md](docs/modes.md).
148
150
 
149
151
  ---
150
152
 
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.65.3",
55
+ version: "7.66.1",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -14700,7 +14700,8 @@ var init_plan_schema = __esm(() => {
14700
14700
  parallelization_enabled: exports_external.boolean().default(false),
14701
14701
  max_concurrent_tasks: exports_external.number().int().min(1).max(64).default(1),
14702
14702
  council_parallel: exports_external.boolean().default(false),
14703
- locked: exports_external.boolean().default(false)
14703
+ locked: exports_external.boolean().default(false),
14704
+ auto_proceed: exports_external.boolean().default(false)
14704
14705
  });
14705
14706
  TaskStatusSchema = exports_external.enum([
14706
14707
  "pending",
@@ -22753,6 +22754,45 @@ var init_state = __esm(() => {
22753
22754
  };
22754
22755
  });
22755
22756
 
22757
+ // src/commands/auto-proceed.ts
22758
+ async function handleAutoProceedCommand(_directory, args, sessionID) {
22759
+ if (!sessionID || sessionID.trim() === "") {
22760
+ return "Error: No active session context. Auto-proceed requires an active session. Use /swarm auto-proceed from within an OpenCode session, or start a session first.";
22761
+ }
22762
+ const session = getAgentSession(sessionID);
22763
+ if (!session) {
22764
+ return "Error: No active session. Auto-proceed requires an active session to operate.";
22765
+ }
22766
+ if (stripKnownSwarmPrefix(session.agentName) !== "architect") {
22767
+ return `Error: Auto-proceed can only be toggled from the architect session. Currently active session is: ${session.agentName}`;
22768
+ }
22769
+ const arg = args[0]?.toLowerCase();
22770
+ const wasUndefinedBefore = session.autoProceedOverride === undefined;
22771
+ let newAutoProceedOverride;
22772
+ if (arg === "on") {
22773
+ newAutoProceedOverride = true;
22774
+ } else if (arg === "off") {
22775
+ newAutoProceedOverride = false;
22776
+ } else if (arg === undefined) {
22777
+ newAutoProceedOverride = !session.autoProceedOverride;
22778
+ } else {
22779
+ return 'Error: Invalid argument for /swarm auto-proceed. Valid options: "on", "off", or omit the argument to toggle.';
22780
+ }
22781
+ session.autoProceedOverride = newAutoProceedOverride;
22782
+ if (arg === "on" || arg === "off" || arg === undefined && wasUndefinedBefore) {
22783
+ session.autoProceedNudgeDone = true;
22784
+ }
22785
+ if (newAutoProceedOverride) {
22786
+ return "Auto-proceed is now ON. Phase boundaries will advance automatically.";
22787
+ } else {
22788
+ return "Auto-proceed is now OFF. You will be asked before advancing to the next phase.";
22789
+ }
22790
+ }
22791
+ var init_auto_proceed = __esm(() => {
22792
+ init_schema();
22793
+ init_state();
22794
+ });
22795
+
22756
22796
  // src/commands/benchmark.ts
22757
22797
  async function handleBenchmarkCommand(directory, args) {
22758
22798
  let cumulative = args.includes("--cumulative");
@@ -48893,6 +48933,12 @@ function serializeAgentSession(s) {
48893
48933
  ...Object.keys(stageBCompletion).length > 0 && { stageBCompletion },
48894
48934
  ...s.maxConcurrencyOverride !== undefined && {
48895
48935
  maxConcurrencyOverride: s.maxConcurrencyOverride
48936
+ },
48937
+ ...s.autoProceedOverride !== undefined && {
48938
+ autoProceedOverride: s.autoProceedOverride
48939
+ },
48940
+ ...s.autoProceedNudgeDone !== undefined && {
48941
+ autoProceedNudgeDone: s.autoProceedNudgeDone
48896
48942
  }
48897
48943
  };
48898
48944
  }
@@ -56263,8 +56309,20 @@ import * as fs24 from "fs";
56263
56309
  import * as path50 from "path";
56264
56310
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
56265
56311
  if (workingDirectory == null || workingDirectory === "") {
56312
+ if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
56313
+ return {
56314
+ success: false,
56315
+ message: "Invalid working_directory: no explicit working_directory was provided and fallbackDirectory is missing or not a string"
56316
+ };
56317
+ }
56266
56318
  return { success: true, directory: fallbackDirectory };
56267
56319
  }
56320
+ if (typeof workingDirectory !== "string") {
56321
+ return {
56322
+ success: false,
56323
+ message: "Invalid working_directory: path must be a string"
56324
+ };
56325
+ }
56268
56326
  if (workingDirectory.includes("\x00")) {
56269
56327
  return {
56270
56328
  success: false,
@@ -56280,14 +56338,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
56280
56338
  };
56281
56339
  }
56282
56340
  }
56283
- const normalizedDir = path50.normalize(workingDirectory);
56284
- const pathParts = normalizedDir.split(path50.sep);
56285
- if (pathParts.includes("..")) {
56341
+ const rawPathParts = workingDirectory.split(path50.sep);
56342
+ if (rawPathParts.includes("..")) {
56286
56343
  return {
56287
56344
  success: false,
56288
56345
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
56289
56346
  };
56290
56347
  }
56348
+ const normalizedDir = path50.normalize(workingDirectory);
56291
56349
  const resolvedDir = path50.resolve(normalizedDir);
56292
56350
  let statResult;
56293
56351
  try {
@@ -56304,6 +56362,9 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
56304
56362
  message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
56305
56363
  };
56306
56364
  }
56365
+ if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
56366
+ return { success: true, directory: resolvedDir };
56367
+ }
56307
56368
  const resolvedFallback = path50.resolve(fallbackDirectory);
56308
56369
  let fallbackExists = false;
56309
56370
  try {
@@ -56312,23 +56373,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
56312
56373
  } catch {
56313
56374
  fallbackExists = false;
56314
56375
  }
56315
- if (workingDirectory != null && workingDirectory !== "") {
56316
- if (fallbackExists) {
56317
- const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path50.sep);
56318
- if (isSubdirectory) {
56319
- return {
56320
- success: false,
56321
- message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
56322
- };
56323
- }
56376
+ if (fallbackExists) {
56377
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path50.sep);
56378
+ if (isSubdirectory) {
56379
+ return {
56380
+ success: false,
56381
+ message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
56382
+ };
56324
56383
  }
56325
- return { success: true, directory: resolvedDir };
56326
- }
56327
- if (resolvedDir !== resolvedFallback) {
56328
- return {
56329
- success: false,
56330
- message: `Invalid working_directory: path resolves to "${resolvedDir}" but fallbackDirectory ` + `"${resolvedFallback}" is not the project root. ` + `This may indicate CWD mismatch. Pass the project root path explicitly.`
56331
- };
56332
56384
  }
56333
56385
  return { success: true, directory: resolvedDir };
56334
56386
  }
@@ -61755,7 +61807,8 @@ var init_tool_policy = __esm(() => {
61755
61807
  "sdd validate",
61756
61808
  "sdd project",
61757
61809
  "sync-plan",
61758
- "export"
61810
+ "export",
61811
+ "auto-proceed"
61759
61812
  ];
61760
61813
  SWARM_COMMAND_TOOL_ALLOWLIST = new Set([
61761
61814
  "agents",
@@ -61784,7 +61837,8 @@ var init_tool_policy = __esm(() => {
61784
61837
  "sdd status",
61785
61838
  "sdd validate",
61786
61839
  "sync-plan",
61787
- "export"
61840
+ "export",
61841
+ "auto-proceed"
61788
61842
  ]);
61789
61843
  HUMAN_ONLY_SWARM_COMMANDS = new Set([
61790
61844
  "acknowledge-spec-drift",
@@ -61878,6 +61932,7 @@ __export(exports_commands, {
61878
61932
  handleCheckpointCommand: () => handleCheckpointCommand,
61879
61933
  handleBrainstormCommand: () => handleBrainstormCommand,
61880
61934
  handleBenchmarkCommand: () => handleBenchmarkCommand,
61935
+ handleAutoProceedCommand: () => handleAutoProceedCommand,
61881
61936
  handleArchiveCommand: () => handleArchiveCommand,
61882
61937
  handleAnalyzeCommand: () => handleAnalyzeCommand,
61883
61938
  handleAgentsCommand: () => handleAgentsCommand,
@@ -62130,6 +62185,7 @@ var init_commands = __esm(() => {
62130
62185
  init_acknowledge_spec_drift();
62131
62186
  init_agents();
62132
62187
  init_archive();
62188
+ init_auto_proceed();
62133
62189
  init_benchmark();
62134
62190
  init_checkpoint2();
62135
62191
  init_close();
@@ -62362,6 +62418,7 @@ var init_registry = __esm(() => {
62362
62418
  init_acknowledge_spec_drift();
62363
62419
  init_agents();
62364
62420
  init_archive();
62421
+ init_auto_proceed();
62365
62422
  init_benchmark();
62366
62423
  init_checkpoint2();
62367
62424
  init_close();
@@ -62839,6 +62896,13 @@ Subcommands:
62839
62896
  details: 'Toggles Full-Auto Mode which enables autonomous execution without confirmation prompts. When enabled, the architect proceeds through implementation steps automatically. Session-scoped \u2014 resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.',
62840
62897
  category: "utility"
62841
62898
  },
62899
+ "auto-proceed": {
62900
+ handler: (ctx) => handleAutoProceedCommand(ctx.directory, ctx.args, ctx.sessionID),
62901
+ description: "Toggle or set auto-proceed override for the active session",
62902
+ args: "[on|off]",
62903
+ category: "config",
62904
+ details: 'Without argument, toggles auto-proceed mode. With "on" or "off", sets the state explicitly.'
62905
+ },
62842
62906
  "write-retro": {
62843
62907
  handler: (ctx) => handleWriteRetroCommand(ctx.directory, ctx.args),
62844
62908
  description: "Write a retrospective evidence bundle for a completed phase <json>",
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Handles the /swarm auto-proceed command.
3
+ * Sets or toggles the auto-proceed override for the active session.
4
+ *
5
+ * Unlike full-auto, this command has no config-level enablement requirement,
6
+ * no durable state, and no v2 oversight infrastructure.
7
+ *
8
+ * @param directory - Project directory (accepted for signature consistency with other command handlers; unused)
9
+ * @param args - Optional argument: "on" | "off" | undefined (toggle behavior)
10
+ * @param sessionID - Session ID for accessing active session state
11
+ * @returns Feedback message about auto-proceed override state
12
+ */
13
+ export declare function handleAutoProceedCommand(_directory: string, args: string[], sessionID: string): Promise<string>;
@@ -3,6 +3,7 @@ export { handleAcknowledgeSpecDriftCommand } from './acknowledge-spec-drift';
3
3
  export { handleAgentsCommand } from './agents';
4
4
  export { handleAnalyzeCommand } from './analyze';
5
5
  export { handleArchiveCommand } from './archive';
6
+ export { handleAutoProceedCommand } from './auto-proceed';
6
7
  export { handleBenchmarkCommand } from './benchmark';
7
8
  export { handleBrainstormCommand } from './brainstorm';
8
9
  export { handleCheckpointCommand } from './checkpoint';
@@ -457,6 +457,13 @@ export declare const COMMAND_REGISTRY: {
457
457
  readonly details: "Toggles Full-Auto Mode which enables autonomous execution without confirmation prompts. When enabled, the architect proceeds through implementation steps automatically. Session-scoped — resets on new session. Use \"on\" or \"off\" to set explicitly, or toggle with no argument.";
458
458
  readonly category: "utility";
459
459
  };
460
+ readonly 'auto-proceed': {
461
+ readonly handler: (ctx: CommandContext) => Promise<string>;
462
+ readonly description: "Toggle or set auto-proceed override for the active session";
463
+ readonly args: "[on|off]";
464
+ readonly category: "config";
465
+ readonly details: "Without argument, toggles auto-proceed mode. With \"on\" or \"off\", sets the state explicitly.";
466
+ };
460
467
  readonly 'write-retro': {
461
468
  readonly handler: (ctx: CommandContext) => Promise<string>;
462
469
  readonly description: "Write a retrospective evidence bundle for a completed phase <json>";
@@ -1,5 +1,5 @@
1
1
  import type { ResolvedSwarmCommand, SwarmCommandPolicyResult } from './command-dispatch.js';
2
- export declare const SWARM_COMMAND_TOOL_COMMANDS: readonly ["agents", "config", "config doctor", "doctor tools", "status", "show-plan", "help", "history", "evidence", "evidence summary", "retrieve", "diagnose", "preflight", "benchmark", "knowledge", "memory", "memory status", "memory pending", "memory recall-log", "memory compact", "memory stale", "memory export", "memory evaluate", "memory import", "memory migrate", "sdd", "sdd status", "sdd validate", "sdd project", "sync-plan", "export"];
2
+ export declare const SWARM_COMMAND_TOOL_COMMANDS: readonly ["agents", "config", "config doctor", "doctor tools", "status", "show-plan", "help", "history", "evidence", "evidence summary", "retrieve", "diagnose", "preflight", "benchmark", "knowledge", "memory", "memory status", "memory pending", "memory recall-log", "memory compact", "memory stale", "memory export", "memory evaluate", "memory import", "memory migrate", "sdd", "sdd status", "sdd validate", "sdd project", "sync-plan", "export", "auto-proceed"];
3
3
  export type SwarmCommandToolInputCommand = (typeof SWARM_COMMAND_TOOL_COMMANDS)[number];
4
4
  export declare const SWARM_COMMAND_TOOL_ALLOWLIST: Set<string>;
5
5
  /**
@@ -73,8 +73,10 @@ export declare const COMPACTION_DEFAULTS: {
73
73
  };
74
74
  export declare const TURBO_MODE_BANNER = "## \uD83D\uDE80 TURBO MODE ACTIVE\n\n**Speed optimization enabled for this session.**\n\nWhile Turbo Mode is active:\n- **Stage A gates** (lint, imports, pre_check_batch) are still REQUIRED for ALL tasks\n- **Tier 3 tasks** (security-sensitive files matching: architect*.ts, delegation*.ts, guardrails*.ts, adversarial*.ts, sanitiz*.ts, auth*, permission*, crypto*, secret*, security) still require FULL review (Stage B)\n- **Tier 0-2 tasks** can skip Stage B (reviewer, test_engineer) to speed up execution\n- **Phase completion gates** (completion-verify and drift verification gate) are automatically bypassed \u2014 phase_complete will succeed without drift verification evidence when turbo is active. Note: turbo bypass is session-scoped; one session's turbo does not affect other sessions.\n\nClassification still determines the pipeline:\n- TIER 0 (metadata): lint + diff only \u2014 no change\n- TIER 1 (docs): Stage A + reviewer \u2014 no change\n- TIER 2 (standard code): Stage A + reviewer + test_engineer \u2014 CAN SKIP Stage B with turboMode\n- TIER 3 (critical): Stage A + 2x reviewer + 2x test_engineer \u2014 Stage B REQUIRED (no turbo bypass)\n\nDo NOT skip Stage A gates. Do NOT skip Stage B for TIER 3.\n";
75
75
  export declare const FULL_AUTO_BANNER = "## \u26A1 FULL-AUTO MODE ACTIVE\n\nYou are operating without a human in the loop. All escalations route to the Autonomous Oversight Critic instead of a user.\n\nBehavioral changes:\n- TIER 3 escalations go to the critic, not a human. Frame your questions technically, not conversationally.\n- Phase completion approval comes from the critic. Ensure all evidence is written before requesting.\n- The critic defaults to REJECT. Do not attempt to pressure, negotiate, or shortcut. Complete the evidence trail.\n- If the critic returns ESCALATE_TO_HUMAN, the session will pause or terminate. Only the critic can trigger this.\n- Do NOT ask \"Ready for Phase N+1?\" \u2014 call phase_complete directly. The critic reviews automatically.\n";
76
+ export declare const AUTO_PROCEED_BANNER = "## \u23ED\uFE0F AUTO-PROCEED STATUS\n\nAuto-proceed controls whether the architect advances to the next phase automatically (skipping the \"Ready for Phase N+1?\" confirmation).\n\nBehavioral rules:\n- Session override (set via /swarm auto-proceed on|off) wins over the plan default.\n- If neither is set, auto-proceed defaults to OFF and the architect asks before advancing.\n- Full-auto mode (critic oversight) is independent \u2014 it has its own auto-advance mechanism.\n- autoProceedNudgeDone prevents the FR-004 first-boundary nudge from re-firing in this session.\n\nTo toggle at runtime: call swarm_command({ command: \"auto-proceed\", args: [\"on\"|\"off\"] }) from the architect.\n";
76
77
  /**
77
78
  * Canonical default Lean Turbo configuration.
79
+
78
80
  *
79
81
  * This is the single source of truth for all LeanTurboConfig fields.
80
82
  * Consumers MUST reference this constant instead of hardcoding their own
@@ -4,6 +4,7 @@ export declare const ExecutionProfileSchema: z.ZodObject<{
4
4
  max_concurrent_tasks: z.ZodDefault<z.ZodNumber>;
5
5
  council_parallel: z.ZodDefault<z.ZodBoolean>;
6
6
  locked: z.ZodDefault<z.ZodBoolean>;
7
+ auto_proceed: z.ZodDefault<z.ZodBoolean>;
7
8
  }, z.core.$strip>;
8
9
  export type ExecutionProfile = z.infer<typeof ExecutionProfileSchema>;
9
10
  export declare const TaskStatusSchema: z.ZodEnum<{
@@ -166,6 +167,7 @@ export declare const PlanSchema: z.ZodObject<{
166
167
  max_concurrent_tasks: z.ZodDefault<z.ZodNumber>;
167
168
  council_parallel: z.ZodDefault<z.ZodBoolean>;
168
169
  locked: z.ZodDefault<z.ZodBoolean>;
170
+ auto_proceed: z.ZodDefault<z.ZodBoolean>;
169
171
  }, z.core.$strip>>;
170
172
  }, z.core.$strip>;
171
173
  export type Plan = z.infer<typeof PlanSchema>;
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.65.3",
72
+ version: "7.66.1",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -851,6 +851,17 @@ Behavioral changes:
851
851
  - The critic defaults to REJECT. Do not attempt to pressure, negotiate, or shortcut. Complete the evidence trail.
852
852
  - If the critic returns ESCALATE_TO_HUMAN, the session will pause or terminate. Only the critic can trigger this.
853
853
  - Do NOT ask "Ready for Phase N+1?" — call phase_complete directly. The critic reviews automatically.
854
+ `, AUTO_PROCEED_BANNER = `## ⏭️ AUTO-PROCEED STATUS
855
+
856
+ Auto-proceed controls whether the architect advances to the next phase automatically (skipping the "Ready for Phase N+1?" confirmation).
857
+
858
+ Behavioral rules:
859
+ - Session override (set via /swarm auto-proceed on|off) wins over the plan default.
860
+ - If neither is set, auto-proceed defaults to OFF and the architect asks before advancing.
861
+ - Full-auto mode (critic oversight) is independent — it has its own auto-advance mechanism.
862
+ - autoProceedNudgeDone prevents the FR-004 first-boundary nudge from re-firing in this session.
863
+
864
+ To toggle at runtime: call swarm_command({ command: "auto-proceed", args: ["on"|"off"] }) from the architect.
854
865
  `, DEFAULT_LEAN_TURBO_CONFIG, DEFAULT_WORKTREE_ISOLATION_CONFIG, LEAN_TURBO_BANNER = `## \uD83D\uDEE4️ LEAN TURBO ACTIVE
855
866
 
856
867
  Lane-based parallel execution is enabled for this phase.
@@ -16568,7 +16579,8 @@ var init_plan_schema = __esm(() => {
16568
16579
  parallelization_enabled: exports_external.boolean().default(false),
16569
16580
  max_concurrent_tasks: exports_external.number().int().min(1).max(64).default(1),
16570
16581
  council_parallel: exports_external.boolean().default(false),
16571
- locked: exports_external.boolean().default(false)
16582
+ locked: exports_external.boolean().default(false),
16583
+ auto_proceed: exports_external.boolean().default(false)
16572
16584
  });
16573
16585
  TaskStatusSchema = exports_external.enum([
16574
16586
  "pending",
@@ -43164,6 +43176,7 @@ __export(exports_state, {
43164
43176
  getTaskState: () => getTaskState,
43165
43177
  getSessionEnvironment: () => getSessionEnvironment,
43166
43178
  getRunContext: () => getRunContext,
43179
+ getResolvedAutoProceed: () => getResolvedAutoProceed,
43167
43180
  getAgentSession: () => getAgentSession,
43168
43181
  getActiveWindow: () => getActiveWindow,
43169
43182
  ensureSessionEnvironment: () => ensureSessionEnvironment,
@@ -43873,6 +43886,9 @@ function hasActiveLeanTurbo(sessionID) {
43873
43886
  }
43874
43887
  return false;
43875
43888
  }
43889
+ function getResolvedAutoProceed(session, planAutoProceed) {
43890
+ return session.autoProceedOverride ?? planAutoProceed ?? false;
43891
+ }
43876
43892
  function setSessionEnvironment(sessionId, profile) {
43877
43893
  swarmState.environmentProfiles.set(sessionId, profile);
43878
43894
  }
@@ -43979,6 +43995,45 @@ var init_state = __esm(() => {
43979
43995
  };
43980
43996
  });
43981
43997
 
43998
+ // src/commands/auto-proceed.ts
43999
+ async function handleAutoProceedCommand(_directory, args2, sessionID) {
44000
+ if (!sessionID || sessionID.trim() === "") {
44001
+ return "Error: No active session context. Auto-proceed requires an active session. Use /swarm auto-proceed from within an OpenCode session, or start a session first.";
44002
+ }
44003
+ const session = getAgentSession(sessionID);
44004
+ if (!session) {
44005
+ return "Error: No active session. Auto-proceed requires an active session to operate.";
44006
+ }
44007
+ if (stripKnownSwarmPrefix(session.agentName) !== "architect") {
44008
+ return `Error: Auto-proceed can only be toggled from the architect session. Currently active session is: ${session.agentName}`;
44009
+ }
44010
+ const arg = args2[0]?.toLowerCase();
44011
+ const wasUndefinedBefore = session.autoProceedOverride === undefined;
44012
+ let newAutoProceedOverride;
44013
+ if (arg === "on") {
44014
+ newAutoProceedOverride = true;
44015
+ } else if (arg === "off") {
44016
+ newAutoProceedOverride = false;
44017
+ } else if (arg === undefined) {
44018
+ newAutoProceedOverride = !session.autoProceedOverride;
44019
+ } else {
44020
+ return 'Error: Invalid argument for /swarm auto-proceed. Valid options: "on", "off", or omit the argument to toggle.';
44021
+ }
44022
+ session.autoProceedOverride = newAutoProceedOverride;
44023
+ if (arg === "on" || arg === "off" || arg === undefined && wasUndefinedBefore) {
44024
+ session.autoProceedNudgeDone = true;
44025
+ }
44026
+ if (newAutoProceedOverride) {
44027
+ return "Auto-proceed is now ON. Phase boundaries will advance automatically.";
44028
+ } else {
44029
+ return "Auto-proceed is now OFF. You will be asked before advancing to the next phase.";
44030
+ }
44031
+ }
44032
+ var init_auto_proceed = __esm(() => {
44033
+ init_schema();
44034
+ init_state();
44035
+ });
44036
+
43982
44037
  // src/commands/benchmark.ts
43983
44038
  async function handleBenchmarkCommand(directory, args2) {
43984
44039
  let cumulative = args2.includes("--cumulative");
@@ -72140,6 +72195,12 @@ function serializeAgentSession(s) {
72140
72195
  ...Object.keys(stageBCompletion).length > 0 && { stageBCompletion },
72141
72196
  ...s.maxConcurrencyOverride !== undefined && {
72142
72197
  maxConcurrencyOverride: s.maxConcurrencyOverride
72198
+ },
72199
+ ...s.autoProceedOverride !== undefined && {
72200
+ autoProceedOverride: s.autoProceedOverride
72201
+ },
72202
+ ...s.autoProceedNudgeDone !== undefined && {
72203
+ autoProceedNudgeDone: s.autoProceedNudgeDone
72143
72204
  }
72144
72205
  };
72145
72206
  }
@@ -80553,8 +80614,20 @@ import * as fs35 from "node:fs";
80553
80614
  import * as path67 from "node:path";
80554
80615
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
80555
80616
  if (workingDirectory == null || workingDirectory === "") {
80617
+ if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
80618
+ return {
80619
+ success: false,
80620
+ message: "Invalid working_directory: no explicit working_directory was provided and fallbackDirectory is missing or not a string"
80621
+ };
80622
+ }
80556
80623
  return { success: true, directory: fallbackDirectory };
80557
80624
  }
80625
+ if (typeof workingDirectory !== "string") {
80626
+ return {
80627
+ success: false,
80628
+ message: "Invalid working_directory: path must be a string"
80629
+ };
80630
+ }
80558
80631
  if (workingDirectory.includes("\x00")) {
80559
80632
  return {
80560
80633
  success: false,
@@ -80570,14 +80643,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
80570
80643
  };
80571
80644
  }
80572
80645
  }
80573
- const normalizedDir = path67.normalize(workingDirectory);
80574
- const pathParts = normalizedDir.split(path67.sep);
80575
- if (pathParts.includes("..")) {
80646
+ const rawPathParts = workingDirectory.split(path67.sep);
80647
+ if (rawPathParts.includes("..")) {
80576
80648
  return {
80577
80649
  success: false,
80578
80650
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
80579
80651
  };
80580
80652
  }
80653
+ const normalizedDir = path67.normalize(workingDirectory);
80581
80654
  const resolvedDir = path67.resolve(normalizedDir);
80582
80655
  let statResult;
80583
80656
  try {
@@ -80594,6 +80667,9 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
80594
80667
  message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
80595
80668
  };
80596
80669
  }
80670
+ if (typeof fallbackDirectory !== "string" || fallbackDirectory === "") {
80671
+ return { success: true, directory: resolvedDir };
80672
+ }
80597
80673
  const resolvedFallback = path67.resolve(fallbackDirectory);
80598
80674
  let fallbackExists = false;
80599
80675
  try {
@@ -80602,23 +80678,14 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
80602
80678
  } catch {
80603
80679
  fallbackExists = false;
80604
80680
  }
80605
- if (workingDirectory != null && workingDirectory !== "") {
80606
- if (fallbackExists) {
80607
- const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path67.sep);
80608
- if (isSubdirectory) {
80609
- return {
80610
- success: false,
80611
- message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
80612
- };
80613
- }
80681
+ if (fallbackExists) {
80682
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path67.sep);
80683
+ if (isSubdirectory) {
80684
+ return {
80685
+ success: false,
80686
+ message: `Invalid working_directory: "${workingDirectory}" resolves to "${resolvedDir}" ` + `which is a subdirectory of fallback "${resolvedFallback}". ` + `Pass the project root path or omit working_directory entirely.`
80687
+ };
80614
80688
  }
80615
- return { success: true, directory: resolvedDir };
80616
- }
80617
- if (resolvedDir !== resolvedFallback) {
80618
- return {
80619
- success: false,
80620
- message: `Invalid working_directory: path resolves to "${resolvedDir}" but fallbackDirectory ` + `"${resolvedFallback}" is not the project root. ` + `This may indicate CWD mismatch. Pass the project root path explicitly.`
80621
- };
80622
80689
  }
80623
80690
  return { success: true, directory: resolvedDir };
80624
80691
  }
@@ -86457,7 +86524,8 @@ var init_tool_policy = __esm(() => {
86457
86524
  "sdd validate",
86458
86525
  "sdd project",
86459
86526
  "sync-plan",
86460
- "export"
86527
+ "export",
86528
+ "auto-proceed"
86461
86529
  ];
86462
86530
  SWARM_COMMAND_TOOL_ALLOWLIST = new Set([
86463
86531
  "agents",
@@ -86486,7 +86554,8 @@ var init_tool_policy = __esm(() => {
86486
86554
  "sdd status",
86487
86555
  "sdd validate",
86488
86556
  "sync-plan",
86489
- "export"
86557
+ "export",
86558
+ "auto-proceed"
86490
86559
  ]);
86491
86560
  HUMAN_ONLY_SWARM_COMMANDS = new Set([
86492
86561
  "acknowledge-spec-drift",
@@ -86580,6 +86649,7 @@ __export(exports_commands, {
86580
86649
  handleCheckpointCommand: () => handleCheckpointCommand,
86581
86650
  handleBrainstormCommand: () => handleBrainstormCommand,
86582
86651
  handleBenchmarkCommand: () => handleBenchmarkCommand,
86652
+ handleAutoProceedCommand: () => handleAutoProceedCommand,
86583
86653
  handleArchiveCommand: () => handleArchiveCommand,
86584
86654
  handleAnalyzeCommand: () => handleAnalyzeCommand,
86585
86655
  handleAgentsCommand: () => handleAgentsCommand,
@@ -86832,6 +86902,7 @@ var init_commands = __esm(() => {
86832
86902
  init_acknowledge_spec_drift();
86833
86903
  init_agents();
86834
86904
  init_archive();
86905
+ init_auto_proceed();
86835
86906
  init_benchmark();
86836
86907
  init_checkpoint2();
86837
86908
  init_close();
@@ -87064,6 +87135,7 @@ var init_registry = __esm(() => {
87064
87135
  init_acknowledge_spec_drift();
87065
87136
  init_agents();
87066
87137
  init_archive();
87138
+ init_auto_proceed();
87067
87139
  init_benchmark();
87068
87140
  init_checkpoint2();
87069
87141
  init_close();
@@ -87541,6 +87613,13 @@ Subcommands:
87541
87613
  details: 'Toggles Full-Auto Mode which enables autonomous execution without confirmation prompts. When enabled, the architect proceeds through implementation steps automatically. Session-scoped — resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.',
87542
87614
  category: "utility"
87543
87615
  },
87616
+ "auto-proceed": {
87617
+ handler: (ctx) => handleAutoProceedCommand(ctx.directory, ctx.args, ctx.sessionID),
87618
+ description: "Toggle or set auto-proceed override for the active session",
87619
+ args: "[on|off]",
87620
+ category: "config",
87621
+ details: 'Without argument, toggles auto-proceed mode. With "on" or "off", sets the state explicitly.'
87622
+ },
87544
87623
  "write-retro": {
87545
87624
  handler: (ctx) => handleWriteRetroCommand(ctx.directory, ctx.args),
87546
87625
  description: "Write a retrospective evidence bundle for a completed phase <json>",
@@ -89004,6 +89083,9 @@ INLINE GATE SELECTION -- no pending section found in context.md. You MUST ask no
89004
89083
 
89005
89084
  MANDATORY PAUSE: Present the gate question. Wait for the user's answer.
89006
89085
  Do NOT call \`set_qa_gates\` until the user has responded.
89086
+
89087
+ Execution preferences (auto-proceed phase transitions):
89088
+ - \`auto_proceed\` (boolean, default false): When true, the architect auto-advances to the next phase without asking "Ready for Phase N+1?". Runtime toggle via /swarm auto-proceed on|off.
89007
89089
  <!-- BEHAVIORAL_GUIDANCE_END -->
89008
89090
  - Preserve task granularity, test task deduplication, phase count guidance, and TRACEABILITY CHECK rules from the loaded skill.
89009
89091
 
@@ -89049,6 +89131,17 @@ ACTION: Load skill file:.opencode/skills/phase-wrap/SKILL.md immediately. Follow
89049
89131
 
89050
89132
  HARD CONSTRAINTS:
89051
89133
  - Complete retrospective evidence with \`write_retro\` before \`phase_complete\`.
89134
+ - Before step 7 (phase transition): read the AUTO_PROCEED STATUS banner injected into your context. The banner tells you:
89135
+ - auto-proceed state (on/off)
89136
+ - source (session override vs plan-or-default)
89137
+ - nudge flag (true if user has already been asked or has explicitly toggled)
89138
+ - If auto-proceed is ON (banner shows "on"): call \`phase_complete\`, then advance to the first task of the next phase. Do NOT ask the user.
89139
+ - If auto-proceed is OFF (banner shows "off") AND nudge flag is false: after the user confirms the phase transition, suggest enabling auto-proceed with: "Auto-proceed is currently disabled. Would you like me to automatically advance to future phases without asking?" Then:
89140
+ - On YES: call \`swarm_command({ command: "auto-proceed", args: ["on"] })\` — this sets both override and nudge-done
89141
+ - On NO: call \`swarm_command({ command: "auto-proceed", args: ["off"] })\` — this sets override=false and nudge-done=true
89142
+ - If auto-proceed is OFF AND nudge flag is true: just ask "Ready for Phase [N+1]?" as before.
89143
+ - SC-001: auto-proceed only skips the phase-transition confirmation. The architect MUST still stop for blocked tasks, user questions, clarification needs, and any decision requiring human input. This behavior is NOT affected by the auto_proceed setting.
89144
+ - Full-auto mode (critic oversight) is independent — its existing "Do NOT ask Ready for Phase N+1?" override continues to work. auto_proceed has no additional effect under full-auto.
89052
89145
 
89053
89146
  > **NOTE**: The \`critic_oversight\` agent (\`AUTONOMOUS_OVERSIGHT_PROMPT\`) is dispatched only via full-auto mode (\`src/full-auto/oversight.ts\`). It has no architect MODE dispatch path — it is **NOT** reachable from \`MODE: CRITIC-GATE\`, \`MODE: EXECUTE\`, or \`MODE: PHASE-WRAP\`. This is intentional: it serves as the sole quality gate in autonomous oversight mode.
89054
89147
 
@@ -97920,7 +98013,7 @@ var init_design_doc_drift = __esm(() => {
97920
98013
  var exports_project_context = {};
97921
98014
  __export(exports_project_context, {
97922
98015
  buildProjectContext: () => buildProjectContext,
97923
- _internals: () => _internals90,
98016
+ _internals: () => _internals91,
97924
98017
  LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
97925
98018
  });
97926
98019
  import * as fs135 from "node:fs";
@@ -98004,7 +98097,7 @@ function selectLintCommand(backend, directory) {
98004
98097
  return null;
98005
98098
  }
98006
98099
  async function buildProjectContext(directory) {
98007
- const backend = await _internals90.pickBackend(directory);
98100
+ const backend = await _internals91.pickBackend(directory);
98008
98101
  if (!backend)
98009
98102
  return null;
98010
98103
  const ctx = emptyProjectContext();
@@ -98043,17 +98136,17 @@ async function buildProjectContext(directory) {
98043
98136
  if (backend.prompts.reviewerChecklist.length > 0) {
98044
98137
  ctx.REVIEWER_CHECKLIST = bulletList(backend.prompts.reviewerChecklist);
98045
98138
  }
98046
- const profiles = _internals90.pickedProfiles(directory);
98139
+ const profiles = _internals91.pickedProfiles(directory);
98047
98140
  if (profiles.length > 1) {
98048
98141
  ctx.PROJECT_CONTEXT_SECONDARY_LANGUAGES = profiles.slice(1).map((p) => p.id).join(", ");
98049
98142
  }
98050
98143
  return ctx;
98051
98144
  }
98052
- var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals90;
98145
+ var LANG_BACKEND_DETECTION_TIMEOUT_MS = 300, _internals91;
98053
98146
  var init_project_context = __esm(() => {
98054
98147
  init_dispatch();
98055
98148
  init_framework_detector();
98056
- _internals90 = {
98149
+ _internals91 = {
98057
98150
  pickBackend,
98058
98151
  pickedProfiles
98059
98152
  };
@@ -103238,6 +103331,7 @@ init_schema();
103238
103331
  init_manager2();
103239
103332
  init_detector();
103240
103333
  init_manager();
103334
+ init_state();
103241
103335
  import * as fs71 from "node:fs";
103242
103336
  import * as path114 from "node:path";
103243
103337
 
@@ -103531,7 +103625,6 @@ init_preflight_service();
103531
103625
  init_status_service();
103532
103626
 
103533
103627
  // src/hooks/system-enhancer.ts
103534
- init_state();
103535
103628
  init_telemetry();
103536
103629
  init_utils();
103537
103630
 
@@ -106637,6 +106730,20 @@ ${lines.join(`
106637
106730
  tryInject(LEAN_TURBO_BANNER);
106638
106731
  }
106639
106732
  }
106733
+ const sessionIdAutoProceed = _input.sessionID;
106734
+ if (sessionIdAutoProceed) {
106735
+ const sessionAutoProceed = getAgentSession(sessionIdAutoProceed);
106736
+ if (sessionAutoProceed && stripKnownSwarmPrefix(sessionAutoProceed.agentName) === "architect") {
106737
+ const resolvedAutoProceed = getResolvedAutoProceed(sessionAutoProceed, plan2?.execution_profile?.auto_proceed);
106738
+ const source = sessionAutoProceed.autoProceedOverride !== undefined ? "session" : "plan-or-default";
106739
+ const banner = `${AUTO_PROCEED_BANNER}
106740
+ ## ⏭️ AUTO_PROCEED STATUS:
106741
+ - auto-proceed: ${resolvedAutoProceed ? "on" : "off"}
106742
+ - source: ${source}
106743
+ - nudge: ${sessionAutoProceed.autoProceedNudgeDone ? "true" : "false"}`;
106744
+ tryInject(banner);
106745
+ }
106746
+ }
106640
106747
  try {
106641
106748
  const currentPhaseNum = plan2?.current_phase ?? 1;
106642
106749
  const retroText = await buildRetroInjection(directory, currentPhaseNum, plan2?.title ?? undefined);
@@ -112462,7 +112569,9 @@ var TRANSIENT_SESSION_FIELDS = [
112462
112569
  { name: "model_fallback_index", resetValue: 0 },
112463
112570
  { name: "modelFallbackExhausted", resetValue: false },
112464
112571
  { name: "scopeViolationDetected", resetValue: false },
112465
- { name: "delegationActive", resetValue: false }
112572
+ { name: "delegationActive", resetValue: false },
112573
+ { name: "autoProceedOverride", resetValue: undefined },
112574
+ { name: "autoProceedNudgeDone", resetValue: undefined }
112466
112575
  ];
112467
112576
  var VALID_TASK_WORKFLOW_STATES = [
112468
112577
  "idle",
@@ -112563,6 +112672,8 @@ function deserializeAgentSession(s) {
112563
112672
  fullAutoDeadlockCount: s.fullAutoDeadlockCount ?? 0,
112564
112673
  fullAutoLastQuestionHash: s.fullAutoLastQuestionHash ?? null,
112565
112674
  maxConcurrencyOverride: typeof s.maxConcurrencyOverride === "number" ? Math.min(64, Math.max(1, s.maxConcurrencyOverride)) : undefined,
112675
+ autoProceedOverride: s.autoProceedOverride,
112676
+ autoProceedNudgeDone: s.autoProceedNudgeDone,
112566
112677
  prmPatternCounts: new Map,
112567
112678
  prmEscalationLevel: 0,
112568
112679
  prmLastPatternDetected: null,
@@ -131215,7 +131326,7 @@ init_effective_spec();
131215
131326
  init_state();
131216
131327
  init_create_tool();
131217
131328
  function executionProfilesEqual(a, b) {
131218
- return a.parallelization_enabled === b.parallelization_enabled && a.max_concurrent_tasks === b.max_concurrent_tasks && a.council_parallel === b.council_parallel && a.locked === b.locked;
131329
+ return a.parallelization_enabled === b.parallelization_enabled && a.max_concurrent_tasks === b.max_concurrent_tasks && a.council_parallel === b.council_parallel && a.locked === b.locked && a.auto_proceed === b.auto_proceed;
131219
131330
  }
131220
131331
  function detectPlaceholderContent(args2) {
131221
131332
  const issues = [];
@@ -131409,7 +131520,7 @@ async function executeSavePlan(args2, fallbackDir) {
131409
131520
  success: false,
131410
131521
  message: "Invalid execution_profile: schema validation failed",
131411
131522
  errors: requestedProfile.error.issues.map((i2) => `${i2.path.join(".")}: ${i2.message}`),
131412
- recovery_guidance: "Check execution_profile fields: parallelization_enabled (boolean), " + "max_concurrent_tasks (integer 1-64), council_parallel (boolean), locked (boolean)."
131523
+ recovery_guidance: "Check execution_profile fields: parallelization_enabled (boolean), " + "max_concurrent_tasks (integer 1-64), council_parallel (boolean), locked (boolean), auto_proceed (boolean)."
131413
131524
  };
131414
131525
  }
131415
131526
  if (executionProfilesEqual(existing.execution_profile, requestedProfile.data)) {
@@ -131699,7 +131810,8 @@ var save_plan = createSwarmTool({
131699
131810
  parallelization_enabled: exports_external.boolean().optional().describe("When true, enables parallel task dispatch for this plan. Default false (serial)."),
131700
131811
  max_concurrent_tasks: exports_external.number().int().min(1).max(64).optional().describe("Maximum tasks that may run concurrently when parallelization is enabled. Default 1."),
131701
131812
  council_parallel: exports_external.boolean().optional().describe("When true, council review phases may run in parallel. Default false."),
131702
- locked: exports_external.boolean().optional().describe("When true, locks the profile — future save_plan calls that include " + "execution_profile will be rejected (fail-closed). " + "Unlock by resetting the plan (reset_statuses: true).")
131813
+ locked: exports_external.boolean().optional().describe("When true, locks the profile — future save_plan calls that include " + "execution_profile will be rejected (fail-closed). " + "Unlock by resetting the plan (reset_statuses: true)."),
131814
+ auto_proceed: exports_external.boolean().optional().describe("When true, the architect advances to the next phase automatically without asking for confirmation. Default false.")
131703
131815
  }).optional().describe("Architect-facing concurrency controls. Once locked, cannot be changed without resetting. " + "Omit to preserve the existing profile.")
131704
131816
  },
131705
131817
  execute: async (args2, _directory) => {
@@ -135859,6 +135971,11 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
135859
135971
  init_task_id();
135860
135972
  init_create_tool();
135861
135973
  init_resolve_working_directory();
135974
+ var _internals89 = {
135975
+ tryAcquireLock,
135976
+ updateTaskStatus,
135977
+ resolveWorkingDirectory
135978
+ };
135862
135979
  var VALID_STATUSES2 = [
135863
135980
  "pending",
135864
135981
  "in_progress",
@@ -135951,7 +136068,7 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
135951
136068
  }
135952
136069
  let resolvedDir;
135953
136070
  if (fallbackDir) {
135954
- const resolveResult = resolveWorkingDirectory(workingDirectory, fallbackDir);
136071
+ const resolveResult = _internals89.resolveWorkingDirectory(workingDirectory, fallbackDir);
135955
136072
  if (resolveResult.success) {
135956
136073
  resolvedDir = resolveResult.directory;
135957
136074
  } else {
@@ -136298,14 +136415,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
136298
136415
  }
136299
136416
  }
136300
136417
  let directory;
136301
- if (!args2.working_directory && !fallbackDir) {
136302
- return {
136303
- success: false,
136304
- message: "No working_directory provided and fallbackDir is undefined",
136305
- errors: ["Cannot resolve directory for task status update"]
136306
- };
136307
- }
136308
- const resolveResult = resolveWorkingDirectory(args2.working_directory ?? fallbackDir, fallbackDir);
136418
+ const resolveResult = _internals89.resolveWorkingDirectory(args2.working_directory, fallbackDir);
136309
136419
  if (!resolveResult.success) {
136310
136420
  return {
136311
136421
  success: false,
@@ -136398,7 +136508,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
136398
136508
  }
136399
136509
  let lockResult;
136400
136510
  try {
136401
- lockResult = await tryAcquireLock(directory, planFilePath, agentName, lockTaskId);
136511
+ lockResult = await _internals89.tryAcquireLock(directory, planFilePath, agentName, lockTaskId);
136402
136512
  } catch (error93) {
136403
136513
  return {
136404
136514
  success: false,
@@ -136417,7 +136527,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
136417
136527
  };
136418
136528
  }
136419
136529
  try {
136420
- const updatedPlan = await updateTaskStatus(directory, args2.task_id, args2.status);
136530
+ const updatedPlan = await _internals89.updateTaskStatus(directory, args2.task_id, args2.status);
136421
136531
  if (args2.status === "completed") {
136422
136532
  for (const [_sessionId, session] of swarmState.agentSessions) {
136423
136533
  if (!(session.taskWorkflowStates instanceof Map)) {
@@ -136841,7 +136951,7 @@ var web_search = createSwarmTool({
136841
136951
  });
136842
136952
  async function captureSearchEvidence(directory, query, results) {
136843
136953
  try {
136844
- const written = await _internals89.writeEvidenceDocuments(directory, results.map((result) => ({
136954
+ const written = await _internals90.writeEvidenceDocuments(directory, results.map((result) => ({
136845
136955
  sourceType: "web_search",
136846
136956
  query,
136847
136957
  title: result.title,
@@ -136869,7 +136979,7 @@ async function captureSearchEvidence(directory, query, results) {
136869
136979
  };
136870
136980
  }
136871
136981
  }
136872
- var _internals89 = {
136982
+ var _internals90 = {
136873
136983
  writeEvidenceDocuments
136874
136984
  };
136875
136985
 
@@ -138379,6 +138489,10 @@ async function initializeOpenCodeSwarm(ctx) {
138379
138489
  template: "/swarm full-auto $ARGUMENTS",
138380
138490
  description: "Toggle Full-Auto Mode for the active session [on|off]"
138381
138491
  },
138492
+ "swarm-auto-proceed": {
138493
+ template: "/swarm auto-proceed $ARGUMENTS",
138494
+ description: "Toggle auto-proceed mode for automatic phase advancement"
138495
+ },
138382
138496
  "swarm-write-retro": {
138383
138497
  template: "/swarm write-retro $ARGUMENTS",
138384
138498
  description: "Use /swarm write-retro to manually write a phase retrospective"
@@ -63,6 +63,10 @@ export interface SerializedAgentSession {
63
63
  stageBCompletion?: Record<string, string[]>;
64
64
  /** Session-scoped concurrency override for max_concurrent_tasks (Issue #761) */
65
65
  maxConcurrencyOverride?: number;
66
+ /** Session-level auto-proceed override (Phase 1) */
67
+ autoProceedOverride?: boolean;
68
+ /** Flag tracking whether the auto-proceed nudge has been shown (Phase 1) */
69
+ autoProceedNudgeDone?: boolean;
66
70
  }
67
71
  /**
68
72
  * Minimal interface for serialized InvocationWindow
package/dist/state.d.ts CHANGED
@@ -175,6 +175,12 @@ export interface AgentSessionState {
175
175
  * When set, overrides the plan's execution_profile.max_concurrent_tasks
176
176
  * for delegation-gate guidance. Cleared on session reset. */
177
177
  maxConcurrencyOverride?: number;
178
+ /** Session-scoped override for execution_profile.auto_proceed.
179
+ * When set, overrides the plan's auto_proceed for this session.
180
+ * true = auto-advance, false = do not auto-advance. Cleared on session reset. */
181
+ autoProceedOverride?: boolean;
182
+ /** Tracks whether the FR-004 nudge ("would you like to auto-advance?") has already been shown this session. */
183
+ autoProceedNudgeDone?: boolean;
178
184
  /** Session-level QA gate overrides layered on top of the spec-level profile.
179
185
  * Overrides can only enable gates (true); false values are ignored by
180
186
  * getEffectiveGates. Cleared on session reset. Optional for backwards
@@ -594,6 +600,13 @@ export declare function hasActiveFullAuto(sessionID?: string): boolean;
594
600
  * or if any session has that combination when no sessionID provided.
595
601
  */
596
602
  export declare function hasActiveLeanTurbo(sessionID?: string): boolean;
603
+ /**
604
+ * Resolves the effective auto_proceed value for a session.
605
+ * Session override (autoProceedOverride) takes precedence over the plan default.
606
+ * Accepts `boolean | undefined` for the plan default so callers can pass
607
+ * `plan?.execution_profile?.auto_proceed` directly without a falsy fallback.
608
+ */
609
+ export declare function getResolvedAutoProceed(session: AgentSessionState, planAutoProceed: boolean | undefined): boolean;
597
610
  export declare function setSessionEnvironment(sessionId: string, profile: EnvironmentProfile): void;
598
611
  export declare function getSessionEnvironment(sessionId: string): EnvironmentProfile | undefined;
599
612
  export declare function ensureSessionEnvironment(sessionId: string): EnvironmentProfile;
@@ -39,4 +39,4 @@ export interface ResolveError {
39
39
  * @param workingDirectory - Explicit working_directory from tool args (caller-controlled)
40
40
  * @param fallbackDirectory - Injected directory from createSwarmTool (ctx.directory ?? process.cwd())
41
41
  */
42
- export declare function resolveWorkingDirectory(workingDirectory: string | undefined | null, fallbackDirectory: string): ResolveResult | ResolveError;
42
+ export declare function resolveWorkingDirectory(workingDirectory: string | undefined | null, fallbackDirectory?: string | null): ResolveResult | ResolveError;
@@ -75,6 +75,7 @@ export interface SavePlanArgs {
75
75
  max_concurrent_tasks?: number;
76
76
  council_parallel?: boolean;
77
77
  locked?: boolean;
78
+ auto_proceed?: boolean;
78
79
  };
79
80
  }
80
81
  /**
@@ -95,6 +96,7 @@ export interface SavePlanResult {
95
96
  max_concurrent_tasks: number;
96
97
  council_parallel: boolean;
97
98
  locked: boolean;
99
+ auto_proceed?: boolean;
98
100
  };
99
101
  }
100
102
  /**
@@ -3,6 +3,19 @@
3
3
  * Allows agents to mark tasks as pending, in_progress, completed, or blocked.
4
4
  */
5
5
  import type { ToolContext, ToolDefinition } from '@opencode-ai/plugin/tool';
6
+ import { tryAcquireLock } from '../parallel/file-locks.js';
7
+ import { updateTaskStatus } from '../plan/manager';
8
+ import { resolveWorkingDirectory } from './resolve-working-directory';
9
+ /**
10
+ * Internal seams for test injection.
11
+ * Tests should save/restore these in beforeEach/afterEach rather than using
12
+ * module-scope vi.mock, which leaks across files in Bun's shared test runner.
13
+ */
14
+ export declare const _internals: {
15
+ readonly tryAcquireLock: typeof tryAcquireLock;
16
+ readonly updateTaskStatus: typeof updateTaskStatus;
17
+ readonly resolveWorkingDirectory: typeof resolveWorkingDirectory;
18
+ };
6
19
  /**
7
20
  * Arguments for the update_task_status tool
8
21
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.65.3",
3
+ "version": "7.66.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",