opencode-swarm 7.52.1 → 7.52.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  ---
18
18
 
19
- OpenCode Swarm is a plugin for [OpenCode](https://opencode.ai) that turns a single AI coding session into an **architect-led team of specialized core, optional, and conditional agents** see `/swarm agents` for the live roster. One agent writes the code. A different agent reviews it. Another writes and runs tests. Another checks security. **Nothing ships until every required gate passes.**
19
+ OpenCode Swarm is a plugin for [OpenCode](https://opencode.ai) that turns a single AI coding session into an **architect-led team of specialized core, optional, and conditional agents**. Run `/swarm agents` for the live roster; it is generated from the current plugin configuration. One agent writes the code. A different agent reviews it. Another writes and runs tests. Another checks security. **Nothing ships until every required gate passes.**
20
20
 
21
21
  ```bash
22
22
  bunx opencode-swarm install
@@ -24,7 +24,7 @@ bunx opencode-swarm install
24
24
 
25
25
  > This single command installs the package, registers it as an OpenCode plugin, disables conflicting default agents, and creates a ready-to-edit config at `~/.config/opencode/opencode-swarm.json`. Requires [Bun](https://bun.sh) (`bun --version` to check). If you must use npm: `npm install -g opencode-swarm && opencode-swarm install`.
26
26
 
27
- > ⚠️ **On first run, Swarm auto-selects the architect and shows a welcome message.** The default OpenCode `Build` and `Plan` modes **bypass this plugin entirely** none of the gates, reviewers, or test agents below run. If you ever need to switch architect manually, open the OpenCode mode/agent picker and choose the Swarm architect; it then coordinates every other agent automatically. If you ever see Swarm "do nothing," this is almost always the cause.
27
+ > **First-run note:** the installer registers the plugin, writes the global plugin config, creates a project override when missing, and disables the native `explore` and `general` agents in `opencode.json`. If you are not using a Swarm architect, the Swarm gates, reviewers, and test agents are bypassed. Open the OpenCode agent or mode picker and choose the Swarm architect when needed.
28
28
 
29
29
  ### Why Swarm?
30
30
 
@@ -44,7 +44,7 @@ Most AI coding tools let one model write code and ask that same model whether th
44
44
  - 🆓 **Free tier** — works with OpenCode Zen's free model roster
45
45
  - ⚙️ **Fully configurable** — override any agent's model, disable agents, tune guardrails
46
46
 
47
- > **The Swarm architect is auto-selected on first run and coordinates all other agents automatically.** You never manually switch between internal roles. If you use the default OpenCode `Build` / `Plan` modes, the plugin is bypassed entirely (see the install warning above).
47
+ > **The Swarm architect coordinates all internal agents automatically.** You never manually switch between internal roles. If the active OpenCode agent is not a Swarm architect, the plugin workflow is bypassed.
48
48
 
49
49
  ---
50
50
 
@@ -157,10 +157,10 @@ The 15-minute guide covers:
157
157
  - Running your first task
158
158
  - Troubleshooting common issues
159
159
 
160
- On first run, Swarm automatically:
161
- - Creates project config at `.opencode/opencode-swarm.json` with all agents enabled
162
- - Selects the Swarm architect as the default
163
- - Shows a welcome message with next steps
160
+ The installer automatically:
161
+ - Creates a project config at `.opencode/opencode-swarm.json` when missing so project-level overrides have a place to live
162
+ - Adds `opencode-swarm` to the OpenCode plugin list
163
+ - Disables the native `explore` and `general` agents to reduce routing conflicts
164
164
 
165
165
  ---
166
166
 
@@ -174,8 +174,7 @@ No animated GIF is shipped in the repo — instead, here is the exact terminal s
174
174
  # 1. Install the plugin (5s)
175
175
  bunx opencode-swarm install
176
176
 
177
- # 2. Open opencode Swarm auto-selects architect on first run
178
- # (the architect is auto-selected; manual selection is only needed to override)
177
+ # 2. Open opencode and select a Swarm architect if it is not already active
179
178
  opencode
180
179
 
181
180
  # 3. Inside the OpenCode session, verify Swarm is live (5s)
@@ -225,12 +224,18 @@ Each row corresponds to a real gate documented further down this README — none
225
224
  **OpenCode caches plugins indefinitely.** A normal OpenCode restart does **not**
226
225
  pull newer versions from npm — once a plugin is cached, OpenCode keeps using
227
226
  that exact copy on every subsequent launch (issue #675). The cache lives in
228
- one of two places depending on your platform:
227
+ several places depending on your platform and OpenCode version:
229
228
 
230
- - Linux / devcontainers / GitHub Codespaces:
229
+ - Current OpenCode cache layout:
230
+ `~/.cache/opencode/node_modules/opencode-swarm/`
231
+ - Legacy Linux / devcontainers / GitHub Codespaces:
231
232
  `~/.config/opencode/node_modules/opencode-swarm/`
232
- - Some macOS / Windows installs:
233
+ - Package cache layout:
233
234
  `~/.cache/opencode/packages/opencode-swarm@latest/`
235
+ - Platform-specific macOS / Windows cache roots:
236
+ `~/Library/Caches/opencode/...`, `%LOCALAPPDATA%\opencode\...`, or `%APPDATA%\opencode\...`
237
+
238
+ The updater also clears known OpenCode lock files (`bun.lock`, `bun.lockb`, and `package-lock.json`) so the next start resolves the latest package.
234
239
 
235
240
  To upgrade to the latest published version (clears both layouts automatically):
236
241
 
@@ -250,7 +255,7 @@ in your `opencode-swarm.json`.
250
255
 
251
256
  ## Commands
252
257
 
253
- All 43 subcommands at a glance:
258
+ Common subcommands at a glance:
254
259
 
255
260
  ```bash
256
261
  /swarm help [command] # List all commands or get detailed help for a specific command
@@ -266,7 +271,7 @@ Use `/swarm help` to see all available commands categorized by function. Use `/s
266
271
 
267
272
  Nine commands display a ⚠️ warning in help output because they share names with Claude Code built-in slash commands (e.g., `/plan`, `/reset`, `/status`). The warning reminds you to always use `/swarm <command>` — the bare CC command does something different and sometimes destructive. See [docs/commands.md#claude-code-command-conflicts](docs/commands.md#claude-code-command-conflicts) for the full conflict registry.
268
273
 
269
- See [docs/commands.md](docs/commands.md) for the full reference (43 commands).
274
+ See [docs/commands.md](docs/commands.md) for the full reference. The live source of truth is `src/commands/registry.ts`, which includes canonical commands, compound commands, and deprecated aliases.
270
275
 
271
276
  ## Command Aliases
272
277
 
@@ -529,13 +534,13 @@ Override via `authority.rules` in config.
529
534
 
530
535
  Built-in tools verify every task before it ships:
531
536
 
532
- - **syntax_check** — Tree-sitter validation (12 languages)
537
+ - **syntax_check** — Tree-sitter validation across the configured language grammar map
533
538
  - **placeholder_scan** — Catches TODOs, stubs, incomplete code
534
539
  - **sast_scan** — 63+ security rules, 9 languages (offline)
535
540
  - **sbom_generate** — Dependency tracking (CycloneDX)
536
541
  - **quality_budget** — Complexity, duplication, test ratio limits
537
542
 
538
- All tools run locally. No Docker, no network calls.
543
+ These quality gates run locally. No Docker is required. Config-gated features such as General Council web search can make external API calls only when you enable them and provide a Tavily or Brave Search key.
539
544
 
540
545
  ### Context Budget Guard
541
546
 
@@ -719,16 +724,16 @@ Swarm provides tools for managing generated skill lifecycles:
719
724
  <details>
720
725
  <summary><strong>Quality Gates (Technical Detail)</strong></summary>
721
726
 
722
- ### Built-in Tools
727
+ ### Built-in Tools and Hooks
723
728
 
724
- | Tool | What It Does |
729
+ | Surface | What It Does |
725
730
  |------|-------------|
726
- | syntax_check | Tree-sitter validation across 12 languages |
731
+ | syntax_check | Tree-sitter validation across the configured language grammar map |
727
732
  | placeholder_scan | Catches TODOs, FIXMEs, stubs, placeholder text |
728
733
  | sast_scan | Offline security analysis, 63+ rules, 9 languages |
729
734
  | sbom_generate | CycloneDX dependency tracking, 8 ecosystems |
730
735
  | build_check | Runs your project's native build/typecheck |
731
- | incremental_verify | Post-coder typecheck for TS/JS, Go, Rust, C# (v6.29.2) |
736
+ | incremental_verify | Post-coder hook for TS/JS, Go, Rust, Python, and C#; configured by `incremental_verify.*`, not invoked as a registered tool |
732
737
  | quality_budget | Enforces complexity, duplication, and test ratio limits |
733
738
  | pre_check_batch | Runs lint, secretscan, SAST, and quality budget in parallel (~15s vs ~60s sequential) |
734
739
  | phase_complete | Enforces phase completion, verifies required agents, requires a valid retrospective evidence bundle, logs events, and resets state; appends to `events.jsonl` with file locking |
@@ -742,7 +747,7 @@ Swarm provides tools for managing generated skill lifecycles:
742
747
  | symbols | Extract exported symbols from source files; supports `workspace` (boolean) and `name` (string) parameters for multi-file symbol search |
743
748
 
744
749
 
745
- All tools run locally. No Docker, no network calls, no external APIs.
750
+ Quality-gate surfaces run locally and require no Docker. Optional search-backed council features use external APIs only when explicitly enabled and configured.
746
751
 
747
752
  Optional enhancement: Semgrep (if on PATH).
748
753
 
@@ -873,19 +878,21 @@ Config file location: `~/.config/opencode/opencode-swarm.json` (global) or `.ope
873
878
  "knowledge": {
874
879
  "enabled": true,
875
880
  "swarm_max_entries": 100,
876
- "hive_max_entries": 1000,
877
- "auto_promote_days": 30,
881
+ "hive_max_entries": 200,
882
+ "auto_promote_days": 90,
878
883
  "max_inject_count": 5,
884
+ "inject_char_budget": 2000,
885
+ "max_lesson_display_chars": 120,
879
886
  "dedup_threshold": 0.6,
880
887
  "scope_filter": ["global"],
881
888
  "hive_enabled": true,
882
- "rejected_max_entries": 200,
889
+ "rejected_max_entries": 20,
883
890
  "validation_enabled": true,
884
- "evergreen_confidence": 0.8,
885
- "evergreen_utility": 0.5,
886
- "low_utility_threshold": 0.2,
891
+ "evergreen_confidence": 0.9,
892
+ "evergreen_utility": 0.8,
893
+ "low_utility_threshold": 0.3,
887
894
  "min_retrievals_for_utility": 3,
888
- "schema_version": "v6.17"
895
+ "schema_version": 1
889
896
  }
890
897
  }
891
898
  ```
@@ -1024,7 +1031,7 @@ Control how tool outputs are summarized for LLM context.
1024
1031
  | `/swarm preflight` | Run phase preflight checks |
1025
1032
  | `/swarm config doctor [--fix]` | Config validation with optional auto-fix |
1026
1033
  | `/swarm doctor tools` | Tool registration coherence and binary readiness check |
1027
- | `/swarm sync-plan` | Force plan.md regeneration from plan.json |
1034
+ | `/swarm sync-plan` | Force `plan.md` regeneration from the canonical plan ledger |
1028
1035
  | `/swarm specify [description]` | Generate or import a feature specification |
1029
1036
  | `/swarm clarify [topic]` | Clarify and refine an existing feature specification |
1030
1037
  | `/swarm analyze` | Analyze spec.md vs plan.md for requirement coverage gaps |
@@ -1040,9 +1047,15 @@ Control how tool outputs are summarized for LLM context.
1040
1047
  | `/swarm knowledge quarantine [id]` | Move a knowledge entry to quarantine |
1041
1048
  | `/swarm knowledge restore [id]` | Restore a quarantined knowledge entry |
1042
1049
  | `/swarm memory status` | Show memory provider and JSONL migration status |
1050
+ | `/swarm memory pending` | Show pending memory proposals and recent rejection reasons |
1051
+ | `/swarm memory recall-log` | Summarize memory recall usage |
1052
+ | `/swarm memory stale` | List expired, superseded, deleted, and low-utility memory records |
1053
+ | `/swarm memory compact` | Dry-run memory cleanup; pass `--confirm` to apply |
1043
1054
  | `/swarm memory export` | Export memory records and proposals to JSONL |
1055
+ | `/swarm memory evaluate` | Run memory recall evaluation fixtures |
1044
1056
  | `/swarm memory import` | Import legacy JSONL memory into SQLite |
1045
1057
  | `/swarm memory migrate` | Run the one-time JSONL to SQLite migration |
1058
+ | `/swarm concurrency <set|status|reset>` | Manage session-scoped runtime concurrency override |
1046
1059
  | `/swarm turbo` | Enable turbo mode for the current session (bypasses QA gates) |
1047
1060
  | `/swarm full-auto` | Toggle Full-Auto Mode for the current session [on|off] |
1048
1061
  | `/swarm checkpoint` | Save a git checkpoint for the current state |
@@ -1053,8 +1066,8 @@ Control how tool outputs are summarized for LLM context.
1053
1066
 
1054
1067
  ## Supported Languages
1055
1068
 
1056
- Full Tier-1 support: TypeScript, JavaScript, Python, Go, Rust
1057
- Tier-2 support: Java, Kotlin, C#, C/C++, Swift
1069
+ Full Tier-1 support: TypeScript, JavaScript, Python, Go, Rust
1070
+ Tier-2 support: Java, Kotlin, C#, C/C++, Swift
1058
1071
  Tier-3 support: Dart, Ruby, PHP/Laravel
1059
1072
 
1060
1073
  All binaries optional. Missing tools produce soft warnings, never hard-fail.
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.52.1",
55
+ version: "7.52.3",
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",
@@ -17275,7 +17275,7 @@ function getCanonicalAgentRole(agentName, generatedAgentNames) {
17275
17275
  function stripKnownSwarmPrefix(agentName) {
17276
17276
  return getCanonicalAgentRole(agentName);
17277
17277
  }
17278
- var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
17278
+ var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
17279
17279
  var init_schema = __esm(() => {
17280
17280
  init_zod();
17281
17281
  init_constants();
@@ -17832,6 +17832,10 @@ var init_schema = __esm(() => {
17832
17832
  "Task"
17833
17833
  ])
17834
17834
  });
17835
+ SkillPropagationConfigSchema = exports_external.object({
17836
+ enabled: exports_external.boolean().default(true),
17837
+ enforce: exports_external.boolean().default(false)
17838
+ });
17835
17839
  SkillImproverConfigSchema = exports_external.object({
17836
17840
  enabled: exports_external.boolean().default(false),
17837
17841
  model: exports_external.string().nullable().default(null),
@@ -18023,6 +18027,7 @@ var init_schema = __esm(() => {
18023
18027
  curator: CuratorConfigSchema.optional(),
18024
18028
  architectural_supervision: ArchitecturalSupervisionConfigSchema.optional(),
18025
18029
  knowledge_application: KnowledgeApplicationConfigSchema.optional(),
18030
+ skillPropagation: SkillPropagationConfigSchema.optional(),
18026
18031
  skill_improver: SkillImproverConfigSchema.optional(),
18027
18032
  spec_writer: SpecWriterConfigSchema.optional(),
18028
18033
  tool_output: exports_external.object({
@@ -37092,6 +37097,57 @@ function readSkillUsageEntries(directory, options) {
37092
37097
  return true;
37093
37098
  });
37094
37099
  }
37100
+ function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
37101
+ const resolved = resolveLogPath(directory);
37102
+ if (!_internals10.existsSync(resolved)) {
37103
+ return { pruned: 0, remaining: 0 };
37104
+ }
37105
+ const allEntries = readSkillUsageEntries(directory);
37106
+ if (allEntries.length === 0) {
37107
+ return { pruned: 0, remaining: 0 };
37108
+ }
37109
+ const groups = new Map;
37110
+ for (const entry of allEntries) {
37111
+ const list = groups.get(entry.skillPath);
37112
+ if (list)
37113
+ list.push(entry);
37114
+ else
37115
+ groups.set(entry.skillPath, [entry]);
37116
+ }
37117
+ let pruned = 0;
37118
+ const surviving = [];
37119
+ groups.forEach((entries) => {
37120
+ if (entries.length <= maxEntriesPerSkill) {
37121
+ surviving.push(...entries);
37122
+ return;
37123
+ }
37124
+ entries.sort((a, b) => b.timestamp > a.timestamp ? 1 : b.timestamp < a.timestamp ? -1 : 0);
37125
+ const kept = entries.slice(0, maxEntriesPerSkill);
37126
+ pruned += entries.length - kept.length;
37127
+ surviving.push(...kept);
37128
+ });
37129
+ if (pruned === 0) {
37130
+ return { pruned: 0, remaining: allEntries.length };
37131
+ }
37132
+ const dir = path14.dirname(resolved);
37133
+ const tmpPath = path14.join(dir, `skill-usage-${Date.now()}.tmp`);
37134
+ const content = surviving.map((e) => JSON.stringify(e)).join(`
37135
+ `).concat(`
37136
+ `);
37137
+ try {
37138
+ _internals10.writeFileSync(tmpPath, content, "utf-8");
37139
+ _internals10.renameSync(tmpPath, resolved);
37140
+ } catch (writeErr) {
37141
+ const msg = writeErr instanceof Error ? writeErr.message : String(writeErr);
37142
+ try {
37143
+ if (_internals10.existsSync(tmpPath)) {
37144
+ _internals10.writeFileSync(tmpPath, "", "utf-8");
37145
+ }
37146
+ } catch {}
37147
+ return { pruned: 0, remaining: allEntries.length, error: msg };
37148
+ }
37149
+ return { pruned, remaining: surviving.length };
37150
+ }
37095
37151
  async function resolveSourceKnowledgeIds(directory, skillPath) {
37096
37152
  try {
37097
37153
  let cleanPath = skillPath;
@@ -37203,7 +37259,7 @@ async function applySkillUsageFeedback(directory, options) {
37203
37259
  }
37204
37260
  return { processed, bumps };
37205
37261
  }
37206
- var _internals10, TAIL_BYTES_DEFAULT, COMPLIANCE_BOOST = 0.05, VIOLATION_DECAY = 0.1;
37262
+ var _internals10, TAIL_BYTES_DEFAULT, SKILL_USAGE_LOG_ROTATE_BYTES, COMPLIANCE_BOOST = 0.05, VIOLATION_DECAY = 0.1;
37207
37263
  var init_skill_usage_log = __esm(() => {
37208
37264
  init_knowledge_store();
37209
37265
  init_utils2();
@@ -37219,11 +37275,13 @@ var init_skill_usage_log = __esm(() => {
37219
37275
  openSync: fs7.openSync.bind(fs7),
37220
37276
  readSync: fs7.readSync.bind(fs7),
37221
37277
  closeSync: fs7.closeSync.bind(fs7),
37278
+ pruneSkillUsageLog,
37222
37279
  resolveSourceKnowledgeIds,
37223
37280
  applySkillUsageFeedback,
37224
37281
  parseGeneratedFromKnowledge
37225
37282
  };
37226
37283
  TAIL_BYTES_DEFAULT = 64 * 1024;
37284
+ SKILL_USAGE_LOG_ROTATE_BYTES = 1024 * 1024;
37227
37285
  });
37228
37286
 
37229
37287
  // src/hooks/curator.ts
@@ -18,7 +18,7 @@ export declare const _internals: {
18
18
  };
19
19
  export type { MigrationStatus, Phase, PhaseStatus, Plan, Task, TaskSize, TaskStatus, } from './plan-schema';
20
20
  export { MigrationStatusSchema, PhaseSchema, PhaseStatusSchema, PlanSchema, TaskSchema, TaskSizeSchema, TaskStatusSchema, } from './plan-schema';
21
- export type { AgentOverrideConfig, AutomationCapabilities, AutomationConfig, AutomationMode, LeanTurboConfig, MemoryConfig, PhaseCompleteConfig, PipelineConfig, PluginConfig, SwarmConfig, TurboConfig, } from './schema';
22
- export { AgentOverrideConfigSchema, AutomationCapabilitiesSchema, AutomationConfigSchema, AutomationModeSchema, LeanTurboConfigSchema, MemoryConfigSchema, PhaseCompleteConfigSchema, PipelineConfigSchema, PluginConfigSchema, SwarmConfigSchema, TurboConfigSchema, } from './schema';
21
+ export type { AgentOverrideConfig, AutomationCapabilities, AutomationConfig, AutomationMode, LeanTurboConfig, MemoryConfig, PhaseCompleteConfig, PipelineConfig, PluginConfig, SkillPropagationConfig, SwarmConfig, TurboConfig, } from './schema';
22
+ export { AgentOverrideConfigSchema, AutomationCapabilitiesSchema, AutomationConfigSchema, AutomationModeSchema, LeanTurboConfigSchema, MemoryConfigSchema, PhaseCompleteConfigSchema, PipelineConfigSchema, PluginConfigSchema, SkillPropagationConfigSchema, SwarmConfigSchema, TurboConfigSchema, } from './schema';
23
23
  export type { DeltaSpec, Obligation, SpecDelta, SpecRequirement, SpecScenario, SpecSection, SwarmSpec, } from './spec-schema';
24
24
  export { DeltaSpecSchema, ObligationSchema, SpecDeltaSchema, SpecRequirementSchema, SpecScenarioSchema, SpecSectionSchema, SwarmSpecSchema, validateSpecContent, } from './spec-schema';
@@ -611,6 +611,11 @@ export declare const KnowledgeApplicationConfigSchema: z.ZodObject<{
611
611
  high_risk_tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
612
612
  }, z.core.$strip>;
613
613
  export type KnowledgeApplicationConfig = z.infer<typeof KnowledgeApplicationConfigSchema>;
614
+ export declare const SkillPropagationConfigSchema: z.ZodObject<{
615
+ enabled: z.ZodDefault<z.ZodBoolean>;
616
+ enforce: z.ZodDefault<z.ZodBoolean>;
617
+ }, z.core.$strip>;
618
+ export type SkillPropagationConfig = z.infer<typeof SkillPropagationConfigSchema>;
614
619
  export declare const SkillImproverConfigSchema: z.ZodObject<{
615
620
  enabled: z.ZodDefault<z.ZodBoolean>;
616
621
  model: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -1309,6 +1314,10 @@ export declare const PluginConfigSchema: z.ZodObject<{
1309
1314
  require_skill_refs: z.ZodDefault<z.ZodBoolean>;
1310
1315
  high_risk_tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
1311
1316
  }, z.core.$strip>>;
1317
+ skillPropagation: z.ZodOptional<z.ZodObject<{
1318
+ enabled: z.ZodDefault<z.ZodBoolean>;
1319
+ enforce: z.ZodDefault<z.ZodBoolean>;
1320
+ }, z.core.$strip>>;
1312
1321
  skill_improver: z.ZodOptional<z.ZodObject<{
1313
1322
  enabled: z.ZodDefault<z.ZodBoolean>;
1314
1323
  model: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -29,10 +29,8 @@ export declare function loadRoutingSkills(directory: string, targetAgent: string
29
29
  /** Agents that should receive skill context in delegations. */
30
30
  export declare const SKILL_CAPABLE_AGENTS: Set<string>;
31
31
  /**
32
- * Maximum number of session-scoped skill-usage entries to process for
33
- * skill scoring. When a session accumulates more entries than this
34
- * limit, scoring is skipped to prevent unbounded file reads from
35
- * stalling every delegated Task call.
32
+ * Maximum number of session-scoped skill-usage tail entries to process for
33
+ * skill scoring. This applies to the bounded tail-read window only.
36
34
  */
37
35
  export declare const MAX_SCORING_SESSION_ENTRIES = 500;
38
36
  export interface SkillGateInput {
@@ -66,6 +66,7 @@ export declare const _internals: {
66
66
  openSync: typeof fs.openSync;
67
67
  readSync: typeof fs.readSync;
68
68
  closeSync: typeof fs.closeSync;
69
+ pruneSkillUsageLog: typeof pruneSkillUsageLog;
69
70
  resolveSourceKnowledgeIds: typeof resolveSourceKnowledgeIds;
70
71
  applySkillUsageFeedback: typeof applySkillUsageFeedback;
71
72
  parseGeneratedFromKnowledge: typeof parseGeneratedFromKnowledge;
@@ -86,6 +87,7 @@ export declare function appendSkillUsageEntry(directory: string, entry: Omit<Ski
86
87
  export declare function readSkillUsageEntries(directory: string, options?: SkillUsageFilterOptions): SkillUsageEntry[];
87
88
  /** Default maximum bytes to read from the end of the log file. */
88
89
  export declare const TAIL_BYTES_DEFAULT: number;
90
+ export declare const MAX_TAIL_BYTES: number;
89
91
  /**
90
92
  * Read the last `maxBytes` of the skill-usage JSONL log and parse matching
91
93
  * entries. Much faster than `readSkillUsageEntries` for large logs because
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.52.1",
72
+ version: "7.52.3",
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",
@@ -15207,7 +15207,7 @@ function resolveGuardrailsConfig(config2, agentName) {
15207
15207
  };
15208
15208
  return resolved;
15209
15209
  }
15210
- var _internals, SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
15210
+ var _internals, SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
15211
15211
  var init_schema = __esm(() => {
15212
15212
  init_zod();
15213
15213
  init_constants();
@@ -15769,6 +15769,10 @@ var init_schema = __esm(() => {
15769
15769
  "Task"
15770
15770
  ])
15771
15771
  });
15772
+ SkillPropagationConfigSchema = exports_external.object({
15773
+ enabled: exports_external.boolean().default(true),
15774
+ enforce: exports_external.boolean().default(false)
15775
+ });
15772
15776
  SkillImproverConfigSchema = exports_external.object({
15773
15777
  enabled: exports_external.boolean().default(false),
15774
15778
  model: exports_external.string().nullable().default(null),
@@ -15960,6 +15964,7 @@ var init_schema = __esm(() => {
15960
15964
  curator: CuratorConfigSchema.optional(),
15961
15965
  architectural_supervision: ArchitecturalSupervisionConfigSchema.optional(),
15962
15966
  knowledge_application: KnowledgeApplicationConfigSchema.optional(),
15967
+ skillPropagation: SkillPropagationConfigSchema.optional(),
15963
15968
  skill_improver: SkillImproverConfigSchema.optional(),
15964
15969
  spec_writer: SpecWriterConfigSchema.optional(),
15965
15970
  tool_output: exports_external.object({
@@ -16520,6 +16525,7 @@ __export(exports_config, {
16520
16525
  SpecScenarioSchema: () => SpecScenarioSchema,
16521
16526
  SpecRequirementSchema: () => SpecRequirementSchema,
16522
16527
  SpecDeltaSchema: () => SpecDeltaSchema,
16528
+ SkillPropagationConfigSchema: () => SkillPropagationConfigSchema,
16523
16529
  ReviewEvidenceSchema: () => ReviewEvidenceSchema,
16524
16530
  QA_AGENTS: () => QA_AGENTS,
16525
16531
  PluginConfigSchema: () => PluginConfigSchema,
@@ -41522,6 +41528,7 @@ __export(exports_state, {
41522
41528
  endAgentSession: () => endAgentSession,
41523
41529
  defaultRunContext: () => defaultRunContext,
41524
41530
  clearCriticalShownIds: () => clearCriticalShownIds,
41531
+ canAdvanceTaskState: () => canAdvanceTaskState,
41525
41532
  buildRehydrationCache: () => buildRehydrationCache,
41526
41533
  beginInvocation: () => beginInvocation,
41527
41534
  applyRehydrationCache: () => applyRehydrationCache,
@@ -41900,14 +41907,6 @@ function advanceTaskState(session, taskId, newState, options, councilConfig) {
41900
41907
  if (!session || !(session.taskWorkflowStates instanceof Map)) {
41901
41908
  throw new Error("INVALID_SESSION: session.taskWorkflowStates must be a Map instance");
41902
41909
  }
41903
- const STATE_ORDER = [
41904
- "idle",
41905
- "coder_delegated",
41906
- "pre_check_passed",
41907
- "reviewer_run",
41908
- "tests_run",
41909
- "complete"
41910
- ];
41911
41910
  const current = session.taskWorkflowStates.get(taskId) ?? "idle";
41912
41911
  const currentIndex = STATE_ORDER.indexOf(current);
41913
41912
  const newIndex = STATE_ORDER.indexOf(newState);
@@ -41924,10 +41923,33 @@ function advanceTaskState(session, taskId, newState, options, councilConfig) {
41924
41923
  }
41925
41924
  }
41926
41925
  session.taskWorkflowStates.set(taskId, newState);
41926
+ if (newState === "complete") {
41927
+ session.stageBCompletion?.delete(taskId);
41928
+ }
41927
41929
  if (options?.emitTelemetry !== false) {
41928
41930
  telemetry.taskStateChanged(options?.telemetrySessionId ?? session.agentName, taskId, newState, current);
41929
41931
  }
41930
41932
  }
41933
+ function canAdvanceTaskState(session, taskId, newState, councilConfig) {
41934
+ if (!isValidTaskId2(taskId))
41935
+ return false;
41936
+ if (!session || !(session.taskWorkflowStates instanceof Map))
41937
+ return false;
41938
+ const current = session.taskWorkflowStates.get(taskId) ?? "idle";
41939
+ const currentIndex = STATE_ORDER.indexOf(current);
41940
+ const newIndex = STATE_ORDER.indexOf(newState);
41941
+ if (newIndex <= currentIndex)
41942
+ return false;
41943
+ if (newState === "complete" && current !== "tests_run") {
41944
+ const councilEntry = session.taskCouncilApproved?.get(taskId);
41945
+ const effectiveMinimum = councilConfig?.requireAllMembers ? 5 : councilConfig?.minimumMembers ?? 3;
41946
+ const councilApproved = councilEntry?.verdict === "APPROVE" && (councilEntry.quorumSize ?? 0) >= effectiveMinimum;
41947
+ const pastPreCheck = currentIndex >= STATE_ORDER.indexOf("pre_check_passed");
41948
+ if (!councilApproved || !pastPreCheck)
41949
+ return false;
41950
+ }
41951
+ return true;
41952
+ }
41931
41953
  async function advanceTaskStateAndPersist(session, taskId, newState, directory, options, councilConfig) {
41932
41954
  advanceTaskState(session, taskId, newState, options, councilConfig);
41933
41955
  if (newState !== "coder_delegated" && newState !== "complete") {
@@ -42098,14 +42120,6 @@ function applyRehydrationCache(session) {
42098
42120
  session.taskCouncilApproved = new Map;
42099
42121
  }
42100
42122
  const { planTaskStates, evidenceMap } = _rehydrationCache;
42101
- const STATE_ORDER = [
42102
- "idle",
42103
- "coder_delegated",
42104
- "pre_check_passed",
42105
- "reviewer_run",
42106
- "tests_run",
42107
- "complete"
42108
- ];
42109
42123
  for (const [taskId, planState] of planTaskStates) {
42110
42124
  const existingState = session.taskWorkflowStates.get(taskId);
42111
42125
  const evidence = evidenceMap.get(taskId);
@@ -42241,7 +42255,7 @@ function addKnowledgeAckDedup(key) {
42241
42255
  set2.delete(oldest);
42242
42256
  }
42243
42257
  }
42244
- var _rehydrationCache = null, _councilDisagreementWarned, _toolAggregates, defaultRunContext, _runContexts, swarmState, MAX_TRACKED_CRITICAL_SHOWN = 500, MAX_TRACKED_KNOWLEDGE_ACKS = 5000, _internals16;
42258
+ var _rehydrationCache = null, _councilDisagreementWarned, STATE_ORDER, _toolAggregates, defaultRunContext, _runContexts, swarmState, MAX_TRACKED_CRITICAL_SHOWN = 500, MAX_TRACKED_KNOWLEDGE_ACKS = 5000, _internals16;
42245
42259
  var init_state = __esm(() => {
42246
42260
  init_constants();
42247
42261
  init_plan_schema();
@@ -42252,6 +42266,14 @@ var init_state = __esm(() => {
42252
42266
  init_telemetry();
42253
42267
  init_logger();
42254
42268
  _councilDisagreementWarned = new Set;
42269
+ STATE_ORDER = [
42270
+ "idle",
42271
+ "coder_delegated",
42272
+ "pre_check_passed",
42273
+ "reviewer_run",
42274
+ "tests_run",
42275
+ "complete"
42276
+ ];
42255
42277
  _toolAggregates = new Map;
42256
42278
  defaultRunContext = new AgentRunContext("default", _toolAggregates);
42257
42279
  _runContexts = new Map;
@@ -58222,6 +58244,12 @@ function appendSkillUsageEntry(directory, entry) {
58222
58244
  };
58223
58245
  _internals19.appendFileSync(resolved, `${JSON.stringify(fullEntry)}
58224
58246
  `, "utf-8");
58247
+ try {
58248
+ const stat3 = _internals19.statSync(resolved);
58249
+ if (stat3.size > SKILL_USAGE_LOG_ROTATE_BYTES) {
58250
+ _internals19.pruneSkillUsageLog(directory, SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL);
58251
+ }
58252
+ } catch {}
58225
58253
  }
58226
58254
  function readSkillUsageEntries(directory, options) {
58227
58255
  const resolved = resolveLogPath(directory);
@@ -58268,8 +58296,10 @@ function readSkillUsageEntriesTail(directory, filters, maxBytes = TAIL_BYTES_DEF
58268
58296
  if (!_internals19.existsSync(logPath))
58269
58297
  return [];
58270
58298
  try {
58299
+ const normalizedMaxBytes = Number.isFinite(maxBytes) ? maxBytes : TAIL_BYTES_DEFAULT;
58300
+ const boundedMaxBytes = Math.min(Math.max(1, normalizedMaxBytes), MAX_TAIL_BYTES);
58271
58301
  const stat3 = _internals19.statSync(logPath);
58272
- const start2 = Math.max(0, stat3.size - maxBytes);
58302
+ const start2 = Math.max(0, stat3.size - boundedMaxBytes);
58273
58303
  const fd = _internals19.openSync(logPath, "r");
58274
58304
  try {
58275
58305
  const readLen = stat3.size - start2;
@@ -58469,7 +58499,7 @@ async function applySkillUsageFeedback(directory, options) {
58469
58499
  }
58470
58500
  return { processed, bumps };
58471
58501
  }
58472
- var _internals19, TAIL_BYTES_DEFAULT, COMPLIANCE_BOOST = 0.05, VIOLATION_DECAY = 0.1;
58502
+ var _internals19, TAIL_BYTES_DEFAULT, MAX_TAIL_BYTES, SKILL_USAGE_LOG_ROTATE_BYTES, SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL = 500, COMPLIANCE_BOOST = 0.05, VIOLATION_DECAY = 0.1;
58473
58503
  var init_skill_usage_log = __esm(() => {
58474
58504
  init_knowledge_store();
58475
58505
  init_utils2();
@@ -58485,11 +58515,14 @@ var init_skill_usage_log = __esm(() => {
58485
58515
  openSync: fs15.openSync.bind(fs15),
58486
58516
  readSync: fs15.readSync.bind(fs15),
58487
58517
  closeSync: fs15.closeSync.bind(fs15),
58518
+ pruneSkillUsageLog,
58488
58519
  resolveSourceKnowledgeIds,
58489
58520
  applySkillUsageFeedback,
58490
58521
  parseGeneratedFromKnowledge
58491
58522
  };
58492
58523
  TAIL_BYTES_DEFAULT = 64 * 1024;
58524
+ MAX_TAIL_BYTES = TAIL_BYTES_DEFAULT;
58525
+ SKILL_USAGE_LOG_ROTATE_BYTES = 1024 * 1024;
58493
58526
  });
58494
58527
 
58495
58528
  // src/hooks/curator.ts
@@ -105511,7 +105544,7 @@ async function skillPropagationGateBefore(directory, input, config3) {
105511
105544
  });
105512
105545
  if (sessionEntries.length > _internals58.MAX_SCORING_SESSION_ENTRIES) {
105513
105546
  scoringSkipped = true;
105514
- warn(`[skill-propagation-gate] skipping scoring — session has ${sessionEntries.length} entries (limit: ${_internals58.MAX_SCORING_SESSION_ENTRIES})`);
105547
+ warn(`[skill-propagation-gate] skipping scoring — tail window has ${sessionEntries.length} session entries (limit: ${_internals58.MAX_SCORING_SESSION_ENTRIES})`);
105515
105548
  } else {
105516
105549
  const prompt = typeof input.args?.prompt === "string" ? String(input.args.prompt) : "";
105517
105550
  scored = availableSkills.map((skillPath) => {
@@ -128556,6 +128589,12 @@ function recoverTaskStateFromDelegations(taskId, directory) {
128556
128589
  advanceTaskState(session, taskId, "coder_delegated");
128557
128590
  } catch {}
128558
128591
  }
128592
+ if (hasReviewer) {
128593
+ recordStageBCompletion(session, taskId, "reviewer");
128594
+ }
128595
+ if (hasTestEngineer) {
128596
+ recordStageBCompletion(session, taskId, "test_engineer");
128597
+ }
128559
128598
  if (hasReviewer) {
128560
128599
  const stateNow = getTaskState(session, taskId);
128561
128600
  if (stateNow === "coder_delegated" || stateNow === "pre_check_passed") {
@@ -130120,6 +130159,7 @@ async function initializeOpenCodeSwarm(ctx) {
130120
130159
  const summaryConfig = SummaryConfigSchema.parse(config3.summaries ?? {});
130121
130160
  const toolSummarizerHook = createToolSummarizerHook(summaryConfig, ctx.directory);
130122
130161
  const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
130162
+ const skillPropagationConfig = SkillPropagationConfigSchema.parse(config3.skillPropagation ?? {});
130123
130163
  const knowledgeCuratorHook = knowledgeConfig.enabled ? createKnowledgeCuratorHook(ctx.directory, knowledgeConfig) : undefined;
130124
130164
  const hivePromoterHook = knowledgeConfig.enabled && knowledgeConfig.hive_enabled ? createHivePromoterHook(ctx.directory, knowledgeConfig) : undefined;
130125
130165
  const knowledgeInjectorHook = knowledgeConfig.enabled ? createKnowledgeInjectorHook(ctx.directory, knowledgeConfig) : undefined;
@@ -130576,6 +130616,9 @@ async function initializeOpenCodeSwarm(ctx) {
130576
130616
  },
130577
130617
  (input, output) => {
130578
130618
  try {
130619
+ if (!skillPropagationConfig.enabled) {
130620
+ return Promise.resolve();
130621
+ }
130579
130622
  const p = input;
130580
130623
  return skillPropagationTransformScan(ctx.directory, output, p.sessionID);
130581
130624
  } catch {
@@ -130656,12 +130699,12 @@ async function initializeOpenCodeSwarm(ctx) {
130656
130699
  agent: input.agent,
130657
130700
  sessionID: input.sessionID
130658
130701
  }, KnowledgeApplicationConfigSchema.parse(config3.knowledge_application ?? {}));
130659
- const skillResult = await skillPropagationGateBefore(ctx.directory, {
130702
+ const skillResult = skillPropagationConfig.enabled ? await skillPropagationGateBefore(ctx.directory, {
130660
130703
  tool: input.tool,
130661
130704
  agent: input.agent,
130662
130705
  sessionID: input.sessionID,
130663
130706
  args: input.args
130664
- }, { enabled: true });
130707
+ }, skillPropagationConfig) : { blocked: false, reason: null, recommendedSkills: undefined };
130665
130708
  if (skillResult.blocked) {
130666
130709
  throw new Error(skillResult.reason ?? "Blocked by skill propagation gate");
130667
130710
  }
package/dist/state.d.ts CHANGED
@@ -425,6 +425,22 @@ export declare function advanceTaskState(session: AgentSessionState, taskId: str
425
425
  minimumMembers?: number;
426
426
  requireAllMembers?: boolean;
427
427
  }): void;
428
+ /**
429
+ * Returns true iff calling `advanceTaskState(session, taskId, newState)` would
430
+ * succeed without throwing. Use this predicate to guard call sites that cannot
431
+ * catch `INVALID_TASK_STATE_TRANSITION` as a control-flow mechanism.
432
+ *
433
+ * Does NOT perform side effects or emit telemetry.
434
+ *
435
+ * @param session - The agent session state
436
+ * @param taskId - The task identifier
437
+ * @param newState - The requested new state
438
+ * @param councilConfig - Optional council quorum config (required when newState='complete')
439
+ */
440
+ export declare function canAdvanceTaskState(session: AgentSessionState, taskId: string, newState: TaskWorkflowState, councilConfig?: {
441
+ minimumMembers?: number;
442
+ requireAllMembers?: boolean;
443
+ }): boolean;
428
444
  /**
429
445
  * Advance the per-task workflow state machine AND persist the corresponding
430
446
  * plan.json status at meaningful workflow boundaries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.52.1",
3
+ "version": "7.52.3",
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",