opencode-swarm 7.18.0 → 7.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ var package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "opencode-swarm",
36
- version: "7.18.0",
36
+ version: "7.18.2",
37
37
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
38
38
  main: "dist/index.js",
39
39
  types: "dist/index.d.ts",
@@ -253,7 +253,7 @@ function isLowCapabilityModel(modelId) {
253
253
  const lower = (modelId || "").toLowerCase();
254
254
  return LOW_CAPABILITY_MODELS.some((substr) => lower.includes(substr));
255
255
  }
256
- var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, WRITE_TOOL_NAMES, TOOL_DESCRIPTIONS, DEFAULT_MODELS, DEFAULT_SCORING_CONFIG, LOW_CAPABILITY_MODELS, TURBO_MODE_BANNER = `## \uD83D\uDE80 TURBO MODE ACTIVE
256
+ var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, WRITE_TOOL_NAMES, TOOL_DESCRIPTIONS, DEFAULT_MODELS, DEFAULT_AGENT_CONFIGS, DEFAULT_SCORING_CONFIG, LOW_CAPABILITY_MODELS, TURBO_MODE_BANNER = `## \uD83D\uDE80 TURBO MODE ACTIVE
257
257
 
258
258
  **Speed optimization enabled for this session.**
259
259
 
@@ -687,8 +687,7 @@ var init_constants = __esm(() => {
687
687
  "skill_improve",
688
688
  "search",
689
689
  "doc_scan",
690
- "doc_extract",
691
- "web_search"
690
+ "doc_extract"
692
691
  ],
693
692
  spec_writer: [
694
693
  "search",
@@ -800,21 +799,87 @@ var init_constants = __esm(() => {
800
799
  explorer: "opencode/big-pickle",
801
800
  coder: "opencode/minimax-m2.5-free",
802
801
  reviewer: "opencode/big-pickle",
803
- test_engineer: "opencode/gpt-5-nano",
802
+ test_engineer: "opencode/big-pickle",
804
803
  sme: "opencode/big-pickle",
805
804
  critic: "opencode/big-pickle",
806
- critic_sounding_board: "opencode/gpt-5-nano",
807
- critic_drift_verifier: "opencode/gpt-5-nano",
808
- critic_hallucination_verifier: "opencode/gpt-5-nano",
809
- critic_oversight: "opencode/gpt-5-nano",
805
+ critic_sounding_board: "opencode/big-pickle",
806
+ critic_drift_verifier: "opencode/big-pickle",
807
+ critic_hallucination_verifier: "opencode/big-pickle",
808
+ critic_oversight: "opencode/big-pickle",
810
809
  docs: "opencode/big-pickle",
811
810
  designer: "opencode/big-pickle",
812
- curator_init: "opencode/gpt-5-nano",
813
- curator_phase: "opencode/gpt-5-nano",
811
+ curator_init: "opencode/big-pickle",
812
+ curator_phase: "opencode/big-pickle",
814
813
  skill_improver: "opencode/big-pickle",
815
814
  spec_writer: "opencode/big-pickle",
816
815
  default: "opencode/big-pickle"
817
816
  };
817
+ DEFAULT_AGENT_CONFIGS = {
818
+ coder: {
819
+ model: "opencode/minimax-m2.5-free",
820
+ fallback_models: ["opencode/big-pickle"]
821
+ },
822
+ reviewer: {
823
+ model: "opencode/big-pickle",
824
+ fallback_models: ["opencode/minimax-m2.5-free"]
825
+ },
826
+ test_engineer: {
827
+ model: "opencode/big-pickle",
828
+ fallback_models: ["opencode/minimax-m2.5-free"]
829
+ },
830
+ explorer: {
831
+ model: "opencode/big-pickle",
832
+ fallback_models: ["opencode/minimax-m2.5-free"]
833
+ },
834
+ sme: {
835
+ model: "opencode/big-pickle",
836
+ fallback_models: ["opencode/minimax-m2.5-free"]
837
+ },
838
+ critic: {
839
+ model: "opencode/big-pickle",
840
+ fallback_models: ["opencode/minimax-m2.5-free"]
841
+ },
842
+ docs: {
843
+ model: "opencode/big-pickle",
844
+ fallback_models: ["opencode/minimax-m2.5-free"]
845
+ },
846
+ designer: {
847
+ model: "opencode/big-pickle",
848
+ fallback_models: ["opencode/minimax-m2.5-free"]
849
+ },
850
+ critic_sounding_board: {
851
+ model: "opencode/big-pickle",
852
+ fallback_models: ["opencode/minimax-m2.5-free"]
853
+ },
854
+ critic_drift_verifier: {
855
+ model: "opencode/big-pickle",
856
+ fallback_models: ["opencode/minimax-m2.5-free"]
857
+ },
858
+ critic_hallucination_verifier: {
859
+ model: "opencode/big-pickle",
860
+ fallback_models: ["opencode/minimax-m2.5-free"]
861
+ },
862
+ critic_oversight: {
863
+ model: "opencode/big-pickle",
864
+ fallback_models: ["opencode/minimax-m2.5-free"]
865
+ },
866
+ curator_init: {
867
+ model: "opencode/big-pickle",
868
+ fallback_models: ["opencode/minimax-m2.5-free"]
869
+ },
870
+ curator_phase: {
871
+ model: "opencode/big-pickle",
872
+ fallback_models: ["opencode/minimax-m2.5-free"]
873
+ },
874
+ skill_improver: {
875
+ model: "opencode/big-pickle",
876
+ fallback_models: ["opencode/minimax-m2.5-free"]
877
+ },
878
+ spec_writer: {
879
+ model: "opencode/big-pickle",
880
+ fallback_models: ["opencode/minimax-m2.5-free"]
881
+ }
882
+ };
818
883
  DEFAULT_SCORING_CONFIG = {
819
884
  enabled: false,
820
885
  max_candidates: 100,
@@ -18532,7 +18597,8 @@ function handleAgentsCommand(agents, guardrails) {
18532
18597
  if (hasUnregistered) {
18533
18598
  lines.push("", "### Unregistered Subagents");
18534
18599
  for (const name2 of unregistered) {
18535
- lines.push(`- **${name2}** (requires configuration)`);
18600
+ const hint = UNREGISTERED_AGENT_HINTS[name2] ?? "requires configuration";
18601
+ lines.push(`- **${name2}** (${hint})`);
18536
18602
  }
18537
18603
  }
18538
18604
  if (guardrails?.profiles && Object.keys(guardrails.profiles).length > 0) {
@@ -18561,9 +18627,18 @@ function handleAgentsCommand(agents, guardrails) {
18561
18627
  return lines.join(`
18562
18628
  `);
18563
18629
  }
18630
+ var UNREGISTERED_AGENT_HINTS;
18564
18631
  var init_agents = __esm(() => {
18565
18632
  init_constants();
18566
18633
  init_schema();
18634
+ UNREGISTERED_AGENT_HINTS = {
18635
+ designer: "enable ui_review.enabled",
18636
+ council_generalist: "enable council.general.enabled",
18637
+ council_skeptic: "enable council.general.enabled",
18638
+ council_domain_expert: "enable council.general.enabled",
18639
+ skill_improver: "registered by default unless agents.skill_improver.disabled is true",
18640
+ spec_writer: "registered by default unless agents.spec_writer.disabled is true"
18641
+ };
18567
18642
  });
18568
18643
 
18569
18644
  // src/commands/analyze.ts
@@ -26355,9 +26430,9 @@ async function getEvidenceTaskId(session, directory) {
26355
26430
  const primary = session.currentTaskId ?? session.lastCoderDelegationTaskId;
26356
26431
  if (primary)
26357
26432
  return primary;
26358
- if (session.taskWorkflowStates && session.taskWorkflowStates.size > 0) {
26359
- return session.taskWorkflowStates.keys().next().value ?? null;
26360
- }
26433
+ const onlyTaskId = getOnlyWorkflowTaskId(session);
26434
+ if (onlyTaskId)
26435
+ return onlyTaskId;
26361
26436
  try {
26362
26437
  if (typeof directory !== "string" || directory.length === 0) {
26363
26438
  return null;
@@ -26707,18 +26782,25 @@ function createDelegationGateHook(config2, directory) {
26707
26782
  const explicitEvidenceTaskId = extractTaskIdFromTaskArgs(mergedTaskArgs);
26708
26783
  const stageBEvidenceTaskId = gateForEvidence === "reviewer" || gateForEvidence === "test_engineer" || gateForEvidence === "adversarial_test_engineer" ? resolveScopedStageBTaskId(session, mergedTaskArgs) : null;
26709
26784
  const evidenceTaskId = stageBEvidenceTaskId ?? explicitEvidenceTaskId ?? await getEvidenceTaskId(session, directory);
26785
+ const gateAgents = [
26786
+ "reviewer",
26787
+ "test_engineer",
26788
+ "docs",
26789
+ "designer",
26790
+ "critic",
26791
+ "explorer",
26792
+ "sme"
26793
+ ];
26794
+ if (evidenceTaskId === null && gateAgents.includes(targetAgentForEvidence)) {
26795
+ session.pendingAdvisoryMessages ??= [];
26796
+ if (!session.pendingAdvisoryMessages.some((m) => m.includes("evidence-task-id-unresolved"))) {
26797
+ session.pendingAdvisoryMessages.push(`[evidence-task-id-unresolved] Gate evidence has NOT been written for one or more recent gate-agent dispatches because the current task id is unresolved. Call update_task_status(<task_id>, 'in_progress') BEFORE dispatching gate agents (e.g. reviewer/test_engineer). Most recent affected agent: ${targetAgentForEvidence}.`);
26798
+ }
26799
+ console.warn(`[delegation-gate] evidence-task-id-unresolved sessionID=${input.sessionID} subagentType=${targetAgentForEvidence} reason=evidence-task-id-unresolved`);
26800
+ }
26710
26801
  if (evidenceTaskId && typeof directory === "string") {
26711
26802
  const turbo = hasActiveTurboMode(input.sessionID);
26712
26803
  const parallelRuntime = resolveStandardParallelizationConfig(config2, directory);
26713
- const gateAgents = [
26714
- "reviewer",
26715
- "test_engineer",
26716
- "docs",
26717
- "designer",
26718
- "critic",
26719
- "explorer",
26720
- "sme"
26721
- ];
26722
26804
  if (gateAgents.includes(targetAgentForEvidence)) {
26723
26805
  const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
26724
26806
  await recordGateEvidence2(directory, evidenceTaskId, gateForEvidence, input.sessionID, turbo, parallelRuntime.evidenceLockTimeoutMs);
@@ -26728,7 +26810,7 @@ function createDelegationGateHook(config2, directory) {
26728
26810
  }
26729
26811
  }
26730
26812
  } catch (err2) {
26731
- console.warn(`[delegation-gate] evidence recording failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
26813
+ console.warn(`[delegation-gate] evidence recording failed reason=evidence-write-failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
26732
26814
  }
26733
26815
  }
26734
26816
  if (storedArgs !== undefined) {
@@ -46818,12 +46900,31 @@ function parseArgs(args2) {
46818
46900
  }
46819
46901
  return out2;
46820
46902
  }
46821
- async function handleCouncilCommand(_directory, args2) {
46903
+ async function handleCouncilCommand(directory, args2) {
46822
46904
  const parsed = parseArgs(args2);
46823
46905
  const question = sanitizeQuestion(parsed.rest.join(" "));
46824
46906
  if (!question) {
46825
46907
  return USAGE;
46826
46908
  }
46909
+ const config3 = loadPluginConfig(directory);
46910
+ if (config3.council?.general?.enabled !== true) {
46911
+ return [
46912
+ "General Council is not enabled for this project.",
46913
+ "",
46914
+ "Enable it in `.opencode/opencode-swarm.json` or `~/.config/opencode/opencode-swarm.json`:",
46915
+ "",
46916
+ "```json",
46917
+ "{",
46918
+ ' "council": {',
46919
+ ' "general": { "enabled": true }',
46920
+ " }",
46921
+ "}",
46922
+ "```",
46923
+ "",
46924
+ "Then restart OpenCode and run `/swarm config doctor` before trying `/swarm council` again."
46925
+ ].join(`
46926
+ `);
46927
+ }
46827
46928
  const tokens = ["MODE: COUNCIL"];
46828
46929
  if (parsed.preset) {
46829
46930
  tokens.push(`preset=${parsed.preset}`);
@@ -46835,6 +46936,7 @@ async function handleCouncilCommand(_directory, args2) {
46835
46936
  }
46836
46937
  var MAX_QUESTION_LEN = 2000, USAGE;
46837
46938
  var init_council = __esm(() => {
46939
+ init_loader();
46838
46940
  USAGE = [
46839
46941
  "Usage: /swarm council <question> [--preset <name>] [--spec-review]",
46840
46942
  "",
@@ -48362,6 +48464,7 @@ __export(exports_config_doctor, {
48362
48464
  restoreFromBackup: () => restoreFromBackup,
48363
48465
  getConfigPaths: () => getConfigPaths,
48364
48466
  createConfigBackup: () => createConfigBackup,
48467
+ collectConfiguredModelRefs: () => collectConfiguredModelRefs,
48365
48468
  applySafeAutoFixes: () => applySafeAutoFixes
48366
48469
  });
48367
48470
  import * as crypto3 from "node:crypto";
@@ -48755,6 +48858,129 @@ function validateConfigKey(path31, value, _config) {
48755
48858
  }
48756
48859
  return findings;
48757
48860
  }
48861
+ function addConfiguredModel(refs, model, configPath) {
48862
+ if (typeof model !== "string")
48863
+ return;
48864
+ const trimmed = model.trim();
48865
+ if (!trimmed)
48866
+ return;
48867
+ const paths = refs.get(trimmed) ?? new Set;
48868
+ paths.add(configPath);
48869
+ refs.set(trimmed, paths);
48870
+ }
48871
+ function addConfiguredAgentModels(refs, agents, prefix) {
48872
+ if (!agents || typeof agents !== "object" || Array.isArray(agents))
48873
+ return;
48874
+ for (const [agentName, value] of Object.entries(agents)) {
48875
+ if (!value || typeof value !== "object" || Array.isArray(value))
48876
+ continue;
48877
+ const agent = value;
48878
+ addConfiguredModel(refs, agent.model, `${prefix}.${agentName}.model`);
48879
+ if (Array.isArray(agent.fallback_models)) {
48880
+ agent.fallback_models.forEach((model, index) => {
48881
+ addConfiguredModel(refs, model, `${prefix}.${agentName}.fallback_models[${index}]`);
48882
+ });
48883
+ }
48884
+ }
48885
+ }
48886
+ function collectConfiguredModelRefs(config3) {
48887
+ const refs = new Map;
48888
+ const rawConfig = config3;
48889
+ addConfiguredAgentModels(refs, rawConfig.agents, "agents");
48890
+ if (rawConfig.swarms && typeof rawConfig.swarms === "object" && !Array.isArray(rawConfig.swarms)) {
48891
+ for (const [swarmId, value] of Object.entries(rawConfig.swarms)) {
48892
+ if (!value || typeof value !== "object" || Array.isArray(value))
48893
+ continue;
48894
+ addConfiguredAgentModels(refs, value.agents, `swarms.${swarmId}.agents`);
48895
+ }
48896
+ }
48897
+ if (rawConfig.full_auto && typeof rawConfig.full_auto === "object" && !Array.isArray(rawConfig.full_auto)) {
48898
+ addConfiguredModel(refs, rawConfig.full_auto.critic_model, "full_auto.critic_model");
48899
+ }
48900
+ if (rawConfig.skill_improver && typeof rawConfig.skill_improver === "object" && !Array.isArray(rawConfig.skill_improver)) {
48901
+ const skillImprover = rawConfig.skill_improver;
48902
+ addConfiguredModel(refs, skillImprover.model, "skill_improver.model");
48903
+ if (Array.isArray(skillImprover.fallback_models)) {
48904
+ skillImprover.fallback_models.forEach((model, index) => {
48905
+ addConfiguredModel(refs, model, `skill_improver.fallback_models[${index}]`);
48906
+ });
48907
+ }
48908
+ }
48909
+ if (rawConfig.spec_writer && typeof rawConfig.spec_writer === "object" && !Array.isArray(rawConfig.spec_writer)) {
48910
+ const specWriter = rawConfig.spec_writer;
48911
+ addConfiguredModel(refs, specWriter.model, "spec_writer.model");
48912
+ if (Array.isArray(specWriter.fallback_models)) {
48913
+ specWriter.fallback_models.forEach((model, index) => {
48914
+ addConfiguredModel(refs, model, `spec_writer.fallback_models[${index}]`);
48915
+ });
48916
+ }
48917
+ }
48918
+ const council = rawConfig.council;
48919
+ const general = council && typeof council === "object" && !Array.isArray(council) ? council.general : undefined;
48920
+ if (general && typeof general === "object" && !Array.isArray(general)) {
48921
+ addConfiguredModel(refs, general.moderatorModel, "council.general.moderatorModel");
48922
+ if (Array.isArray(general.members)) {
48923
+ general.members.forEach((member, index) => {
48924
+ if (!member || typeof member !== "object" || Array.isArray(member)) {
48925
+ return;
48926
+ }
48927
+ addConfiguredModel(refs, member.model, `council.general.members[${index}].model`);
48928
+ });
48929
+ }
48930
+ if (general.presets && typeof general.presets === "object" && !Array.isArray(general.presets)) {
48931
+ for (const [presetName, members] of Object.entries(general.presets)) {
48932
+ if (!Array.isArray(members))
48933
+ continue;
48934
+ members.forEach((member, index) => {
48935
+ if (!member || typeof member !== "object" || Array.isArray(member)) {
48936
+ return;
48937
+ }
48938
+ addConfiguredModel(refs, member.model, `council.general.presets.${presetName}[${index}].model`);
48939
+ });
48940
+ }
48941
+ }
48942
+ }
48943
+ return refs;
48944
+ }
48945
+ function validateConfiguredModels(config3, modelAvailability) {
48946
+ const refs = collectConfiguredModelRefs(config3);
48947
+ const findings = [];
48948
+ if (modelAvailability.error) {
48949
+ findings.push({
48950
+ id: "model-availability-unchecked",
48951
+ title: "Model availability check skipped",
48952
+ description: `Could not load OpenCode provider models from ${modelAvailability.source}: ` + modelAvailability.error,
48953
+ severity: "info",
48954
+ path: "agents",
48955
+ autoFixable: false
48956
+ });
48957
+ return findings;
48958
+ }
48959
+ if (refs.size === 0)
48960
+ return findings;
48961
+ for (const [modelId, paths] of refs.entries()) {
48962
+ if (modelAvailability.availableModelIds.has(modelId))
48963
+ continue;
48964
+ findings.push({
48965
+ id: "configured-model-unavailable",
48966
+ title: "Configured model is unavailable",
48967
+ description: `Configured model ${formatModelIdForDoctor(modelId)} was not found in the active OpenCode provider model registry. ` + "Run `/models` to choose a currently available model, then update opencode-swarm.json.",
48968
+ severity: "error",
48969
+ path: [...paths].sort().join(", "),
48970
+ currentValue: modelId,
48971
+ autoFixable: false
48972
+ });
48973
+ }
48974
+ return findings;
48975
+ }
48976
+ function formatModelIdForDoctor(modelId) {
48977
+ const json3 = JSON.stringify(modelId);
48978
+ if (!json3)
48979
+ return '"<invalid model id>"';
48980
+ if (json3.length <= 160)
48981
+ return json3;
48982
+ return `${json3.slice(0, 157)}..."`;
48983
+ }
48758
48984
  function walkConfigAndValidate(obj, path31, config3, findings) {
48759
48985
  if (obj === null || obj === undefined) {
48760
48986
  return;
@@ -48779,9 +49005,12 @@ function walkConfigAndValidate(obj, path31, config3, findings) {
48779
49005
  walkConfigAndValidate(value, newPath, config3, findings);
48780
49006
  }
48781
49007
  }
48782
- function runConfigDoctor(config3, directory) {
49008
+ function runConfigDoctor(config3, directory, options = {}) {
48783
49009
  const findings = [];
48784
49010
  walkConfigAndValidate(config3, "", config3, findings);
49011
+ if (options.modelAvailability) {
49012
+ findings.push(...validateConfiguredModels(config3, options.modelAvailability));
49013
+ }
48785
49014
  const summary = {
48786
49015
  info: findings.filter((f) => f.severity === "info").length,
48787
49016
  warn: findings.filter((f) => f.severity === "warn").length,
@@ -48943,8 +49172,8 @@ function shouldRunOnStartup(automationConfig) {
48943
49172
  }
48944
49173
  return automationConfig.capabilities?.config_doctor_on_startup === true;
48945
49174
  }
48946
- async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
48947
- const result = runConfigDoctor(config3, directory);
49175
+ async function runConfigDoctorWithFixes(directory, config3, autoFix = false, options = {}) {
49176
+ const result = runConfigDoctor(config3, directory, options);
48948
49177
  const artifactPath = writeDoctorArtifact(directory, result);
48949
49178
  if (!autoFix) {
48950
49179
  return {
@@ -48964,7 +49193,7 @@ async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
48964
49193
  if (appliedFixes.length > 0) {
48965
49194
  const freshConfig = readConfigFromFile(directory);
48966
49195
  if (freshConfig) {
48967
- const newResult = runConfigDoctor(freshConfig.config, directory);
49196
+ const newResult = runConfigDoctor(freshConfig.config, directory, options);
48968
49197
  writeDoctorArtifact(directory, newResult);
48969
49198
  }
48970
49199
  }
@@ -50609,6 +50838,31 @@ var init_tool_doctor = __esm(() => {
50609
50838
  ];
50610
50839
  });
50611
50840
 
50841
+ // src/utils/timeout.ts
50842
+ async function withTimeout(promise3, ms, timeoutError) {
50843
+ let timer;
50844
+ const timeoutPromise = new Promise((_, reject) => {
50845
+ timer = setTimeout(() => reject(timeoutError), ms);
50846
+ if (typeof timer.unref === "function") {
50847
+ timer.unref();
50848
+ }
50849
+ });
50850
+ try {
50851
+ return await Promise.race([promise3, timeoutPromise]);
50852
+ } finally {
50853
+ if (timer !== undefined)
50854
+ clearTimeout(timer);
50855
+ }
50856
+ }
50857
+ function yieldToEventLoop() {
50858
+ return new Promise((resolve11) => {
50859
+ const t = setTimeout(resolve11, 0);
50860
+ if (typeof t.unref === "function") {
50861
+ t.unref();
50862
+ }
50863
+ });
50864
+ }
50865
+
50612
50866
  // src/commands/doctor.ts
50613
50867
  function formatToolDoctorMarkdown(result) {
50614
50868
  const lines = [
@@ -50682,13 +50936,67 @@ function formatDoctorMarkdown(result) {
50682
50936
  return lines.join(`
50683
50937
  `);
50684
50938
  }
50685
- async function handleDoctorCommand(directory, args2) {
50939
+ function extractAvailableModelIds(response) {
50940
+ const available = new Set;
50941
+ if (response?.providers !== undefined && !Array.isArray(response.providers)) {
50942
+ throw new Error("provider registry returned malformed provider list");
50943
+ }
50944
+ for (const provider of response?.providers ?? []) {
50945
+ if (!provider || typeof provider !== "object" || !provider.id || !provider.models || typeof provider.models !== "object" || Array.isArray(provider.models)) {
50946
+ continue;
50947
+ }
50948
+ for (const [modelKey, modelInfo] of Object.entries(provider.models)) {
50949
+ available.add(`${provider.id}/${modelKey}`);
50950
+ if (modelInfo && typeof modelInfo === "object" && modelInfo.id) {
50951
+ available.add(`${provider.id}/${modelInfo.id}`);
50952
+ }
50953
+ }
50954
+ }
50955
+ return available;
50956
+ }
50957
+ async function loadModelAvailability(directory, client, options = {}) {
50958
+ const providerClient = client;
50959
+ const providers = providerClient?.config?.providers;
50960
+ if (typeof providers !== "function") {
50961
+ return;
50962
+ }
50963
+ try {
50964
+ const response = await withTimeout(providers({ directory }), options.timeoutMs ?? MODEL_REGISTRY_TIMEOUT_MS, new Error(`OpenCode provider model registry lookup exceeded ${options.timeoutMs ?? MODEL_REGISTRY_TIMEOUT_MS}ms`));
50965
+ if (response.error) {
50966
+ return {
50967
+ availableModelIds: new Set,
50968
+ source: MODEL_REGISTRY_SOURCE,
50969
+ error: typeof response.error === "string" ? response.error : JSON.stringify(response.error)
50970
+ };
50971
+ }
50972
+ if (!response.data) {
50973
+ return {
50974
+ availableModelIds: new Set,
50975
+ source: MODEL_REGISTRY_SOURCE,
50976
+ error: "provider registry returned no data"
50977
+ };
50978
+ }
50979
+ return {
50980
+ availableModelIds: extractAvailableModelIds(response.data),
50981
+ source: MODEL_REGISTRY_SOURCE
50982
+ };
50983
+ } catch (error93) {
50984
+ return {
50985
+ availableModelIds: new Set,
50986
+ source: MODEL_REGISTRY_SOURCE,
50987
+ error: error93 instanceof Error ? error93.message : String(error93)
50988
+ };
50989
+ }
50990
+ }
50991
+ async function handleDoctorCommand(directory, args2, options = {}) {
50686
50992
  const enableAutoFix = args2.includes("--fix") || args2.includes("-f");
50687
50993
  const config3 = loadPluginConfig(directory);
50688
- const result = runConfigDoctor(config3, directory);
50994
+ const modelAvailability = await loadModelAvailability(directory, options.client);
50995
+ const doctorOptions = { modelAvailability };
50996
+ const result = runConfigDoctor(config3, directory, doctorOptions);
50689
50997
  if (enableAutoFix && result.hasAutoFixableIssues) {
50690
50998
  const { runConfigDoctorWithFixes: runConfigDoctorWithFixes2 } = await Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor));
50691
- const fixResult = await runConfigDoctorWithFixes2(directory, config3, true);
50999
+ const fixResult = await runConfigDoctorWithFixes2(directory, config3, true, doctorOptions);
50692
51000
  return formatDoctorMarkdown(fixResult.result);
50693
51001
  }
50694
51002
  return formatDoctorMarkdown(result);
@@ -50697,6 +51005,7 @@ async function handleDoctorToolsCommand(directory, _args) {
50697
51005
  const result = runToolDoctor(directory);
50698
51006
  return formatToolDoctorMarkdown(result);
50699
51007
  }
51008
+ var MODEL_REGISTRY_TIMEOUT_MS = 3000, MODEL_REGISTRY_SOURCE = "OpenCode config.providers";
50700
51009
  var init_doctor = __esm(() => {
50701
51010
  init_loader();
50702
51011
  init_config_doctor();
@@ -60867,7 +61176,7 @@ function buildHelpText() {
60867
61176
  return lines.join(`
60868
61177
  `);
60869
61178
  }
60870
- function createSwarmCommandHandler(directory, agents) {
61179
+ function createSwarmCommandHandler(directory, agents, client) {
60871
61180
  return async (input, output) => {
60872
61181
  if (input.command !== "swarm" && !input.command.startsWith("swarm-")) {
60873
61182
  return;
@@ -60914,7 +61223,8 @@ ${similar.map((cmd) => ` • /swarm ${cmd}`).join(`
60914
61223
  directory,
60915
61224
  args: resolved.remainingArgs,
60916
61225
  sessionID: input.sessionID,
60917
- agents
61226
+ agents,
61227
+ client
60918
61228
  });
60919
61229
  } catch (_err) {
60920
61230
  const cmdName = tokens[0] || "unknown";
@@ -60930,7 +61240,10 @@ ${text}`;
60930
61240
  if (isFirstRun) {
60931
61241
  const welcomeMessage = `Welcome to OpenCode Swarm! \uD83D\uDC1D
60932
61242
  ` + `
60933
- ` + `Run \`/swarm help\` to see all available commands, or \`/swarm config\` to review your configuration.
61243
+ ` + `Start here: run \`/swarm diagnose\`, then \`/swarm agents\` to confirm the plugin loaded and see the exact models in use.
61244
+ ` + `If a model is unavailable, edit \`.opencode/opencode-swarm.json\` or \`~/.config/opencode/opencode-swarm.json\` and run \`/swarm config doctor\`.
61245
+ ` + `Useful next steps: \`/swarm brainstorm <task>\` for guided planning, \`/swarm full-auto on\` for autonomous runs after enabling it in config, and \`/swarm council <question>\` after enabling council.general.
61246
+
60934
61247
  `;
60935
61248
  text = welcomeMessage + text;
60936
61249
  }
@@ -61247,13 +61560,13 @@ var init_registry = __esm(() => {
61247
61560
  clashesWithNativeCcCommand: "/config"
61248
61561
  },
61249
61562
  "config doctor": {
61250
- handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
61563
+ handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
61251
61564
  description: "Run config doctor checks",
61252
61565
  subcommandOf: "config",
61253
61566
  category: "diagnostics"
61254
61567
  },
61255
61568
  "config-doctor": {
61256
- handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
61569
+ handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
61257
61570
  description: "Run config doctor checks",
61258
61571
  subcommandOf: "config",
61259
61572
  category: "diagnostics",
@@ -61328,7 +61641,7 @@ var init_registry = __esm(() => {
61328
61641
  deprecated: true
61329
61642
  },
61330
61643
  doctor: {
61331
- handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
61644
+ handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
61332
61645
  description: "Run config doctor checks",
61333
61646
  category: "diagnostics",
61334
61647
  aliasOf: "config doctor",
@@ -71616,13 +71929,7 @@ function writeSwarmConfigExampleIfNew(projectDirectory) {
71616
71929
  fs40.mkdirSync(swarmDir, { recursive: true });
71617
71930
  }
71618
71931
  const example = {
71619
- agents: Object.fromEntries(Object.entries(DEFAULT_MODELS).filter(([name2]) => name2 !== "default").map(([name2, model]) => [
71620
- name2,
71621
- {
71622
- model,
71623
- fallback_models: ["opencode/gpt-5-nano", "opencode/big-pickle"]
71624
- }
71625
- ])),
71932
+ agents: DEFAULT_AGENT_CONFIGS,
71626
71933
  max_iterations: 5
71627
71934
  };
71628
71935
  fs40.writeFileSync(dest, `${JSON.stringify(example, null, 2)}
@@ -74064,31 +74371,6 @@ import * as fsPromises5 from "node:fs/promises";
74064
74371
  import * as os7 from "node:os";
74065
74372
  import * as path66 from "node:path";
74066
74373
 
74067
- // src/utils/timeout.ts
74068
- async function withTimeout(promise3, ms, timeoutError) {
74069
- let timer;
74070
- const timeoutPromise = new Promise((_, reject) => {
74071
- timer = setTimeout(() => reject(timeoutError), ms);
74072
- if (typeof timer.unref === "function") {
74073
- timer.unref();
74074
- }
74075
- });
74076
- try {
74077
- return await Promise.race([promise3, timeoutPromise]);
74078
- } finally {
74079
- if (timer !== undefined)
74080
- clearTimeout(timer);
74081
- }
74082
- }
74083
- function yieldToEventLoop() {
74084
- return new Promise((resolve19) => {
74085
- const t = setTimeout(resolve19, 0);
74086
- if (typeof t.unref === "function") {
74087
- t.unref();
74088
- }
74089
- });
74090
- }
74091
-
74092
74374
  // src/tools/symbols.ts
74093
74375
  init_zod();
74094
74376
  init_create_tool();
@@ -89503,6 +89785,7 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
89503
89785
  }
89504
89786
 
89505
89787
  // src/tools/phase-complete.ts
89788
+ init_task_id();
89506
89789
  init_create_tool();
89507
89790
  init_resolve_working_directory();
89508
89791
  function safeWarn(message, error93) {
@@ -89510,6 +89793,15 @@ function safeWarn(message, error93) {
89510
89793
  console.warn(message, error93 instanceof Error ? error93.message : String(error93));
89511
89794
  } catch {}
89512
89795
  }
89796
+ function taskIdToPhase(taskId) {
89797
+ if (typeof taskId !== "string")
89798
+ return null;
89799
+ if (!isStrictTaskId(taskId))
89800
+ return null;
89801
+ const head = taskId.split(".")[0];
89802
+ const n = Number.parseInt(head, 10);
89803
+ return Number.isFinite(n) && n > 0 ? n : null;
89804
+ }
89513
89805
  function collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, callerSessionId) {
89514
89806
  const agents = new Set;
89515
89807
  const contributorSessionIds = [];
@@ -90622,6 +90914,52 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
90622
90914
  const oldPhase = contributorSession.lastPhaseCompletePhase;
90623
90915
  contributorSession.lastPhaseCompletePhase = phase;
90624
90916
  telemetry.phaseChanged(contributorSessionId, oldPhase ?? 0, phase);
90917
+ const currentTid = contributorSession.currentTaskId;
90918
+ if (currentTid) {
90919
+ const tp = taskIdToPhase(currentTid);
90920
+ if (tp !== null && tp <= phase) {
90921
+ contributorSession.currentTaskId = null;
90922
+ }
90923
+ }
90924
+ const lastCoderTid = contributorSession.lastCoderDelegationTaskId;
90925
+ if (lastCoderTid) {
90926
+ const tp = taskIdToPhase(lastCoderTid);
90927
+ if (tp !== null && tp <= phase) {
90928
+ contributorSession.lastCoderDelegationTaskId = null;
90929
+ }
90930
+ }
90931
+ const openStates = new Set([
90932
+ "coder_delegated",
90933
+ "pre_check_passed",
90934
+ "reviewer_run"
90935
+ ]);
90936
+ if (contributorSession.taskWorkflowStates instanceof Map) {
90937
+ for (const [taskId, state] of Array.from(contributorSession.taskWorkflowStates.entries())) {
90938
+ const tp = taskIdToPhase(taskId);
90939
+ if (tp === null || tp > phase)
90940
+ continue;
90941
+ if (openStates.has(state)) {
90942
+ console.warn(`[phase-complete] dropping open task state at phase boundary: taskId=${taskId} state=${state} phaseCompleted=${phase}`);
90943
+ }
90944
+ contributorSession.taskWorkflowStates.delete(taskId);
90945
+ }
90946
+ }
90947
+ if (contributorSession.stageBCompletion instanceof Map) {
90948
+ for (const taskId of Array.from(contributorSession.stageBCompletion.keys())) {
90949
+ const tp = taskIdToPhase(taskId);
90950
+ if (tp !== null && tp <= phase) {
90951
+ contributorSession.stageBCompletion.delete(taskId);
90952
+ }
90953
+ }
90954
+ }
90955
+ if (contributorSession.requiredStageBGates instanceof Map) {
90956
+ for (const taskId of Array.from(contributorSession.requiredStageBGates.keys())) {
90957
+ const tp = taskIdToPhase(taskId);
90958
+ if (tp !== null && tp <= phase) {
90959
+ contributorSession.requiredStageBGates.delete(taskId);
90960
+ }
90961
+ }
90962
+ }
90625
90963
  }
90626
90964
  }
90627
90965
  try {
@@ -103330,7 +103668,6 @@ init_write_retro();
103330
103668
 
103331
103669
  // src/index.ts
103332
103670
  init_utils();
103333
-
103334
103671
  // src/utils/tool-output.ts
103335
103672
  function truncateToolOutput(output, maxLines, toolName, tailLines = 10) {
103336
103673
  if (!output) {
@@ -103473,7 +103810,7 @@ async function initializeOpenCodeSwarm(ctx) {
103473
103810
  const systemEnhancerHook = createSystemEnhancerHook(config3, ctx.directory);
103474
103811
  const compactionHook = createCompactionCustomizerHook(config3, ctx.directory);
103475
103812
  const contextBudgetHandler = createContextBudgetHandler(config3);
103476
- const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])));
103813
+ const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])), ctx.client);
103477
103814
  const activityHooks = createAgentActivityHooks(config3, ctx.directory);
103478
103815
  const prmHook = createPrmHook(config3.prm ?? PrmConfigSchema.parse({}), ctx.directory);
103479
103816
  const trajectoryLoggerHook = createTrajectoryLoggerHook({