opencode-swarm 7.189.0 → 7.190.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.
Files changed (177) hide show
  1. package/README.md +42 -6
  2. package/binaries/win32-arm64/swarm-sandbox-runner.exe +0 -0
  3. package/binaries/win32-x64/swarm-sandbox-runner.exe +0 -0
  4. package/dist/agents/architect.d.ts +1 -1
  5. package/dist/background/completion-observer.d.ts +6 -0
  6. package/dist/cli/{auto-checkpoint-m7c4x8bk.js → auto-checkpoint-26m3h5f4.js} +9 -9
  7. package/dist/cli/{checkpoint-m7q9886q.js → checkpoint-7a0achpv.js} +18 -20
  8. package/dist/cli/{coder-settlement-hh81z2f7.js → coder-settlement-dsb1t39t.js} +13 -13
  9. package/dist/cli/{config-doctor-0rexy3xt.js → config-doctor-9txawzng.js} +23 -20
  10. package/dist/cli/{core-ays6stz4.js → core-kv7jz9ty.js} +4 -4
  11. package/dist/cli/{curation-policy-65ckmmd5.js → curation-policy-6p6fcfjb.js} +6 -6
  12. package/dist/cli/{curator-dfzkfgs0.js → curator-26yzg8wp.js} +44 -44
  13. package/dist/cli/{curator-drift-nts5atks.js → curator-drift-kxn5sk3z.js} +9 -9
  14. package/dist/cli/curator-llm-factory-hs2y9kmw.js +82 -0
  15. package/dist/cli/{doctor-knowledge-receipt-phase-id-ec40k5hn.js → doctor-knowledge-receipt-phase-id-z8wea03e.js} +9 -9
  16. package/dist/cli/{evidence-summary-service-vgtm72g5.js → evidence-summary-service-bg5zq78s.js} +21 -22
  17. package/dist/cli/{gate-evidence-vmn65kxe.js → gate-evidence-0beg9c7r.js} +8 -8
  18. package/dist/cli/guardrail-explain-n8bm0hpm.js +83 -0
  19. package/dist/cli/{guardrail-log-hc8fjm4x.js → guardrail-log-r2zazmbn.js} +8 -8
  20. package/dist/cli/{guardrail-reset-ct00w300.js → guardrail-reset-0q54t44w.js} +44 -44
  21. package/dist/cli/{hive-promoter-2xax6van.js → hive-promoter-7dr5b9zv.js} +44 -44
  22. package/dist/cli/{index-9gqb5nkj.js → index-0gve0mkj.js} +4 -4
  23. package/dist/cli/{index-34vsyj15.js → index-0yrg10jx.js} +5 -5
  24. package/dist/cli/{index-mfxef20k.js → index-0z8vdkvg.js} +4 -4
  25. package/dist/cli/{index-2nexf5vz.js → index-1f084tqn.js} +22223 -15395
  26. package/dist/cli/{index-qx3g1bbe.js → index-26e2s6r1.js} +1 -1
  27. package/dist/cli/{index-kqegvngt.js → index-2c47efpt.js} +4 -4
  28. package/dist/cli/{index-9tvjwv01.js → index-2fpv2wmc.js} +1 -1
  29. package/dist/cli/{index-8q8783g0.js → index-3zwhxk98.js} +1 -1
  30. package/dist/cli/{index-9j3ybyfs.js → index-4a15347d.js} +7 -7
  31. package/dist/cli/{index-5cfhwd65.js → index-67a6mk20.js} +2 -2
  32. package/dist/cli/{index-d3rjvm9e.js → index-7vnr5cmp.js} +2 -2
  33. package/dist/cli/{index-twb3f4tk.js → index-8bx6vhzk.js} +1 -1
  34. package/dist/cli/{index-0gejy4cx.js → index-ay8ggtgr.js} +2 -2
  35. package/dist/cli/{index-8xabhfwr.js → index-b2j231jy.js} +2 -2
  36. package/dist/cli/{index-cbqkag7q.js → index-bcwmn4d7.js} +3 -3
  37. package/dist/cli/{index-fbgjj7h5.js → index-bfyh3gpe.js} +101 -13
  38. package/dist/cli/{index-skdpgjar.js → index-cewpxcwm.js} +862 -1221
  39. package/dist/cli/{index-bzbxn24k.js → index-dbdkh7pq.js} +5 -5
  40. package/dist/cli/{index-w4zscz6p.js → index-dnp4qtpf.js} +5 -5
  41. package/dist/cli/{index-waxvpwbv.js → index-efztf4yw.js} +127 -13
  42. package/dist/cli/{index-4p3jh14t.js → index-enq166kw.js} +4 -4
  43. package/dist/cli/{index-g03m5jhw.js → index-f46dbyew.js} +3 -3
  44. package/dist/cli/{index-ntda6tje.js → index-feaxsjrt.js} +10 -2
  45. package/dist/cli/{index-89f56ej4.js → index-fthhppnc.js} +46 -46
  46. package/dist/cli/{index-wqa31xnf.js → index-gqb8etv4.js} +7 -7
  47. package/dist/cli/{index-1cwmj1gs.js → index-ha0w0a7t.js} +77 -16
  48. package/dist/cli/{index-nhsafbrh.js → index-hx1fmywk.js} +5 -5
  49. package/dist/cli/{index-kaqdzhj3.js → index-kfevm0n5.js} +2 -2
  50. package/dist/cli/{index-z29hksyw.js → index-m28dzyy8.js} +26 -10
  51. package/dist/cli/{index-x1eh2636.js → index-mcv0v2dp.js} +1 -1
  52. package/dist/cli/{index-9czp2dcg.js → index-mjfpaxw3.js} +7 -7
  53. package/dist/cli/{index-f6exny9m.js → index-n8q43eff.js} +2 -2
  54. package/dist/cli/{index-7hr5m7yp.js → index-nkgzkt7c.js} +1 -1
  55. package/dist/cli/{index-p2tqzwh6.js → index-qs9myayh.js} +5 -10
  56. package/dist/cli/{index-hh7pa3yk.js → index-r9yvpr2m.js} +2 -2
  57. package/dist/cli/{index-cdjxzs2r.js → index-rjzn7hf0.js} +7 -7
  58. package/dist/cli/{index-zfbxfm2m.js → index-s8b67e6h.js} +33 -12
  59. package/dist/cli/{index-bskex45z.js → index-s9cgnd2z.js} +2 -2
  60. package/dist/cli/{index-cj4mp0fm.js → index-sc7t1tev.js} +6 -6
  61. package/dist/cli/{index-3dx0c7ve.js → index-sfjpbnqs.js} +44 -44
  62. package/dist/cli/{index-16m2m4ah.js → index-spgfs5y7.js} +1 -1
  63. package/dist/cli/{index-q9kefh33.js → index-sxj143a4.js} +1 -1
  64. package/dist/cli/{index-zsrtt42s.js → index-v3bmwmqw.js} +25 -1
  65. package/dist/cli/{index-5v09zw1q.js → index-v3j9fk16.js} +18 -8
  66. package/dist/cli/{index-2btybj8w.js → index-xdq6mak1.js} +4 -4
  67. package/dist/cli/{index-k8sarme1.js → index-y9v41185.js} +1 -1
  68. package/dist/cli/{index-4a7j5f60.js → index-yy5yyyst.js} +4 -4
  69. package/dist/cli/{index-1xa8wq76.js → index-z3ahy9c2.js} +2 -2
  70. package/dist/cli/{index-wrdktgpy.js → index-zk6ttjkt.js} +2 -2
  71. package/dist/cli/index.js +45 -45
  72. package/dist/cli/{knowledge-escalator-v3c8w86g.js → knowledge-escalator-hdrqt5pj.js} +15 -15
  73. package/dist/cli/{knowledge-events-kagaccj9.js → knowledge-events-ttebc8gn.js} +13 -13
  74. package/dist/cli/{knowledge-link-h4pdcnya.js → knowledge-link-6hw3z6wn.js} +5 -5
  75. package/dist/cli/{knowledge-store-ywxjk5pb.js → knowledge-store-8cxv6w2n.js} +6 -6
  76. package/dist/cli/{knowledge-validator-210gkpec.js → knowledge-validator-kr5xmqpa.js} +8 -8
  77. package/dist/cli/{mcp-58hgx6av.js → mcp-jtk79ygp.js} +1 -1
  78. package/dist/cli/{model-preflight-e69b48n0.js → model-preflight-d6tt7479.js} +44 -44
  79. package/dist/cli/{pending-delegations-g24d3qa9.js → pending-delegations-ttk09f50.js} +12 -12
  80. package/dist/cli/{pr-subscriptions-qprh2zsm.js → pr-subscriptions-2kbeezsr.js} +12 -12
  81. package/dist/cli/{pr-workflow-gate-pj7dhkv4.js → pr-workflow-gate-3ra6p7k4.js} +44 -44
  82. package/dist/cli/{runner-4hfmp1ag.js → runner-ssnsje35.js} +6 -6
  83. package/dist/cli/{scan-cursor-s5wsxxd1.js → scan-cursor-zzccy117.js} +7 -7
  84. package/dist/cli/{schema-smgngq9m.js → schema-9prswg1x.js} +14 -2
  85. package/dist/cli/{scope-persistence-tbv2ar4w.js → scope-persistence-eqm8vwnx.js} +16 -16
  86. package/dist/cli/{server-7p2ds5w2.js → server-swjre81h.js} +45 -45
  87. package/dist/cli/{skill-generator-r4hsne71.js → skill-generator-d8dk9vps.js} +17 -17
  88. package/dist/cli/{snapshot-coordination-init-ydjwh56d.js → snapshot-coordination-init-f49y0zr0.js} +44 -44
  89. package/dist/cli/{speckit-checkoff-aj75vmjq.js → speckit-checkoff-pnb6q9gd.js} +5 -5
  90. package/dist/cli/{telemetry-kvhe94gs.js → telemetry-9szfdfqv.js} +1 -1
  91. package/dist/cli/{workspace-snapshot-30kdf018.js → workspace-snapshot-9f12p1s1.js} +3 -3
  92. package/dist/cli/{worktree-collision-ownership-0bxj9f0t.js → worktree-collision-ownership-3c7xyc4p.js} +14 -14
  93. package/dist/cli/{worktree-isolation-h0c22pm2.js → worktree-isolation-jggdf28m.js} +46 -44
  94. package/dist/commands/coupling.d.ts +21 -4
  95. package/dist/commands/epic-learning.d.ts +28 -0
  96. package/dist/commands/epic.d.ts +81 -36
  97. package/dist/commands/registry.d.ts +8 -8
  98. package/dist/commands/turbo.d.ts +16 -1
  99. package/dist/config/constants.d.ts +11 -1
  100. package/dist/config/loader.d.ts +2 -0
  101. package/dist/config/schema.d.ts +291 -77
  102. package/dist/db/coordination-store.d.ts +6 -0
  103. package/dist/epic/close.d.ts +209 -0
  104. package/dist/{turbo/epic → epic}/cochange-conflict.d.ts +5 -4
  105. package/dist/{turbo/epic → epic}/cochange-source.d.ts +10 -1
  106. package/dist/epic/components.d.ts +238 -0
  107. package/dist/epic/config-gate.d.ts +25 -0
  108. package/dist/epic/config.d.ts +101 -0
  109. package/dist/{turbo/epic → epic}/coupling-report.d.ts +3 -1
  110. package/dist/epic/declared-scopes.d.ts +37 -0
  111. package/dist/epic/epic-branch.d.ts +157 -0
  112. package/dist/epic/gate-policy.d.ts +114 -0
  113. package/dist/epic/git-once.d.ts +26 -0
  114. package/dist/epic/learning-store.d.ts +176 -0
  115. package/dist/epic/learning.d.ts +198 -0
  116. package/dist/epic/legacy-migration.d.ts +26 -0
  117. package/dist/epic/lifecycle.d.ts +437 -0
  118. package/dist/epic/markers.d.ts +123 -0
  119. package/dist/epic/merge-epoch.d.ts +90 -0
  120. package/dist/epic/next-wave-format.d.ts +101 -0
  121. package/dist/epic/next-wave.d.ts +102 -0
  122. package/dist/epic/phase-readiness.d.ts +221 -0
  123. package/dist/epic/plan-key.d.ts +104 -0
  124. package/dist/epic/plan-shaping-seam.d.ts +112 -0
  125. package/dist/epic/planning-signals.d.ts +38 -0
  126. package/dist/epic/report.d.ts +152 -0
  127. package/dist/epic/residue-commit.d.ts +151 -0
  128. package/dist/epic/scorecard.d.ts +130 -0
  129. package/dist/epic/shaping-sizing.d.ts +108 -0
  130. package/dist/epic/shaping-suggestions.d.ts +172 -0
  131. package/dist/epic/shaping.d.ts +112 -0
  132. package/dist/epic/sizing.d.ts +60 -0
  133. package/dist/epic/start.d.ts +187 -0
  134. package/dist/epic/task-landing.d.ts +83 -0
  135. package/dist/epic/wave-close.d.ts +112 -0
  136. package/dist/epic/wave-select.d.ts +95 -0
  137. package/dist/evaluation/contracts.d.ts +1 -1
  138. package/dist/evaluation/public-api.d.ts +1 -1
  139. package/dist/full-auto/policy.d.ts +12 -1
  140. package/dist/hooks/delegation-gate/worktree-isolation.d.ts +16 -0
  141. package/dist/hooks/delegation-gate.d.ts +11 -0
  142. package/dist/hooks/guardrails/index.d.ts +7 -0
  143. package/dist/hooks/promotion-evidence-store.d.ts +2 -2
  144. package/dist/index.js +814 -807
  145. package/dist/plan/manager.d.ts +6 -8
  146. package/dist/plan/parallel-verdict.d.ts +13 -2
  147. package/dist/retention/caps.d.ts +1 -1
  148. package/dist/review/contracts.d.ts +8 -1
  149. package/dist/services/config-doctor.d.ts +38 -0
  150. package/dist/session/snapshot-reader.d.ts +2 -0
  151. package/dist/session/snapshot-writer.d.ts +13 -2
  152. package/dist/state.d.ts +13 -18
  153. package/dist/tools/epic-next-wave.d.ts +20 -0
  154. package/dist/tools/epic-phase-review.d.ts +43 -0
  155. package/dist/tools/index.d.ts +2 -3
  156. package/dist/tools/manifest.d.ts +2 -3
  157. package/dist/tools/phase-complete.d.ts +5 -1
  158. package/dist/tools/save-plan.d.ts +7 -0
  159. package/dist/tools/tool-metadata.d.ts +4 -8
  160. package/dist/worktree/merge.d.ts +14 -1
  161. package/opencode-swarm.schema.json +273 -28
  162. package/package.json +1 -1
  163. package/dist/cli/curator-llm-factory-nrn6n2p1.js +0 -82
  164. package/dist/cli/guardrail-explain-9jmr8ezt.js +0 -83
  165. package/dist/tools/epic-plan-waves.d.ts +0 -79
  166. package/dist/tools/epic-record-divergence.d.ts +0 -73
  167. package/dist/tools/epic-run-phase.d.ts +0 -179
  168. package/dist/turbo/epic/activation.d.ts +0 -193
  169. package/dist/turbo/epic/calibration-engine.d.ts +0 -88
  170. package/dist/turbo/epic/calibration.d.ts +0 -80
  171. package/dist/turbo/epic/divergence-recorder.d.ts +0 -123
  172. package/dist/turbo/epic/index.d.ts +0 -24
  173. package/dist/turbo/epic/promotion-evidence.d.ts +0 -42
  174. package/dist/turbo/epic/state.d.ts +0 -69
  175. package/dist/turbo/epic/task-commit.d.ts +0 -110
  176. package/dist/turbo/epic/upstream-commits.d.ts +0 -82
  177. package/dist/turbo/epic/wave-planner.d.ts +0 -83
