fix: add /model list alias

This commit is contained in:
Peter Steinberger
2026-01-05 14:11:33 +00:00
parent bb959684fe
commit cffbe79077
4 changed files with 31 additions and 1 deletions

View File

@@ -13,6 +13,7 @@
- macOS: drop deprecated `afterMs` from agent wait params to match gateway schema. - macOS: drop deprecated `afterMs` from agent wait params to match gateway schema.
- Auth: add OpenAI Codex OAuth support and migrate legacy oauth.json into auth.json. - Auth: add OpenAI Codex OAuth support and migrate legacy oauth.json into auth.json.
- Model: `/model` list shows auth source (masked key or OAuth email) per provider. - Model: `/model` list shows auth source (masked key or OAuth email) per provider.
- Model: `/model list` is an alias for `/model`.
- Docs: clarify auth storage, migration, and OpenAI Codex OAuth onboarding. - Docs: clarify auth storage, migration, and OpenAI Codex OAuth onboarding.
- Sandbox: copy inbound media into sandbox workspaces so agent tools can read attachments. - Sandbox: copy inbound media into sandbox workspaces so agent tools can read attachments.
- Status: show runtime (docker/direct) and move shortcuts to `/help`. - Status: show runtime (docker/direct) and move shortcuts to `/help`.

View File

@@ -556,6 +556,8 @@ Use `/model` to switch without restarting:
/model gemini-flash /model gemini-flash
``` ```
List available models with `/model`, `/model list`, or `/model status`.
Clawdbot ships a few default model shorthands (you can override them in config): Clawdbot ships a few default model shorthands (you can override them in config):
`opus`, `sonnet`, `gpt`, `gpt-mini`, `gemini`, `gemini-flash`. `opus`, `sonnet`, `gpt`, `gpt-mini`, `gemini`, `gemini-flash`.

View File

@@ -610,6 +610,32 @@ describe("directive parsing", () => {
}); });
}); });
it("lists allowlisted models on /model list", async () => {
await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset();
const storePath = path.join(home, "sessions.json");
const res = await getReplyFromConfig(
{ Body: "/model list", From: "+1222", To: "+1222" },
{},
{
agent: {
model: "anthropic/claude-opus-4-5",
workspace: path.join(home, "clawd"),
allowedModels: ["anthropic/claude-opus-4-5", "openai/gpt-4.1-mini"],
},
session: { store: storePath },
},
);
const text = Array.isArray(res) ? res[0]?.text : res?.text;
expect(text).toContain("anthropic/claude-opus-4-5");
expect(text).toContain("openai/gpt-4.1-mini");
expect(text).toContain("auth:");
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
});
});
it("sets model override on /model directive", async () => { it("sets model override on /model directive", async () => {
await withTempHome(async (home) => { await withTempHome(async (home) => {
vi.mocked(runEmbeddedPiAgent).mockReset(); vi.mocked(runEmbeddedPiAgent).mockReset();

View File

@@ -234,8 +234,9 @@ export async function handleDirectiveOnly(params: {
} = params; } = params;
if (directives.hasModelDirective) { if (directives.hasModelDirective) {
const modelDirective = directives.rawModelDirective?.trim().toLowerCase();
const isModelListAlias = const isModelListAlias =
directives.rawModelDirective?.trim().toLowerCase() === "status"; modelDirective === "status" || modelDirective === "list";
if (!directives.rawModelDirective || isModelListAlias) { if (!directives.rawModelDirective || isModelListAlias) {
if (allowedModelCatalog.length === 0) { if (allowedModelCatalog.length === 0) {
return { text: "No models available." }; return { text: "No models available." };