opencode-swarm 7.18.1 → 7.18.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 +35 -22
- package/dist/cli/index.js +282 -45
- package/dist/commands/council.d.ts +1 -1
- package/dist/commands/doctor.d.ts +7 -2
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/registry.d.ts +1 -0
- package/dist/index.js +343 -64
- package/dist/services/config-doctor.d.ts +15 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.18.
|
|
37
|
+
version: "7.18.3",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
|
@@ -16501,8 +16501,7 @@ var init_constants = __esm(() => {
|
|
|
16501
16501
|
"skill_improve",
|
|
16502
16502
|
"search",
|
|
16503
16503
|
"doc_scan",
|
|
16504
|
-
"doc_extract"
|
|
16505
|
-
"web_search"
|
|
16504
|
+
"doc_extract"
|
|
16506
16505
|
],
|
|
16507
16506
|
spec_writer: [
|
|
16508
16507
|
"search",
|
|
@@ -16602,67 +16601,67 @@ var init_constants = __esm(() => {
|
|
|
16602
16601
|
DEFAULT_AGENT_CONFIGS = {
|
|
16603
16602
|
coder: {
|
|
16604
16603
|
model: "opencode/minimax-m2.5-free",
|
|
16605
|
-
fallback_models: ["opencode/
|
|
16604
|
+
fallback_models: ["opencode/big-pickle"]
|
|
16606
16605
|
},
|
|
16607
16606
|
reviewer: {
|
|
16608
16607
|
model: "opencode/big-pickle",
|
|
16609
|
-
fallback_models: ["opencode/
|
|
16608
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16610
16609
|
},
|
|
16611
16610
|
test_engineer: {
|
|
16612
|
-
model: "opencode/
|
|
16613
|
-
fallback_models: ["opencode/
|
|
16611
|
+
model: "opencode/big-pickle",
|
|
16612
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16614
16613
|
},
|
|
16615
16614
|
explorer: {
|
|
16616
16615
|
model: "opencode/big-pickle",
|
|
16617
|
-
fallback_models: ["opencode/
|
|
16616
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16618
16617
|
},
|
|
16619
16618
|
sme: {
|
|
16620
16619
|
model: "opencode/big-pickle",
|
|
16621
|
-
fallback_models: ["opencode/
|
|
16620
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16622
16621
|
},
|
|
16623
16622
|
critic: {
|
|
16624
16623
|
model: "opencode/big-pickle",
|
|
16625
|
-
fallback_models: ["opencode/
|
|
16624
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16626
16625
|
},
|
|
16627
16626
|
docs: {
|
|
16628
16627
|
model: "opencode/big-pickle",
|
|
16629
|
-
fallback_models: ["opencode/
|
|
16628
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16630
16629
|
},
|
|
16631
16630
|
designer: {
|
|
16632
16631
|
model: "opencode/big-pickle",
|
|
16633
|
-
fallback_models: ["opencode/
|
|
16632
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16634
16633
|
},
|
|
16635
16634
|
critic_sounding_board: {
|
|
16636
|
-
model: "opencode/
|
|
16637
|
-
fallback_models: ["opencode/
|
|
16635
|
+
model: "opencode/big-pickle",
|
|
16636
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16638
16637
|
},
|
|
16639
16638
|
critic_drift_verifier: {
|
|
16640
|
-
model: "opencode/
|
|
16641
|
-
fallback_models: ["opencode/
|
|
16639
|
+
model: "opencode/big-pickle",
|
|
16640
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16642
16641
|
},
|
|
16643
16642
|
critic_hallucination_verifier: {
|
|
16644
|
-
model: "opencode/
|
|
16645
|
-
fallback_models: ["opencode/
|
|
16643
|
+
model: "opencode/big-pickle",
|
|
16644
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16646
16645
|
},
|
|
16647
16646
|
critic_oversight: {
|
|
16648
|
-
model: "opencode/
|
|
16649
|
-
fallback_models: ["opencode/
|
|
16647
|
+
model: "opencode/big-pickle",
|
|
16648
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16650
16649
|
},
|
|
16651
16650
|
curator_init: {
|
|
16652
|
-
model: "opencode/
|
|
16653
|
-
fallback_models: ["opencode/
|
|
16651
|
+
model: "opencode/big-pickle",
|
|
16652
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16654
16653
|
},
|
|
16655
16654
|
curator_phase: {
|
|
16656
|
-
model: "opencode/
|
|
16657
|
-
fallback_models: ["opencode/
|
|
16655
|
+
model: "opencode/big-pickle",
|
|
16656
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16658
16657
|
},
|
|
16659
16658
|
skill_improver: {
|
|
16660
16659
|
model: "opencode/big-pickle",
|
|
16661
|
-
fallback_models: ["opencode/
|
|
16660
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16662
16661
|
},
|
|
16663
16662
|
spec_writer: {
|
|
16664
16663
|
model: "opencode/big-pickle",
|
|
16665
|
-
fallback_models: ["opencode/
|
|
16664
|
+
fallback_models: ["opencode/minimax-m2.5-free"]
|
|
16666
16665
|
}
|
|
16667
16666
|
};
|
|
16668
16667
|
});
|
|
@@ -17545,7 +17544,8 @@ function handleAgentsCommand(agents, guardrails) {
|
|
|
17545
17544
|
if (hasUnregistered) {
|
|
17546
17545
|
lines.push("", "### Unregistered Subagents");
|
|
17547
17546
|
for (const name of unregistered) {
|
|
17548
|
-
|
|
17547
|
+
const hint = UNREGISTERED_AGENT_HINTS[name] ?? "requires configuration";
|
|
17548
|
+
lines.push(`- **${name}** (${hint})`);
|
|
17549
17549
|
}
|
|
17550
17550
|
}
|
|
17551
17551
|
if (guardrails?.profiles && Object.keys(guardrails.profiles).length > 0) {
|
|
@@ -17574,9 +17574,18 @@ function handleAgentsCommand(agents, guardrails) {
|
|
|
17574
17574
|
return lines.join(`
|
|
17575
17575
|
`);
|
|
17576
17576
|
}
|
|
17577
|
+
var UNREGISTERED_AGENT_HINTS;
|
|
17577
17578
|
var init_agents = __esm(() => {
|
|
17578
17579
|
init_constants();
|
|
17579
17580
|
init_schema();
|
|
17581
|
+
UNREGISTERED_AGENT_HINTS = {
|
|
17582
|
+
designer: "enable ui_review.enabled",
|
|
17583
|
+
council_generalist: "enable council.general.enabled",
|
|
17584
|
+
council_skeptic: "enable council.general.enabled",
|
|
17585
|
+
council_domain_expert: "enable council.general.enabled",
|
|
17586
|
+
skill_improver: "registered by default unless agents.skill_improver.disabled is true",
|
|
17587
|
+
spec_writer: "registered by default unless agents.spec_writer.disabled is true"
|
|
17588
|
+
};
|
|
17580
17589
|
});
|
|
17581
17590
|
|
|
17582
17591
|
// src/commands/analyze.ts
|
|
@@ -38247,12 +38256,31 @@ function parseArgs(args) {
|
|
|
38247
38256
|
}
|
|
38248
38257
|
return out;
|
|
38249
38258
|
}
|
|
38250
|
-
async function handleCouncilCommand(
|
|
38259
|
+
async function handleCouncilCommand(directory, args) {
|
|
38251
38260
|
const parsed = parseArgs(args);
|
|
38252
38261
|
const question = sanitizeQuestion(parsed.rest.join(" "));
|
|
38253
38262
|
if (!question) {
|
|
38254
38263
|
return USAGE;
|
|
38255
38264
|
}
|
|
38265
|
+
const config3 = loadPluginConfig(directory);
|
|
38266
|
+
if (config3.council?.general?.enabled !== true) {
|
|
38267
|
+
return [
|
|
38268
|
+
"General Council is not enabled for this project.",
|
|
38269
|
+
"",
|
|
38270
|
+
"Enable it in `.opencode/opencode-swarm.json` or `~/.config/opencode/opencode-swarm.json`:",
|
|
38271
|
+
"",
|
|
38272
|
+
"```json",
|
|
38273
|
+
"{",
|
|
38274
|
+
' "council": {',
|
|
38275
|
+
' "general": { "enabled": true }',
|
|
38276
|
+
" }",
|
|
38277
|
+
"}",
|
|
38278
|
+
"```",
|
|
38279
|
+
"",
|
|
38280
|
+
"Then restart OpenCode and run `/swarm config doctor` before trying `/swarm council` again."
|
|
38281
|
+
].join(`
|
|
38282
|
+
`);
|
|
38283
|
+
}
|
|
38256
38284
|
const tokens = ["MODE: COUNCIL"];
|
|
38257
38285
|
if (parsed.preset) {
|
|
38258
38286
|
tokens.push(`preset=${parsed.preset}`);
|
|
@@ -38264,6 +38292,7 @@ async function handleCouncilCommand(_directory, args) {
|
|
|
38264
38292
|
}
|
|
38265
38293
|
var MAX_QUESTION_LEN = 2000, USAGE;
|
|
38266
38294
|
var init_council = __esm(() => {
|
|
38295
|
+
init_loader();
|
|
38267
38296
|
USAGE = [
|
|
38268
38297
|
"Usage: /swarm council <question> [--preset <name>] [--spec-review]",
|
|
38269
38298
|
"",
|
|
@@ -39740,6 +39769,7 @@ __export(exports_config_doctor, {
|
|
|
39740
39769
|
restoreFromBackup: () => restoreFromBackup,
|
|
39741
39770
|
getConfigPaths: () => getConfigPaths,
|
|
39742
39771
|
createConfigBackup: () => createConfigBackup,
|
|
39772
|
+
collectConfiguredModelRefs: () => collectConfiguredModelRefs,
|
|
39743
39773
|
applySafeAutoFixes: () => applySafeAutoFixes
|
|
39744
39774
|
});
|
|
39745
39775
|
import * as crypto3 from "crypto";
|
|
@@ -40133,6 +40163,129 @@ function validateConfigKey(path24, value, _config) {
|
|
|
40133
40163
|
}
|
|
40134
40164
|
return findings;
|
|
40135
40165
|
}
|
|
40166
|
+
function addConfiguredModel(refs, model, configPath) {
|
|
40167
|
+
if (typeof model !== "string")
|
|
40168
|
+
return;
|
|
40169
|
+
const trimmed = model.trim();
|
|
40170
|
+
if (!trimmed)
|
|
40171
|
+
return;
|
|
40172
|
+
const paths = refs.get(trimmed) ?? new Set;
|
|
40173
|
+
paths.add(configPath);
|
|
40174
|
+
refs.set(trimmed, paths);
|
|
40175
|
+
}
|
|
40176
|
+
function addConfiguredAgentModels(refs, agents, prefix) {
|
|
40177
|
+
if (!agents || typeof agents !== "object" || Array.isArray(agents))
|
|
40178
|
+
return;
|
|
40179
|
+
for (const [agentName, value] of Object.entries(agents)) {
|
|
40180
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
40181
|
+
continue;
|
|
40182
|
+
const agent = value;
|
|
40183
|
+
addConfiguredModel(refs, agent.model, `${prefix}.${agentName}.model`);
|
|
40184
|
+
if (Array.isArray(agent.fallback_models)) {
|
|
40185
|
+
agent.fallback_models.forEach((model, index) => {
|
|
40186
|
+
addConfiguredModel(refs, model, `${prefix}.${agentName}.fallback_models[${index}]`);
|
|
40187
|
+
});
|
|
40188
|
+
}
|
|
40189
|
+
}
|
|
40190
|
+
}
|
|
40191
|
+
function collectConfiguredModelRefs(config3) {
|
|
40192
|
+
const refs = new Map;
|
|
40193
|
+
const rawConfig = config3;
|
|
40194
|
+
addConfiguredAgentModels(refs, rawConfig.agents, "agents");
|
|
40195
|
+
if (rawConfig.swarms && typeof rawConfig.swarms === "object" && !Array.isArray(rawConfig.swarms)) {
|
|
40196
|
+
for (const [swarmId, value] of Object.entries(rawConfig.swarms)) {
|
|
40197
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
40198
|
+
continue;
|
|
40199
|
+
addConfiguredAgentModels(refs, value.agents, `swarms.${swarmId}.agents`);
|
|
40200
|
+
}
|
|
40201
|
+
}
|
|
40202
|
+
if (rawConfig.full_auto && typeof rawConfig.full_auto === "object" && !Array.isArray(rawConfig.full_auto)) {
|
|
40203
|
+
addConfiguredModel(refs, rawConfig.full_auto.critic_model, "full_auto.critic_model");
|
|
40204
|
+
}
|
|
40205
|
+
if (rawConfig.skill_improver && typeof rawConfig.skill_improver === "object" && !Array.isArray(rawConfig.skill_improver)) {
|
|
40206
|
+
const skillImprover = rawConfig.skill_improver;
|
|
40207
|
+
addConfiguredModel(refs, skillImprover.model, "skill_improver.model");
|
|
40208
|
+
if (Array.isArray(skillImprover.fallback_models)) {
|
|
40209
|
+
skillImprover.fallback_models.forEach((model, index) => {
|
|
40210
|
+
addConfiguredModel(refs, model, `skill_improver.fallback_models[${index}]`);
|
|
40211
|
+
});
|
|
40212
|
+
}
|
|
40213
|
+
}
|
|
40214
|
+
if (rawConfig.spec_writer && typeof rawConfig.spec_writer === "object" && !Array.isArray(rawConfig.spec_writer)) {
|
|
40215
|
+
const specWriter = rawConfig.spec_writer;
|
|
40216
|
+
addConfiguredModel(refs, specWriter.model, "spec_writer.model");
|
|
40217
|
+
if (Array.isArray(specWriter.fallback_models)) {
|
|
40218
|
+
specWriter.fallback_models.forEach((model, index) => {
|
|
40219
|
+
addConfiguredModel(refs, model, `spec_writer.fallback_models[${index}]`);
|
|
40220
|
+
});
|
|
40221
|
+
}
|
|
40222
|
+
}
|
|
40223
|
+
const council = rawConfig.council;
|
|
40224
|
+
const general = council && typeof council === "object" && !Array.isArray(council) ? council.general : undefined;
|
|
40225
|
+
if (general && typeof general === "object" && !Array.isArray(general)) {
|
|
40226
|
+
addConfiguredModel(refs, general.moderatorModel, "council.general.moderatorModel");
|
|
40227
|
+
if (Array.isArray(general.members)) {
|
|
40228
|
+
general.members.forEach((member, index) => {
|
|
40229
|
+
if (!member || typeof member !== "object" || Array.isArray(member)) {
|
|
40230
|
+
return;
|
|
40231
|
+
}
|
|
40232
|
+
addConfiguredModel(refs, member.model, `council.general.members[${index}].model`);
|
|
40233
|
+
});
|
|
40234
|
+
}
|
|
40235
|
+
if (general.presets && typeof general.presets === "object" && !Array.isArray(general.presets)) {
|
|
40236
|
+
for (const [presetName, members] of Object.entries(general.presets)) {
|
|
40237
|
+
if (!Array.isArray(members))
|
|
40238
|
+
continue;
|
|
40239
|
+
members.forEach((member, index) => {
|
|
40240
|
+
if (!member || typeof member !== "object" || Array.isArray(member)) {
|
|
40241
|
+
return;
|
|
40242
|
+
}
|
|
40243
|
+
addConfiguredModel(refs, member.model, `council.general.presets.${presetName}[${index}].model`);
|
|
40244
|
+
});
|
|
40245
|
+
}
|
|
40246
|
+
}
|
|
40247
|
+
}
|
|
40248
|
+
return refs;
|
|
40249
|
+
}
|
|
40250
|
+
function validateConfiguredModels(config3, modelAvailability) {
|
|
40251
|
+
const refs = collectConfiguredModelRefs(config3);
|
|
40252
|
+
const findings = [];
|
|
40253
|
+
if (modelAvailability.error) {
|
|
40254
|
+
findings.push({
|
|
40255
|
+
id: "model-availability-unchecked",
|
|
40256
|
+
title: "Model availability check skipped",
|
|
40257
|
+
description: `Could not load OpenCode provider models from ${modelAvailability.source}: ` + modelAvailability.error,
|
|
40258
|
+
severity: "info",
|
|
40259
|
+
path: "agents",
|
|
40260
|
+
autoFixable: false
|
|
40261
|
+
});
|
|
40262
|
+
return findings;
|
|
40263
|
+
}
|
|
40264
|
+
if (refs.size === 0)
|
|
40265
|
+
return findings;
|
|
40266
|
+
for (const [modelId, paths] of refs.entries()) {
|
|
40267
|
+
if (modelAvailability.availableModelIds.has(modelId))
|
|
40268
|
+
continue;
|
|
40269
|
+
findings.push({
|
|
40270
|
+
id: "configured-model-unavailable",
|
|
40271
|
+
title: "Configured model is unavailable",
|
|
40272
|
+
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.",
|
|
40273
|
+
severity: "error",
|
|
40274
|
+
path: [...paths].sort().join(", "),
|
|
40275
|
+
currentValue: modelId,
|
|
40276
|
+
autoFixable: false
|
|
40277
|
+
});
|
|
40278
|
+
}
|
|
40279
|
+
return findings;
|
|
40280
|
+
}
|
|
40281
|
+
function formatModelIdForDoctor(modelId) {
|
|
40282
|
+
const json3 = JSON.stringify(modelId);
|
|
40283
|
+
if (!json3)
|
|
40284
|
+
return '"<invalid model id>"';
|
|
40285
|
+
if (json3.length <= 160)
|
|
40286
|
+
return json3;
|
|
40287
|
+
return `${json3.slice(0, 157)}..."`;
|
|
40288
|
+
}
|
|
40136
40289
|
function walkConfigAndValidate(obj, path24, config3, findings) {
|
|
40137
40290
|
if (obj === null || obj === undefined) {
|
|
40138
40291
|
return;
|
|
@@ -40157,9 +40310,12 @@ function walkConfigAndValidate(obj, path24, config3, findings) {
|
|
|
40157
40310
|
walkConfigAndValidate(value, newPath, config3, findings);
|
|
40158
40311
|
}
|
|
40159
40312
|
}
|
|
40160
|
-
function runConfigDoctor(config3, directory) {
|
|
40313
|
+
function runConfigDoctor(config3, directory, options = {}) {
|
|
40161
40314
|
const findings = [];
|
|
40162
40315
|
walkConfigAndValidate(config3, "", config3, findings);
|
|
40316
|
+
if (options.modelAvailability) {
|
|
40317
|
+
findings.push(...validateConfiguredModels(config3, options.modelAvailability));
|
|
40318
|
+
}
|
|
40163
40319
|
const summary = {
|
|
40164
40320
|
info: findings.filter((f) => f.severity === "info").length,
|
|
40165
40321
|
warn: findings.filter((f) => f.severity === "warn").length,
|
|
@@ -40321,8 +40477,8 @@ function shouldRunOnStartup(automationConfig) {
|
|
|
40321
40477
|
}
|
|
40322
40478
|
return automationConfig.capabilities?.config_doctor_on_startup === true;
|
|
40323
40479
|
}
|
|
40324
|
-
async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
|
|
40325
|
-
const result = runConfigDoctor(config3, directory);
|
|
40480
|
+
async function runConfigDoctorWithFixes(directory, config3, autoFix = false, options = {}) {
|
|
40481
|
+
const result = runConfigDoctor(config3, directory, options);
|
|
40326
40482
|
const artifactPath = writeDoctorArtifact(directory, result);
|
|
40327
40483
|
if (!autoFix) {
|
|
40328
40484
|
return {
|
|
@@ -40342,7 +40498,7 @@ async function runConfigDoctorWithFixes(directory, config3, autoFix = false) {
|
|
|
40342
40498
|
if (appliedFixes.length > 0) {
|
|
40343
40499
|
const freshConfig = readConfigFromFile(directory);
|
|
40344
40500
|
if (freshConfig) {
|
|
40345
|
-
const newResult = runConfigDoctor(freshConfig.config, directory);
|
|
40501
|
+
const newResult = runConfigDoctor(freshConfig.config, directory, options);
|
|
40346
40502
|
writeDoctorArtifact(directory, newResult);
|
|
40347
40503
|
}
|
|
40348
40504
|
}
|
|
@@ -41964,6 +42120,23 @@ var init_tool_doctor = __esm(() => {
|
|
|
41964
42120
|
];
|
|
41965
42121
|
});
|
|
41966
42122
|
|
|
42123
|
+
// src/utils/timeout.ts
|
|
42124
|
+
async function withTimeout(promise3, ms, timeoutError) {
|
|
42125
|
+
let timer;
|
|
42126
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
42127
|
+
timer = setTimeout(() => reject(timeoutError), ms);
|
|
42128
|
+
if (typeof timer.unref === "function") {
|
|
42129
|
+
timer.unref();
|
|
42130
|
+
}
|
|
42131
|
+
});
|
|
42132
|
+
try {
|
|
42133
|
+
return await Promise.race([promise3, timeoutPromise]);
|
|
42134
|
+
} finally {
|
|
42135
|
+
if (timer !== undefined)
|
|
42136
|
+
clearTimeout(timer);
|
|
42137
|
+
}
|
|
42138
|
+
}
|
|
42139
|
+
|
|
41967
42140
|
// src/commands/doctor.ts
|
|
41968
42141
|
function formatToolDoctorMarkdown(result) {
|
|
41969
42142
|
const lines = [
|
|
@@ -42037,13 +42210,67 @@ function formatDoctorMarkdown(result) {
|
|
|
42037
42210
|
return lines.join(`
|
|
42038
42211
|
`);
|
|
42039
42212
|
}
|
|
42040
|
-
|
|
42213
|
+
function extractAvailableModelIds(response) {
|
|
42214
|
+
const available = new Set;
|
|
42215
|
+
if (response?.providers !== undefined && !Array.isArray(response.providers)) {
|
|
42216
|
+
throw new Error("provider registry returned malformed provider list");
|
|
42217
|
+
}
|
|
42218
|
+
for (const provider of response?.providers ?? []) {
|
|
42219
|
+
if (!provider || typeof provider !== "object" || !provider.id || !provider.models || typeof provider.models !== "object" || Array.isArray(provider.models)) {
|
|
42220
|
+
continue;
|
|
42221
|
+
}
|
|
42222
|
+
for (const [modelKey, modelInfo] of Object.entries(provider.models)) {
|
|
42223
|
+
available.add(`${provider.id}/${modelKey}`);
|
|
42224
|
+
if (modelInfo && typeof modelInfo === "object" && modelInfo.id) {
|
|
42225
|
+
available.add(`${provider.id}/${modelInfo.id}`);
|
|
42226
|
+
}
|
|
42227
|
+
}
|
|
42228
|
+
}
|
|
42229
|
+
return available;
|
|
42230
|
+
}
|
|
42231
|
+
async function loadModelAvailability(directory, client, options = {}) {
|
|
42232
|
+
const providerClient = client;
|
|
42233
|
+
const providers = providerClient?.config?.providers;
|
|
42234
|
+
if (typeof providers !== "function") {
|
|
42235
|
+
return;
|
|
42236
|
+
}
|
|
42237
|
+
try {
|
|
42238
|
+
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`));
|
|
42239
|
+
if (response.error) {
|
|
42240
|
+
return {
|
|
42241
|
+
availableModelIds: new Set,
|
|
42242
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
42243
|
+
error: typeof response.error === "string" ? response.error : JSON.stringify(response.error)
|
|
42244
|
+
};
|
|
42245
|
+
}
|
|
42246
|
+
if (!response.data) {
|
|
42247
|
+
return {
|
|
42248
|
+
availableModelIds: new Set,
|
|
42249
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
42250
|
+
error: "provider registry returned no data"
|
|
42251
|
+
};
|
|
42252
|
+
}
|
|
42253
|
+
return {
|
|
42254
|
+
availableModelIds: extractAvailableModelIds(response.data),
|
|
42255
|
+
source: MODEL_REGISTRY_SOURCE
|
|
42256
|
+
};
|
|
42257
|
+
} catch (error93) {
|
|
42258
|
+
return {
|
|
42259
|
+
availableModelIds: new Set,
|
|
42260
|
+
source: MODEL_REGISTRY_SOURCE,
|
|
42261
|
+
error: error93 instanceof Error ? error93.message : String(error93)
|
|
42262
|
+
};
|
|
42263
|
+
}
|
|
42264
|
+
}
|
|
42265
|
+
async function handleDoctorCommand(directory, args, options = {}) {
|
|
42041
42266
|
const enableAutoFix = args.includes("--fix") || args.includes("-f");
|
|
42042
42267
|
const config3 = loadPluginConfig(directory);
|
|
42043
|
-
const
|
|
42268
|
+
const modelAvailability = await loadModelAvailability(directory, options.client);
|
|
42269
|
+
const doctorOptions = { modelAvailability };
|
|
42270
|
+
const result = runConfigDoctor(config3, directory, doctorOptions);
|
|
42044
42271
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
42045
42272
|
const { runConfigDoctorWithFixes: runConfigDoctorWithFixes2 } = await Promise.resolve().then(() => (init_config_doctor(), exports_config_doctor));
|
|
42046
|
-
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true);
|
|
42273
|
+
const fixResult = await runConfigDoctorWithFixes2(directory, config3, true, doctorOptions);
|
|
42047
42274
|
return formatDoctorMarkdown(fixResult.result);
|
|
42048
42275
|
}
|
|
42049
42276
|
return formatDoctorMarkdown(result);
|
|
@@ -42052,6 +42279,7 @@ async function handleDoctorToolsCommand(directory, _args) {
|
|
|
42052
42279
|
const result = runToolDoctor(directory);
|
|
42053
42280
|
return formatToolDoctorMarkdown(result);
|
|
42054
42281
|
}
|
|
42282
|
+
var MODEL_REGISTRY_TIMEOUT_MS = 3000, MODEL_REGISTRY_SOURCE = "OpenCode config.providers";
|
|
42055
42283
|
var init_doctor = __esm(() => {
|
|
42056
42284
|
init_loader();
|
|
42057
42285
|
init_config_doctor();
|
|
@@ -51681,6 +51909,7 @@ __export(exports_commands, {
|
|
|
51681
51909
|
createSwarmCommandHandler: () => createSwarmCommandHandler,
|
|
51682
51910
|
buildHelpText: () => buildHelpText,
|
|
51683
51911
|
VALID_COMMANDS: () => VALID_COMMANDS,
|
|
51912
|
+
LLM_MEDIATION_WARNING: () => LLM_MEDIATION_WARNING,
|
|
51684
51913
|
COMMAND_REGISTRY: () => COMMAND_REGISTRY,
|
|
51685
51914
|
COMMAND_NAME_SET: () => COMMAND_NAME_SET,
|
|
51686
51915
|
COMMAND_NAMES: () => COMMAND_NAMES
|
|
@@ -51688,7 +51917,7 @@ __export(exports_commands, {
|
|
|
51688
51917
|
import fs28 from "fs";
|
|
51689
51918
|
import path46 from "path";
|
|
51690
51919
|
function buildHelpText() {
|
|
51691
|
-
const lines = ["## Swarm Commands", ""];
|
|
51920
|
+
const lines = ["## Swarm Commands", "", LLM_MEDIATION_WARNING, ""];
|
|
51692
51921
|
const CATEGORIES = [
|
|
51693
51922
|
"core",
|
|
51694
51923
|
"agent",
|
|
@@ -51783,7 +52012,7 @@ function buildHelpText() {
|
|
|
51783
52012
|
return lines.join(`
|
|
51784
52013
|
`);
|
|
51785
52014
|
}
|
|
51786
|
-
function createSwarmCommandHandler(directory, agents) {
|
|
52015
|
+
function createSwarmCommandHandler(directory, agents, client) {
|
|
51787
52016
|
return async (input, output) => {
|
|
51788
52017
|
if (input.command !== "swarm" && !input.command.startsWith("swarm-")) {
|
|
51789
52018
|
return;
|
|
@@ -51830,7 +52059,8 @@ ${similar.map((cmd) => ` \u2022 /swarm ${cmd}`).join(`
|
|
|
51830
52059
|
directory,
|
|
51831
52060
|
args: resolved.remainingArgs,
|
|
51832
52061
|
sessionID: input.sessionID,
|
|
51833
|
-
agents
|
|
52062
|
+
agents,
|
|
52063
|
+
client
|
|
51834
52064
|
});
|
|
51835
52065
|
} catch (_err) {
|
|
51836
52066
|
const cmdName = tokens[0] || "unknown";
|
|
@@ -51846,15 +52076,20 @@ ${text}`;
|
|
|
51846
52076
|
if (isFirstRun) {
|
|
51847
52077
|
const welcomeMessage = `Welcome to OpenCode Swarm! \uD83D\uDC1D
|
|
51848
52078
|
` + `
|
|
51849
|
-
` + `
|
|
52079
|
+
` + `Start here: run \`/swarm diagnose\`, then \`/swarm agents\` to confirm the plugin loaded and see the exact models in use.
|
|
52080
|
+
` + `If a model is unavailable, edit \`.opencode/opencode-swarm.json\` or \`~/.config/opencode/opencode-swarm.json\` and run \`/swarm config doctor\`.
|
|
52081
|
+
` + `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.
|
|
52082
|
+
|
|
51850
52083
|
`;
|
|
51851
52084
|
text = welcomeMessage + text;
|
|
51852
52085
|
}
|
|
51853
|
-
output.parts
|
|
51854
|
-
|
|
51855
|
-
|
|
52086
|
+
output.parts.splice(0, output.parts.length, {
|
|
52087
|
+
type: "text",
|
|
52088
|
+
text
|
|
52089
|
+
});
|
|
51856
52090
|
};
|
|
51857
52091
|
}
|
|
52092
|
+
var LLM_MEDIATION_WARNING;
|
|
51858
52093
|
var init_commands = __esm(() => {
|
|
51859
52094
|
init_registry();
|
|
51860
52095
|
init_acknowledge_spec_drift();
|
|
@@ -51892,6 +52127,8 @@ var init_commands = __esm(() => {
|
|
|
51892
52127
|
init_sync_plan();
|
|
51893
52128
|
init_turbo();
|
|
51894
52129
|
init_write_retro2();
|
|
52130
|
+
LLM_MEDIATION_WARNING = "> \u26A0\uFE0F Chat-typed `/swarm` is LLM-mediated in current OpenCode (see anomalyco/opencode#9306).\n" + `> The text below is the canonical handler output, but the model may rephrase it before display.
|
|
52131
|
+
` + "> For deterministic output, run `bunx opencode-swarm run <subcommand>` from a terminal.";
|
|
51895
52132
|
});
|
|
51896
52133
|
|
|
51897
52134
|
// src/commands/registry.ts
|
|
@@ -52163,13 +52400,13 @@ var init_registry = __esm(() => {
|
|
|
52163
52400
|
clashesWithNativeCcCommand: "/config"
|
|
52164
52401
|
},
|
|
52165
52402
|
"config doctor": {
|
|
52166
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
52403
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
52167
52404
|
description: "Run config doctor checks",
|
|
52168
52405
|
subcommandOf: "config",
|
|
52169
52406
|
category: "diagnostics"
|
|
52170
52407
|
},
|
|
52171
52408
|
"config-doctor": {
|
|
52172
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
52409
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
52173
52410
|
description: "Run config doctor checks",
|
|
52174
52411
|
subcommandOf: "config",
|
|
52175
52412
|
category: "diagnostics",
|
|
@@ -52244,7 +52481,7 @@ var init_registry = __esm(() => {
|
|
|
52244
52481
|
deprecated: true
|
|
52245
52482
|
},
|
|
52246
52483
|
doctor: {
|
|
52247
|
-
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args),
|
|
52484
|
+
handler: (ctx) => handleDoctorCommand(ctx.directory, ctx.args, { client: ctx.client }),
|
|
52248
52485
|
description: "Run config doctor checks",
|
|
52249
52486
|
category: "diagnostics",
|
|
52250
52487
|
aliasOf: "config doctor",
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
* Sanitizes the question to prevent prompt injection of rival MODE: headers
|
|
15
15
|
* or control sequences (mirrors brainstorm.ts).
|
|
16
16
|
*/
|
|
17
|
-
export declare function handleCouncilCommand(
|
|
17
|
+
export declare function handleCouncilCommand(directory: string, args: string[]): Promise<string>;
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { type ConfigDoctorResult } from '../services/config-doctor';
|
|
1
|
+
import { type ConfigDoctorResult, type ModelAvailability } from '../services/config-doctor';
|
|
2
2
|
/**
|
|
3
3
|
* Format tool doctor result as markdown for command output.
|
|
4
4
|
*
|
|
5
5
|
* Exported for unit testing of the BLOCKING footer enforcement path.
|
|
6
6
|
*/
|
|
7
7
|
export declare function formatToolDoctorMarkdown(result: ConfigDoctorResult): string;
|
|
8
|
+
export declare function loadModelAvailability(directory: string, client: unknown, options?: {
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
}): Promise<ModelAvailability | undefined>;
|
|
8
11
|
/**
|
|
9
12
|
* Handle /swarm config doctor command.
|
|
10
13
|
* Maps to: config doctor service (runConfigDoctor)
|
|
11
14
|
*/
|
|
12
|
-
export declare function handleDoctorCommand(directory: string, args: string[]
|
|
15
|
+
export declare function handleDoctorCommand(directory: string, args: string[], options?: {
|
|
16
|
+
client?: unknown;
|
|
17
|
+
}): Promise<string>;
|
|
13
18
|
/**
|
|
14
19
|
* Handle /swarm doctor tools command.
|
|
15
20
|
* Maps to: tool doctor service (runToolDoctor)
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -40,12 +40,13 @@ export { handleStatusCommand } from './status';
|
|
|
40
40
|
export { handleSyncPlanCommand } from './sync-plan';
|
|
41
41
|
export { handleTurboCommand } from './turbo';
|
|
42
42
|
export { handleWriteRetroCommand } from './write-retro';
|
|
43
|
+
export declare const LLM_MEDIATION_WARNING: string;
|
|
43
44
|
export declare function buildHelpText(): string;
|
|
44
45
|
/**
|
|
45
46
|
* Creates a command.execute.before handler for /swarm commands.
|
|
46
47
|
* Uses factory pattern to close over directory and agents.
|
|
47
48
|
*/
|
|
48
|
-
export declare function createSwarmCommandHandler(directory: string, agents: Record<string, AgentDefinition
|
|
49
|
+
export declare function createSwarmCommandHandler(directory: string, agents: Record<string, AgentDefinition>, client?: unknown): (input: {
|
|
49
50
|
command: string;
|
|
50
51
|
sessionID: string;
|
|
51
52
|
arguments: string;
|
|
@@ -8,6 +8,7 @@ export type CommandContext = {
|
|
|
8
8
|
args: string[];
|
|
9
9
|
sessionID: string;
|
|
10
10
|
agents: Record<string, AgentDefinition>;
|
|
11
|
+
client?: unknown;
|
|
11
12
|
};
|
|
12
13
|
export type CommandResult = Promise<string>;
|
|
13
14
|
export type CommandCategory = 'core' | 'agent' | 'config' | 'diagnostics' | 'utility';
|