@@ -48,11 +48,8 @@ export interface AcknowledgedRemovals {
48
48
  source: string;
49
49
  }
50
50
  import { type Plan, type RuntimePlan, type TaskStatus } from '../config/plan-schema';
51
- import { isGitRepo } from '../git/branch';
52
- import { getWorktreeMergeFailure } from '../hooks/delegation-gate/worktree-merge-status';
51
+ import { epicMergeFailureSkipsCheckpoint } from '../epic/merge-epoch.js';
53
52
  import { recordTaskAttempt } from '../services/run-memory.js';
54
- import { isEpicModeActiveForProject } from '../turbo/epic/state.js';
55
- import { commitTaskCompletion } from '../turbo/epic/task-commit.js';
56
53
  import type { AutoCheckpointOutcome } from './auto-checkpoint.js';
57
54
  import { type LedgerEvent, type LedgerEventInput, ledgerExists, loadLastApprovedPlan, readLedgerEventsWithIntegrity, replayFromLedger, takeSnapshotWithRetry } from './ledger';
58
55
  /** Reset the startup ledger check flag. For testing only. */
@@ -77,10 +74,11 @@ export declare const _internals: {
77
74
  loadLastApprovedPlan: typeof loadLastApprovedPlan;
78
75
  readLedgerEventsWithIntegrity: typeof readLedgerEventsWithIntegrity;
79
76
  regeneratePlanMarkdown: typeof regeneratePlanMarkdown;
80
- isGitRepo: typeof isGitRepo;
81
- isEpicModeActiveForProject: typeof isEpicModeActiveForProject;
82
- commitTaskCompletion: typeof commitTaskCompletion;
83
- getWorktreeMergeFailure: typeof getWorktreeMergeFailure;
77
+ /**
78
+ * Epic v2 C3: skip the #2582 auto-checkpoint for an open epic's task
79
+ * whose worktree merge-back failed (one existsSync when no epic).
80
+ */
81
+ epicMergeFailureSkipsCheckpoint: typeof epicMergeFailureSkipsCheckpoint;
84
82
  recordTaskAttempt: typeof recordTaskAttempt;
85
83
  /**
86
84
  * Issue #2582 — the checkpoint.auto_checkpoint_threshold runtime trigger,
@@ -36,8 +36,8 @@
36
36
  * acceptance criterion.
37
37
  */
38
38
  import { type Plan } from '../config/plan-schema.js';
39
+ import { type CoChangeThreshold } from '../epic/cochange-conflict.js';
39
40
  import type { CoChangeEntry } from '../tools/co-change-analyzer.js';
40
- import { type CoChangeThreshold } from '../turbo/epic/cochange-conflict.js';
41
41
  /**
42
42
  * Per-pair conflict classification. Mirrors `EpicPairVerdict`'s signal
43
43
  * decomposition but flattens `none` into `disjoint` for the plan-level view.
@@ -84,12 +84,23 @@ export interface ComputeParallelVerdictOptions {
84
84
  * or unparseable plan resolves every task to `unknown` (fail-closed).
85
85
  */
86
86
  plan?: Plan;
87
+ /**
88
+ * Explicit per-task scopes (Epic v2 C4: an epic wave's FROZEN declared
89
+ * scopes). When present, every task's scope is resolved ONLY from this
90
+ * map — the binding store is never read (no
91
+ * `readAuthoritativeScopeBindingSet` scan) and `plan` is neither needed
92
+ * nor read (it may be omitted). A task missing from the map, or mapped to an
93
+ * empty list, is `unknown` (fail-closed). When absent, behaviour is
94
+ * unchanged: scopes come from the live v2 bindings.
95
+ */
96
+ scopes?: Record<string, string[]>;
87
97
  }
