Add a Provider or Model
Version: wb-flow 1.0.7
A model name in
models.jsonis not enough. A usable model has three parts: a stable catalog ID, a billing/rate-limit pool, and a CLI adapter that can run it headlessly. If any one is missing, the orchestrator cannot safely delegate a wave cell to it.
This guide has two paths:
| Your change | Path | What changes |
|---|---|---|
| Add or refresh models for an already supported provider | CLI catalog path | Your active models.json and roster only |
| Add a provider with a new or dedicated CLI | Manual provider path | wb-flow 1.0.7 source, tests, shipped catalog seed, and provider documentation |
wb-flow model deliberately cannot invent a new CLI adapter. Discovering a credential is not permission to spend against it, and an unknown adapter might send a namespaced ID to the wrong binary. Complete Path B once for a new provider; after that, operators can use Path A for its models.
The provider contract
Collect this information before changing anything. Do not infer model IDs from a subscription name.
| Field | Example | Why it is required |
|---|---|---|
| Provider key | acmecloud | Stable key in models.json and the dispatch registry |
| Pool key | acme-team-plan | A billing and rate-limit boundary used to build independent fallbacks |
| Native CLI | acme | The executable launched by a delegated cell |
| Canonical model slug | acmecloud/acme-coder-v2 | The catalog and roster identity |
| CLI model argument | acme-coder-v2 | Many vendor CLIs require the bare ID, not the catalog slug |
| Headless command | acme run --model acme-coder-v2 --prompt "…" | The exact command wb-flow must generate |
| Authentication and region | vendor login/profile/base URL | Prevents a valid CLI from using the wrong account or endpoint |
| Listing capability | acme models list, or “not enumerable” | Determines whether catalog sync can discover models automatically |
| Permission and stdin behavior | non-interactive flag, /dev/null need | Prevents a delegated wave from blocking for approval or input |
Never put API keys, cookies, or account IDs in models.json, a roster, a plan, or a generated wave script. Authenticate the vendor CLI using its normal secure login/profile mechanism.
Path A: CLI catalog path
Use this when the provider's dispatch adapter already ships with wb-flow 1.0.7. It is the normal way to pick up a new model release without changing package source.
1. Confirm the local CLI and account
Run the vendor's own non-writing, one-prompt command first. It must prove the exact model and account that the orchestrator will use:
<vendor-cli> --version
<vendor-cli> <headless-model-and-prompt-flags> "Reply exactly: MODEL_OK"Record the model's actual catalog slug and pool. A successful interactive chat is not enough: delegated wave cells are non-interactive processes.
2. Preview catalog changes
--sync-catalog enumerates only providers already present in the catalog. It does not make a credentialed provider active merely because a CLI reports it.
wb-flow model --sync-catalog --dry-run
wb-flow model --add=<supported-provider> --dry-runUse --add to scope the refresh to one registered provider. Review additions, retirement marks, and the reported billing pool before removing --dry-run:
wb-flow model --add=<supported-provider>For a provider whose CLI cannot list models, use its documented picker/import route instead of guessing. For example, wb-flow supports importing a reviewed picker transcript with --from-picker or --from-file; only use that with model IDs copied from the vendor CLI.
3. Prove reachability, then assign roles
Catalogued means “known”; it does not mean “your plan can run it.” Probes make real provider calls.
wb-flow model --probe --all=<supported-provider>
wb-flow model --set worker=<provider>/<model-id>
wb-flow model --set mechanical=<provider>/<model-id>
wb-flow wave <plan.md> --wave=A --listUse --list before dispatching work. It shows the exact roster file and generated lane, without spending a model call. Keep a Validator fallback in a different pool from the Worker that edited the files.
Path B: manual provider integration
Use this for a new provider, especially when it brings its own CLI. This is a source change to wb-flow 1.0.7, not an end-user catalog command.
1. Validate the vendor CLI outside wb-flow
Verify the command shape from a clean terminal:
<vendor-cli> --version
<vendor-cli> --help
<vendor-cli> <non-interactive-flags> <model-flag> <bare-model-id> <prompt-flag> "Reply exactly: MODEL_OK"Test both approval modes if the CLI supports a sandboxed mode. Confirm whether it reads stdin even when it receives a prompt: a CLI that does must be redirected from /dev/null in generated scripts.
2. Define the shipped catalog seed
Add the provider group to core/templates/models.json:
{
"provider": "acmecloud",
"pool": "acme-team-plan",
"cli": "acme",
"models": ["acmecloud/acme-coder-v2"]
}The namespaced slug is wb-flow's stable identity. The adapter decides whether the vendor CLI receives that full slug or only acme-coder-v2. The template is the seed for new installs; an existing user catalog is resolved from WB_MODELS_FILE, .wb/models.json, ~/.wb/models.json, or ~/.config/wb-flow/models.json.
3. Add the dispatch adapter
Update core/bin/cli_registry.js, the single source of truth shared by model probing and wave dispatch:
- Add
acmecloud: 'acme'toPROVIDER_CLI. - Add an
acmeentry toCLI_SPECwith its executable, non-interactive argument builder, prompt form, permission behavior, andstdinNullrequirement. - Add
acmetoBARE_MODEL_CLISif the vendor accepts only bare model IDs. - Set
slashCommands: falseunless the CLI really expands/wbWork-style commands. For a false value, wb-flow embeds the command template in the generated prompt.
Do not rely on the fallback-to-OpenCode behavior for a dedicated CLI. It is appropriate only when OpenCode itself is the provider's supported execution lane.
3a. Implement the command syntax explicitly
models.json records what a model is. cli_registry.js records how to invoke it. wb-flow never guesses a vendor's flag order, approval mode, prompt form, or whether the CLI accepts a bare model ID. Copy this shape and replace every placeholder only after verifying it against the installed vendor CLI:
// 1. In PROVIDER_CLI: provider key from models.json → CLI_SPEC key.
const PROVIDER_CLI = {
// existing providers …
acmecloud: 'acme',
};
// 2. In CLI_SPEC: the exact headless invocation contract.
const CLI_SPEC = {
// existing CLI specifications …
acme: {
bin: 'acme',
slashCommands: false, // functional: false makes wb-flow inline the /wb* template
inlineTemplate: true, // optional explanatory metadata; do not rely on it for behavior
stdinNull: true, // set only when the CLI would otherwise consume script stdin
argv: function (model, prompt, opts) {
const a = [];
if (model) a.push('--model', model);
// Replace with the verified flags. A sandboxed route must never retain a
// non-sandbox permission bypass by accident.
if (opts && opts.sandbox) {
a.push('--sandbox', '--approval-mode=auto');
} else {
a.push('--approval-mode=yolo');
}
if (prompt != null) a.push(prompt); // positional prompt in this example
return a;
},
},
};
// 3. Add the CLI here only when it accepts a bare model ID.
const BARE_MODEL_CLIS = new Set([
'claude', 'codex', 'agy', 'gemini', 'copilot', 'grok', 'acme',
]);With a catalog ID of acmecloud/acme-coder-v2, this adapter receives model === 'acme-coder-v2' because acme is in BARE_MODEL_CLIS. Leave it out of that set when the CLI expects the full namespaced slug instead.
The generated Worker command is then conceptually:
acme --model acme-coder-v2 --approval-mode=yolo \
"Read <wb-flow command template> and execute the procedure described there. Arguments: …" \
< /dev/nullThe health-check command and the delegated-task command are intentionally different. A probe can use --approval-mode=plan --max-tool-calls=0 because it only asks for text. A Worker must be allowed to read and edit scoped files, so it needs the provider's reviewed non-interactive execution mode instead. Do not put --max-tool-calls=0 into the production adapter.
Qwen Code adapter — shipped in wb-flow 1.0.7
Qwen Code 0.24.4 exposes qwen, -m/--model, a positional prompt, --sandbox, and --approval-mode. wb-flow 1.0.7 registers qwencloud → qwen with this adapter:
// PROVIDER_CLI
qwencloud: 'qwen',
// CLI_SPEC
qwen: {
bin: 'qwen',
slashCommands: false,
inlineTemplate: true,
stdinNull: true,
argv: function (model, prompt, opts) {
const a = [];
if (model) a.push('-m', model);
if (opts && opts.probe) a.push('--approval-mode=plan', '--max-tool-calls=0');
else if (opts && opts.sandbox) a.push('--sandbox', '--approval-mode=auto');
else a.push('--approval-mode=yolo');
if (prompt != null) a.push(prompt);
return a;
},
},
// BARE_MODEL_CLIS
// add 'qwen' to the Set initializerQwen Cloud ships with reviewed seed IDs under the alibaba-token-plan pool. Qwen has no stable non-interactive model-list command, so wb-flow model --sync-catalog preserves those IDs rather than inventing a list. Confirm the IDs your account serves with a real, no-tool probe before using one in a roster:
# Required once after upgrading an existing catalog created before Qwen support.
wb-flow model --add=qwencloud
# Makes real, no-tool provider calls; only keep models your Token Plan answers with.
wb-flow model --probe --all=qwencloud4. Teach catalog discovery what is true
Update core/bin/model.js and core/bin/catalog_sync.js:
- detect the installed CLI and its authenticated state;
- enumerate models only when the CLI has a stable, non-interactive list command;
- otherwise mark the provider non-enumerable and require explicit reviewed IDs or a safe picker import;
- add the pool to ranking when it is a supported subscription pool;
- add role-pattern preferences only when their behavior is deliberate, not merely because a model name contains a familiar word.
Never invent an enumeration command. A stale curated list is safer when paired with a real probe; an invented list creates a catalog that fails only after a wave has started.
5. Document the integration
Add the provider's authentication prerequisites, region/profile requirements, exact verified command shapes, canonical IDs, and known limitations to core/templates/commands/model_reference_manual.md. The page you are reading explains the generic contract; that reference manual is where users find provider-specific operational commands.
6. Test the adapter before publishing
At minimum, extend core/test/model_routing.js to prove that:
acmecloud/acme-coder-v2resolves toacme, neveropencode;- the generated argument vector uses the expected bare or namespaced ID;
- headless permission and stdin handling are present;
- sandbox behavior does not silently enable a bypass flag;
- the provider pool is preserved for fallback and cross-provider validation.
Then run the package test suite and preview the actual generated wave command:
cd deployement/packages/wb-flow/next/core
npm test
wb-flow wave <plan.md> --wave=A --printRead the generated command before a live wave. The essential check is that it launches the intended binary with the intended model ID and inline template behavior.
Example: applying the contract to Qwen Cloud
Qwen Cloud demonstrates the completed manual-provider path: wb-flow 1.0.7 now has the dedicated qwencloud → qwen adapter, a tested bare-model argument shape, the alibaba-token-plan pool, and a non-enumerable catalog policy. Your Token Plan still determines which seeded IDs are entitled, so probe the selected model before assigning it to a production roster.
Annex — providers registered in wb-flow 1.0.7
This table reflects core/bin/cli_registry.js, which is the dispatch authority. The cli field in a catalog group is descriptive; a route is selected through PROVIDER_CLI and built by CLI_SPEC.
| Provider key | Adapter / executable | Model argument | /wb* command expansion |
|---|---|---|---|
anthropic | claude | bare model ID | yes |
openai | codex | bare model ID | no — template is inlined |
xai | grok | bare model ID | yes |
antigravity | agy | bare model ID | no — template is inlined |
google | gemini | bare model ID | no — template is inlined |
github-copilot | copilot | bare model ID | no — template is inlined |
openrouter | opencode | full namespaced slug | yes |
opencode-go | opencode | full namespaced slug | yes |
opencode-zen | opencode | full namespaced slug | yes |
qwencloud | qwen | bare model ID | no — template is inlined |
qwencloud is non-enumerable by design: a Qwen model is not added merely because a subscription exists. The local qwen CLI must be authenticated, and the selected model must pass a real probe.
Release checklist
- [ ] Vendor CLI command succeeds non-interactively with the target account and model.
- [ ] Provider, pool, canonical slug, and CLI model argument are recorded.
- [ ]
models.jsonseed and provider manual are updated. - [ ]
cli_registry.jsresolves the provider to the intended binary. - [ ] Discovery is implemented or explicitly marked non-enumerable.
- [ ] Routing, pool, permission, and stdin behavior have regression tests.
- [ ]
npm testpasses andwb-flow wave --printshows the intended invocation. - [ ] A model probe succeeds before the provider enters a production roster.
← Concepts Hub · Model Fallback Chains · Model Picker Tutorial