88
98
  /**
89
99
  * Compute a pairwise conflict verdict for the given task ids.
90
100
  *
91
101
  * Pure + synchronous. Reads only the authoritative v2 binding store (plus a
92
- * bounded plan.json read when `options.plan` is omitted). Writes nothing.
102
+ * bounded plan.json read when `options.plan` is omitted) — or nothing at all
103
+ * when `options.scopes` supplies the scopes explicitly. Writes nothing.
93
104
  * Fail-closed on any read/parse error (treats the task as `unknown`).
94
105
  *
95
106
  * @param directory Project root (for the binding store and plan.json reads).
@@ -12,7 +12,7 @@
12
12
  * both the Bun and Node hosts (AGENTS.md invariant 2).
13
13
  */
14
14
  /** Canonical cap names (frozen by the #2483 acceptance checks). */
15
- export type RetentionCapName = 'MAX_RETRACTION_RECORDS' | 'MAX_UNACKNOWLEDGED_CRITICALS' | 'MAX_CURATION_PROPOSALS' | 'MAX_CONSOLIDATION_LOG_ENTRIES' | 'MAX_CONTEXT_SNAPSHOT_BYTES' | 'MAX_CONTEXT_SNAPSHOT_ENTRIES' | 'MAX_CALIBRATION_MODULES' | 'MAX_DIVERGENCE_BYTES' | 'MAX_TEST_HISTORY_ENTRIES' | 'MAX_TEST_HISTORY_KEYS' | 'MAX_SKILL_CHANGELOG_ENTRIES_PER_SKILL' | 'MAX_SKILL_CHANGELOG_GLOBAL_ENTRIES' | 'MAX_RECEIPTS_READ' | 'MAX_CAPSULES_LISTED' | 'MAX_SUMMARIES_LISTED' | 'MAX_RUN_LOG_ENTRIES' | 'MAX_UNITID_PROBE_ENTRIES';
15
+ export type RetentionCapName = 'MAX_RETRACTION_RECORDS' | 'MAX_UNACKNOWLEDGED_CRITICALS' | 'MAX_CURATION_PROPOSALS' | 'MAX_CONSOLIDATION_LOG_ENTRIES' | 'MAX_CONTEXT_SNAPSHOT_BYTES' | 'MAX_CONTEXT_SNAPSHOT_ENTRIES' | 'MAX_EPIC_LEARNING_FILES' | 'MAX_EPIC_LEARNING_EDGES' | 'MAX_TEST_HISTORY_ENTRIES' | 'MAX_TEST_HISTORY_KEYS' | 'MAX_SKILL_CHANGELOG_ENTRIES_PER_SKILL' | 'MAX_SKILL_CHANGELOG_GLOBAL_ENTRIES' | 'MAX_RECEIPTS_READ' | 'MAX_CAPSULES_LISTED' | 'MAX_SUMMARIES_LISTED' | 'MAX_RUN_LOG_ENTRIES' | 'MAX_UNITID_PROBE_ENTRIES';
16
16
  /**
17
17
  * Install test cap overrides (partial). Production code never calls this;
18
18
  * tests and the #2483 acceptance checks do. Values are validated to be
@@ -1,5 +1,5 @@
1
1
  import type { OpencodeClient } from '@opencode-ai/sdk';
2
- import { type EphemeralAgentDispatchResult } from '../evaluation/ephemeral-agent-dispatcher.js';
2
+ import { type EphemeralAgentDispatchResult, type ReadOnlyToolDenials } from '../evaluation/ephemeral-agent-dispatcher.js';
3
3
  import type { PricingConfig } from '../services/cost-accounting.js';
4
4
  import type { ModelOverride } from '../utils/model-dispatch-fallback.js';
5
5
  export type ReviewDispatchRequest = {
@@ -16,6 +16,13 @@ export type ReviewDispatchRequest = {
16
16
  promptByteLimit?: number;
17
17
  responseByteLimit?: number;
18
18
  abortSignal?: AbortSignal;
19
+ /**
20
+ * Tool-denial map for the isolated session. Omitted ⇒
21
+ * `DEFAULT_READ_ONLY_TOOLS` (every caller except Epic's phase review,
22
+ * whose provider-refusal retry keeps `bash` — see
23
+ * `src/epic/phase-readiness.ts`).
24
+ */
25
+ tools?: ReadOnlyToolDenials;
19
26
  };
20
27
  export type ReviewDispatchResult = EphemeralAgentDispatchResult;
21
28
  export interface ReviewModelDispatcher {
@@ -4,6 +4,7 @@
4
4
  * Validates opencode-swarm config shape, detects stale/invalid settings,
5
5
  * classifies findings by severity, and proposes safe auto-fixes.
6
6
  */
7
+ import { type ConfigConsumerDeclaration } from '../config/consumers';
7
8
  import type { PluginConfig } from '../config/schema';
8
9
  import { loadPlanJsonOnly } from '../plan/manager';
9
10
  /**
@@ -30,6 +31,20 @@ export declare const DEFAULT_FLIPS: ReadonlyArray<{
30
31
  sinceVersion: number;
31
32
  evidence: string;
32
33
  }>;
34
+ /**
35
+ * Inert config-key advisories (issue #2904), driven by the RAW config files so
36
+ * they fire ONLY when the user explicitly wrote the key — schema defaults never
37
+ * produce noise (same contract as the #2102 raw collectors above). The inert set
38
+ * comes from CONFIG_CONSUMERS (`src/config/consumers.ts`), the single
39
+ * declaration of consumer truth enforced by `bun run scripts/check-config-consumption.ts`.
40
+ *
41
+ * The `consumers` parameter is a default-parameter DI seam (issue #2957): tests
42
+ * inject declaration states so both arms of `ConfigConsumerDeclaration`
43
+ * (inert / consumed) are provable on any tree, without `mock.module` — the same
44
+ * pattern as `collectLeanTurboGateSatisfiabilityFindings`'s
45
+ * `registeredToolNames` seam below.
46
+ */
47
+ export declare function collectRawInertKeyFindings(directory: string, consumers?: Readonly<Record<string, ConfigConsumerDeclaration>>): ConfigFinding[];
33
48
  /**
34
49
  * Lean Turbo evidence-requiring gates and the registered producer tool that
35
50
  * writes each gate's evidence (issue #2470: every evidence-requiring gate must
@@ -58,6 +73,29 @@ export declare const LEAN_TURBO_GATE_PRODUCERS: readonly [{
58
73
  * gate becomes structurally unsatisfiable and this lint fails with an error.
59
74
  */
60
75
  export declare function collectLeanTurboGateSatisfiabilityFindings(config: PluginConfig, registeredToolNames?: ReadonlySet<string>): ConfigFinding[];
76
+ /**
77
+ * Retired Epic config keys (Epic v2). The schema accepts and strips them
78
+ * (with a precise "retired" loader warning instead of an unrecognized-key
79
+ * recovery), so the parsed config the doctor walks no longer has them; read
80
+ * the raw user + project files instead. Only when the parsed config has an
81
+ * Epic block (top-level `epic`, or the legacy `turbo.epic` — every retired key
82
+ * lives in one): a non-Epic doctor run reads no extra file. Report-only:
83
+ * nothing reads the key, and removing it is the user's edit (no auto-fix
84
+ * writes the config for a no-op key).
85
+ */
86
+ export declare function collectRawRetiredEpicKeyFindings(config: PluginConfig, directory: string): ConfigFinding[];
87
+ /**
88
+ * Legacy Epic config path (Epic C9): a config file that still sets
89
+ * `turbo.epic`. The loader migrates it to that file's top-level `epic`
90
+ * (per-key merge, the file's top-level `epic` wins) before the files are
91
+ * merged, and keeps honoring it permanently; this finding tells the user
92
+ * where to move it. Report-only, like `retired-config-key`: the doctor's fix
93
+ * model (`remove` / `update` / `add` of one value, on the one preferred
94
+ * config file) has no move-and-merge, and it would only ever touch one of
95
+ * the two files. Only when the parsed config has an Epic block: a non-Epic
96
+ * doctor run reads no extra file.
97
+ */
98
+ export declare function collectRawLegacyEpicConfigFindings(config: PluginConfig, directory: string): ConfigFinding[];
61
99
  /**
62
100
  * Test-only dependency-injection seam per invariant 7 (mock.module leaks
63
101
  * across files in Bun's shared test runner; `loadPlanJsonOnly` is a frequent
@@ -2,12 +2,14 @@
2
2
  * Session snapshot reader for OpenCode Swarm plugin.
3
3
  * Reads .swarm/session/state.json and rehydrates swarmState on plugin init.
4
4
  */
5
+ import { isEpicOpenForProject } from '../epic/lifecycle.js';
5
6
  import type { AgentSessionState } from '../state';
6
7
  import { type HydrationScope } from './hydration-ownership.js';
7
8
  import { recordInterruptedExecution } from './restart-reconciliation.js';
8
9
  import type { SerializedAgentSession, SnapshotData } from './snapshot-writer';
9
10
  export declare const _internals: {
10
11
  recordInterruptedExecution: typeof recordInterruptedExecution;
12
+ isEpicOpenForProject: typeof isEpicOpenForProject;
11
13
  };
12
14
  /**
13
15
  * Transient session fields that must be reset on rehydration.
@@ -36,7 +36,13 @@ export interface SerializedAgentSession {
36
36
  turboStrategy?: 'standard' | 'lean';
37
37
  leanTurboActive?: boolean;
38
38
  leanTurboCurrentPhase?: number;
39
- epicModeActive?: boolean;
39
+ /**
40
+ * Frozen legacy field (Epic v1 per-session flag). Epic v2 is plan-scoped
41
+ * and has no session field; the writer keeps emitting the constant
42
+ * `false` so non-Epic snapshot JSON stays byte-identical, and the reader
43
+ * ignores the value. See {@link LEGACY_SERIALIZED_ONLY_FIELDS}.
44
+ */
45
+ epicModeActive?: false;
40
46
  gateLog: Record<string, string[]>;
41
47
  reviewerCallCount: Record<string, number>;
42
48
  lastGateFailure: {
@@ -145,7 +151,12 @@ export declare const SESSION_TRANSIENT_FIELDS: Readonly<Record<Exclude<keyof Age
145
151
  * state counterpart, the type degrades to the error tuple below and assigning
146
152
  * `true` fails `bun run typecheck` with that message.
147
153
  */
148
- export type SerializedFieldsAllExistOnState = Exclude<keyof SerializedAgentSession, keyof AgentSessionState> extends never ? true : ['SerializedAgentSession field missing from AgentSessionState'];
154
+ /**
155
+ * Serialized fields kept only for byte-stable snapshot JSON, with no live
156
+ * session counterpart (written as a constant, ignored on read).
157
+ */
158
+ export type LEGACY_SERIALIZED_ONLY_FIELDS = 'epicModeActive';
159
+ export type SerializedFieldsAllExistOnState = Exclude<keyof SerializedAgentSession, keyof AgentSessionState | LEGACY_SERIALIZED_ONLY_FIELDS> extends never ? true : ['SerializedAgentSession field missing from AgentSessionState'];
149
160
  export declare const _serializedFieldsExistOnState: SerializedFieldsAllExistOnState;
150
161
  /**
151
162
  * Minimal interface for serialized InvocationWindow
package/dist/state.d.ts CHANGED
@@ -424,11 +424,6 @@ export interface AgentSessionState {
424
424
  * When set, overrides the plan's execution_profile.max_concurrent_tasks
425
425
  * for delegation-gate guidance. Cleared on session reset. */
426
426
  maxConcurrencyOverride?: number;
427
- /** Whether Epic Mode (additive overlay above Lean Turbo) is active for
428
- * this session. Durable mirror lives in `.swarm/epic-state.json`; this
429
- * in-memory flag matches what `src/turbo/epic/state.ts` persists and is
430
- * what `hasActiveEpicMode(sessionID)` reads on the hot path. */
431
- epicModeActive?: boolean;
432
427
  /** Session-scoped override for execution_profile.auto_proceed.
433
428
  * When set, overrides the plan's auto_proceed for this session.
434
429
  * true = auto-advance, false = do not auto-advance. Cleared on session reset. */
@@ -1173,6 +1168,13 @@ export declare function maybeSweepStaleSessions(staleDurationMs?: number, now?:
1173
1168
  * constructions (e.g. the recovery session) and any config-read failure
1174
1169
  * default to false. `/swarm turbo` remains the per-session authority after
1175
1170
  * construction.
1171
+ *
1172
+ * Epic seam: while an epic is open for the project's current plan
1173
+ * (`isEpicOpenForProject` — config-gated and identity-checked) config
1174
+ * cannot seed Turbo on, because Turbo waives per-task QA that Epic never
1175
+ * waives. The sentinel is probed only when config would otherwise seed
1176
+ * `true`, so the common path does no extra I/O and a project without an
1177
+ * open epic gets the identical result.
1176
1178
  */
1177
1179
  export declare function resolveInitialTurboMode(directory?: string): boolean;
1178
1180
  export declare function startAgentSession(sessionId: string, agentName: string, staleDurationMs?: number, directory?: string): void;
@@ -1348,8 +1350,12 @@ export declare function resetModifiedFilesForTask(session: AgentSessionState, ta
1348
1350
  /**
1349
1351
  * Apply task-completion retention rules.
1350
1352
  *
1351
- * Non-Epic sessions release attribution at the workflow-complete boundary.
1352
- * Epic sessions retain it until `epic_record_divergence` consumes the entry.
1353
+ * Attribution is released at the workflow-complete boundary, except while an
1354
+ * epic is open for the session's project: then it is retained until its
1355
+ * wave closes (`epic_next_wave` → `wave-close.ts` reads it for divergence
1356
+ * and releases it). The project comes from the session's owning project key
1357
+ * (a canonical root path); an unowned session is treated as non-Epic. With
1358
+ * no epic the probe costs one `existsSync`.
1353
1359
  */
1354
1360
  export declare function completeModifiedFilesForTask(session: AgentSessionState, taskId: string): void;
1355
1361
  export declare function updateTaskWorkflowCache(session: AgentSessionState, taskId: string, overrides: Partial<TaskWorkflowCacheEntry>): void;
@@ -1547,16 +1553,6 @@ export declare function getActiveFullAutoSessionID(): string | undefined;
1547
1553
  * or if any session has that combination when no sessionID provided.
1548
1554
  */
1549
1555
  export declare function hasActiveLeanTurbo(sessionID?: string): boolean;
1550
- /**
1551
- * Check if Epic Mode is active for a specific session or ANY session.
1552
- * Mirrors `hasActiveLeanTurbo` but reads `session.epicModeActive`. The flag
1553
- * is set by `enableEpicMode` (and by `/swarm turbo epic on`) and cleared by
1554
- * `disableEpicMode` (and `/swarm turbo epic off`). The durable mirror is
1555
- * `.swarm/epic-state.json` — see `src/turbo/epic/state.ts`. Epic Mode does
1556
- * NOT require `turboStrategy === 'lean'`; it composes Lean Turbo internally
1557
- * inside `epic_run_phase`.
1558
- */
1559
- export declare function hasActiveEpicMode(sessionID?: string): boolean;
1560
1556
  /**
1561
1557
  * Resolves the effective auto_proceed value for a session.
1562
1558
  * Session override (autoProceedOverride) takes precedence over the plan default.
@@ -1730,7 +1726,6 @@ export declare const _internals: {
1730
1726
  hasActiveFullAuto: typeof hasActiveFullAuto;
1731
1727
  hasActiveTurboMode: typeof hasActiveTurboMode;
1732
1728
  hasActiveLeanTurbo: typeof hasActiveLeanTurbo;
1733
- hasActiveEpicMode: typeof hasActiveEpicMode;
1734
1729
  buildRehydrationCache: typeof buildRehydrationCache;
1735
1730
  applyRehydrationCache: typeof applyRehydrationCache;
1736
1731
  rehydrateSessionFromDisk: typeof rehydrateSessionFromDisk;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Epic Mode `epic_next_wave` tool (Epic v2 C2) — architect-only, gated by
3
+ * `epic.mode.enabled` through `EPIC_AGENT_TOOL_MAP`.
4
+ *
5
+ * The single way forward while an epic is open: it closes the active wave
6
+ * when every task is resolved (recording outcomes and divergence), keeps
7
+ * phases in order, and issues the next wave with dispatch instructions.
8
+ * Idempotent: calling it again while a wave runs returns `in-progress` for
9
+ * the same wave. See `src/epic/next-wave.ts` for the full contract.
10
+ */
11
+ import type { ToolDefinition } from '@opencode-ai/plugin/tool';
12
+ import { runEpicNextWave } from '../epic/next-wave.js';
13
+ /**
14
+ * DI seam (AGENTS.md invariant 7); restore in afterEach.
15
+ * @tool-opt-out Test-only dependency-injection seam; not a public tool.
16
+ */
17
+ export declare const _internals: {
18
+ runEpicNextWave: typeof runEpicNextWave;
19
+ };
20
+ export declare const epic_next_wave: ToolDefinition;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Epic Mode phase review tool (`epic_phase_review`).
3
+ *
4
+ * Satisfies the `epic_phase_readiness` gate in `phase_complete`. The tool —
5
+ * not the architect — dispatches a read-only phase reviewer and, when the
6
+ * reviewer APPROVES, a read-only phase critic through the plugin-owned review
7
+ * dispatcher, parses each verdict from the agent's own response, and records
8
+ * both to `.swarm/evidence/{phase}/epic-phase-review.json` bound to the
9
+ * current plan / phase task state. See `src/epic/phase-readiness.ts`.
10
+ */
11
+ import type { ToolDefinition } from '@opencode-ai/plugin/tool';
12
+ import { getOpenEpic, isEpicOpenForProject, updateEpicRecord } from '../epic/lifecycle.js';
13
+ import { describeOpenEpicWaves, type EpicPhaseReviewRunResult, resolveEpicPhaseReviewTimeoutMs, runEpicPhaseReview } from '../epic/phase-readiness.js';
14
+ import type { ReviewModelDispatcher } from '../review/contracts.js';
15
+ import type { ReviewAgentModelRegistry } from '../review/runtime.js';
16
+ export type EpicPhaseReviewToolResult = EpicPhaseReviewRunResult | {
17
+ success: false;
18
+ phase: number;
19
+ reason: 'epic-mode-not-active' | 'no-session' | 'invalid-phase' | 'waves-open';
20
+ message: string;
21
+ };
22
+ /**
23
+ * DI seam (AGENTS.md invariant 7); restore in afterEach.
24
+ * @tool-opt-out Test-only dependency-injection seam; not a public tool.
25
+ */
26
+ export declare const _internals: {
27
+ runEpicPhaseReview: typeof runEpicPhaseReview;
28
+ resolveEpicPhaseReviewTimeoutMs: typeof resolveEpicPhaseReviewTimeoutMs;
29
+ isEpicOpenForProject: typeof isEpicOpenForProject;
30
+ describeOpenEpicWaves: typeof describeOpenEpicWaves;
31
+ getOpenEpic: typeof getOpenEpic;
32
+ updateEpicRecord: typeof updateEpicRecord;
33
+ };
34
+ export declare function executeEpicPhaseReview(args: {
35
+ phase: number;
36
+ }, directory: string, sessionID: string | undefined, options?: {
37
+ dispatcher?: ReviewModelDispatcher;
38
+ generatedAgentNames?: readonly string[];
39
+ agentModelRegistry?: ReviewAgentModelRegistry;
40
+ activeAgentName?: string;
41
+ }): Promise<EpicPhaseReviewToolResult>;
42
+ export declare function createEpicPhaseReviewTool(dispatcher?: ReviewModelDispatcher, generatedAgentNames?: readonly string[], agentModelRegistry?: ReviewAgentModelRegistry, getActiveAgentName?: (sessionID: string) => string | undefined): ToolDefinition;
43
+ export declare const epic_phase_review: ToolDefinition;
@@ -104,9 +104,8 @@ export { approve_retry_sounding_board, executeApproveRetrySoundingBoard, } from
104
104
  export { authorize_pr_review_reentry } from './authorize-pr-review-reentry';
105
105
  export { CancelLaneBatchArgsSchema, cancel_lane_batch, executeCancelLaneBatch, } from './cancel-lane-batch';
106
106
  export { complete_pr_workflow, executeCompletePrWorkflow, } from './complete-pr-workflow';
107
- export { epic_plan_waves } from './epic-plan-waves';
108
- export { epic_record_divergence } from './epic-record-divergence';
109
- export { epic_decide_phase } from './epic-run-phase';
107
+ export { epic_next_wave } from './epic-next-wave';
108
+ export { epic_phase_review } from './epic-phase-review';
110
109
  export { generate_mutants } from './generate-mutants';
111
110
  export { executeInvalidatePrFeedbackPublication, invalidate_pr_feedback_publication, } from './invalidate-pr-feedback-publication';
112
111
  export { lean_turbo_acquire_locks } from './lean-turbo-acquire-locks';
@@ -160,7 +160,6 @@ export declare const TOOL_MANIFEST: {
160
160
  external_skill_reject: () => ToolDefinition;
161
161
  external_skill_delete: () => ToolDefinition;
162
162
  external_skill_revoke: () => ToolDefinition;
163
- epic_decide_phase: () => ToolDefinition;
164
- epic_plan_waves: () => ToolDefinition;
165
- epic_record_divergence: () => ToolDefinition;
163
+ epic_next_wave: () => ToolDefinition;
164
+ epic_phase_review: () => ToolDefinition;
166
165
  };
@@ -3,7 +3,8 @@
3
3
  * Core implementation - gathers data, enforces policy, writes event, resets state.
4
4
  */
5
5
  import type { ToolDefinition } from '@opencode-ai/plugin/tool';
6
- import { savePlan } from '../plan/manager';
6
+ import { isEpicOpenForProject, markEpicPhaseComplete } from '../epic/lifecycle';
7
+ import { verifyEpicPhaseReadiness } from '../epic/phase-readiness';
7
8
  import type { ReviewModelDispatcher } from '../review/contracts.js';
8
9
  import type { ReviewAgentModelRegistry } from '../review/runtime.js';
9
10
  import { runArchitectureSupervisorGate, runCompletionVerifyGate, runDriftGate, runFinalCouncilGate, runFinalReviewGate, runHallucinationGate, runMutationGate, runPhaseCouncilGate, runTodoGateGate } from './phase-complete/gates/index.js';
@@ -105,6 +106,7 @@ export declare const phaseCompleteCommitInternals: {
105
106
  };
106
107
  preCommitCheck?: () => void;
107
108
  } | undefined) => Promise<import("../plan").PlanSaveDurability>;
109
+ markEpicPhaseComplete: typeof markEpicPhaseComplete;
108
110
  };
109
111
  /** Injectable observational gates for aggregate-preflight regression tests. */
110
112
  export declare const phaseCompletePreflightInternals: {
@@ -117,6 +119,8 @@ export declare const phaseCompletePreflightInternals: {
117
119
  runFinalReviewGate: typeof runFinalReviewGate;
118
120
  runFinalCouncilGate: typeof runFinalCouncilGate;
119
121
  runTodoGateGate: typeof runTodoGateGate;
122
+ verifyEpicPhaseReadiness: typeof verifyEpicPhaseReadiness;
123
+ isEpicOpenForProject: typeof isEpicOpenForProject;
120
124
  };
121
125
  /**
122
126
  * Arguments for the phase_complete tool
@@ -5,6 +5,7 @@
5
5
  import type { ToolDefinition } from '@opencode-ai/plugin/tool';
6
6
  import { loadPluginConfigWithMeta } from '../config';
7
7
  import { type ExecutionProfile } from '../config/plan-schema';
8
+ import { computeSavePlanEpicShaping, type EpicSavePlanShaping } from '../epic/plan-shaping-seam.js';
8
9
  import { takeSnapshotWithRetry } from '../plan/ledger';
9
10
  /**
10
11
  * DI seam for hermetic config-load substitution in tests (AGENTS.md invariant 7).
@@ -16,6 +17,7 @@ import { takeSnapshotWithRetry } from '../plan/ledger';
16
17
  */
17
18
  export declare const _internals: {
18
19
  loadPluginConfigWithMeta: typeof loadPluginConfigWithMeta;
20
+ computeSavePlanEpicShaping: typeof computeSavePlanEpicShaping;
19
21
  };
20
22
  /** Test seam for the snapshot retry helper (FR-004). */
21
23
  export declare const _test_exports: {
@@ -116,6 +118,11 @@ export interface SavePlanResult {
116
118
  requirement_coverage?: RequirementCoverageResult;
117
119
  /** The resolved execution_profile that was persisted, if any. */
118
120
  execution_profile?: ExecutionProfile;
121
+ /**
122
+ * Epic v2 C7 plan-shaping advisory — present only when
123
+ * `epic.mode.enabled` is true and no epic is open.
124
+ */
125
+ epic_shaping?: EpicSavePlanShaping;
119
126
  }
120
127
  interface RequirementCoverageEntry {
121
128
  id: string;
@@ -673,17 +673,13 @@ export declare const TOOL_METADATA: {
673
673
  description: string;
674
674
  agents: never[];
675
675
  };
676
- epic_decide_phase: {
676
+ epic_next_wave: {
677
677
  description: string;
678
- agents: "architect"[];
679
- };
680
- epic_plan_waves: {
681
- description: string;
682
- agents: "architect"[];
678
+ agents: never[];
683
679
  };
684
- epic_record_divergence: {
680
+ epic_phase_review: {
685
681
  description: string;
686
- agents: "architect"[];
682
+ agents: never[];
687
683
  };
688
684
  };
689
685
  /** Union type of all valid tool names (the metadata keys). */
@@ -203,6 +203,17 @@ export interface DirtyMergeOptions {
203
203
  * `'rebase'` and `'cherry-pick'` always land committed regardless.
204
204
  */
205
205
  commitLanding?: boolean;
206
+ /**
207
+ * Epic v2 C3: message of the committed landing (only with
208
+ * `commitLanding` on a `'merge'` dispatch). When set, the lane lands as
209
+ * an explicit merge commit carrying this message (`git -c
210
+ * commit.gpgsign=false merge --no-ff --no-edit --no-verify -m <message>`)
211
+ * so the landing is identifiable in history. It is a protocol commit on
212
+ * the epic branch: repository commit hooks and signing apply when the
213
+ * user commits the epic's squash at close. Absent ⇒ the plain
214
+ * `git merge --no-edit` landing.
215
+ */
216
+ landingCommitMessage?: string;
206
217
  /**
207
218
  * Awaited after auto-commit and HEAD capture, but before the Git merge.
208
219
  * A rejection fails closed without invoking the merge.
@@ -274,7 +285,9 @@ export declare function getMergeStrategy(config: {
274
285
  * @param strategy - Merge strategy to use.
275
286
  * @returns Discriminated union: success, conflict, or failure.
276
287
  */
277
- export declare function mergeLaneBranch(primaryDir: string, branchName: string, strategy: MergeStrategy): Promise<MergeSuccess | MergeConflict | MergeFailure>;
288
+ export declare function mergeLaneBranch(primaryDir: string, branchName: string, strategy: MergeStrategy,
289
+ /** Epic v2 C3: explicit merge-commit message (see `DirtyMergeOptions`). */
290
+ commitMessage?: string): Promise<MergeSuccess | MergeConflict | MergeFailure>;
278
291
  /**
279
292
  * Re-applies a preserved lane using exact immutable object ids captured by the
280
293
  * #2105 recovery authority.