feat: update webui extensions, models, and agent config
Some checks failed
CI / build-check-test (push) Has been cancelled
npm audit / audit (push) Has been cancelled

This commit is contained in:
2026-06-10 21:45:53 +08:00
parent cf5edd6394
commit d51a055d78
64 changed files with 3238 additions and 1665 deletions

View File

@@ -125,6 +125,21 @@ export const IMAGE_MODELS = {
cacheWrite: 0,
},
} satisfies ImagesModel<"openrouter-images">,
"microsoft/mai-image-2.5": {
id: "microsoft/mai-image-2.5",
name: "Microsoft: MAI-Image-2.5",
api: "openrouter-images",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
input: ["text", "image"],
output: ["image"],
cost: {
input: 5,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
} satisfies ImagesModel<"openrouter-images">,
"openai/gpt-5-image": {
id: "openai/gpt-5-image",
name: "OpenAI: GPT-5 Image",
@@ -425,6 +440,36 @@ export const IMAGE_MODELS = {
cacheWrite: 0,
},
} satisfies ImagesModel<"openrouter-images">,
"sourceful/riverflow-v2.5-fast": {
id: "sourceful/riverflow-v2.5-fast",
name: "Sourceful: Riverflow V2.5 Fast",
api: "openrouter-images",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
input: ["text", "image"],
output: ["image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
} satisfies ImagesModel<"openrouter-images">,
"sourceful/riverflow-v2.5-pro": {
id: "sourceful/riverflow-v2.5-pro",
name: "Sourceful: Riverflow V2.5 Pro",
api: "openrouter-images",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
input: ["text", "image"],
output: ["image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
} satisfies ImagesModel<"openrouter-images">,
"x-ai/grok-imagine-image-quality": {
id: "x-ai/grok-imagine-image-quality",
name: "xAI: Grok Imagine Image Quality",

File diff suppressed because it is too large Load Diff

View File

@@ -126,9 +126,9 @@ describe("Context overflow error handling", () => {
describe("GitHub Copilot (OAuth)", () => {
// OpenAI model via Copilot
it.skipIf(!githubCopilotToken)(
"gpt-4o - should detect overflow via isContextOverflow",
"claude-sonnet-4.6 - should detect overflow via isContextOverflow",
async () => {
const model = getModel("github-copilot", "gpt-4o");
const model = getModel("github-copilot", "claude-sonnet-4.6");
const result = await testContextOverflow(model, githubCopilotToken!);
logResult(result);
@@ -297,8 +297,8 @@ describe("Context overflow error handling", () => {
// =============================================================================
describe.skipIf(!process.env.CEREBRAS_API_KEY)("Cerebras", () => {
it("qwen-3-235b - should detect overflow via isContextOverflow", async () => {
const model = getModel("cerebras", "qwen-3-235b-a22b-instruct-2507");
it("gpt-oss-120b - should detect overflow via isContextOverflow", async () => {
const model = getModel("cerebras", "gpt-oss-120b");
const result = await testContextOverflow(model, process.env.CEREBRAS_API_KEY!);
logResult(result);

View File

@@ -630,37 +630,37 @@ describe("AI Providers Empty Message Tests", () => {
describe("GitHub Copilot Provider Empty Messages", () => {
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle empty content array",
"claude-sonnet-4.6 - should handle empty content array",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testEmptyMessage(llm, { apiKey: githubCopilotToken });
},
);
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle empty string content",
"claude-sonnet-4.6 - should handle empty string content",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testEmptyStringMessage(llm, { apiKey: githubCopilotToken });
},
);
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle whitespace-only content",
"claude-sonnet-4.6 - should handle whitespace-only content",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testWhitespaceOnlyMessage(llm, { apiKey: githubCopilotToken });
},
);
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle empty assistant message in conversation",
"claude-sonnet-4.6 - should handle empty assistant message in conversation",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testEmptyAssistantMessage(llm, { apiKey: githubCopilotToken });
},
);

View File

@@ -38,7 +38,7 @@ describe("Fireworks models", () => {
});
it("registers the Fire Pass turbo router model", () => {
const model = getModel("fireworks", "accounts/fireworks/routers/kimi-k2p5-turbo");
const model = getModel("fireworks", "accounts/fireworks/routers/kimi-k2p6-turbo");
expect(model).toBeDefined();
expect(model.api).toBe("anthropic-messages");

View File

@@ -443,19 +443,19 @@ describe("Tool Results with Images", () => {
describe("GitHub Copilot Provider", () => {
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle tool result with only image",
"claude-sonnet-4.6 - should handle tool result with only image",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await handleToolWithImageResult(llm, { apiKey: githubCopilotToken });
},
);
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle tool result with text and image",
"claude-sonnet-4.6 - should handle tool result with text and image",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await handleToolWithTextAndImageResult(llm, { apiKey: githubCopilotToken });
},
);

View File

@@ -149,7 +149,7 @@ describe("Token Statistics on Abort", () => {
});
describe.skipIf(!process.env.CEREBRAS_API_KEY)("Cerebras Provider", () => {
const llm = getModel("cerebras", "qwen-3-235b-a22b-instruct-2507");
const llm = getModel("cerebras", "gpt-oss-120b");
it("should include token stats when aborted mid-stream", { retry: 3, timeout: 30000 }, async () => {
await testTokensOnAbort(llm);
@@ -289,10 +289,10 @@ describe("Token Statistics on Abort", () => {
describe("GitHub Copilot Provider", () => {
it.skipIf(!githubCopilotToken)(
"gpt-4o - should include token stats when aborted mid-stream",
"claude-sonnet-4.6 - should include token stats when aborted mid-stream",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testTokensOnAbort(llm, { apiKey: githubCopilotToken });
},
);

View File

@@ -297,10 +297,10 @@ describe("Tool Call Without Result Tests", () => {
describe("GitHub Copilot Provider", () => {
it.skipIf(!githubCopilotToken)(
"gpt-4o - should filter out tool calls without corresponding tool results",
"claude-sonnet-4.6 - should filter out tool calls without corresponding tool results",
{ retry: 3, timeout: 30000 },
async () => {
const model = getModel("github-copilot", "gpt-4o");
const model = getModel("github-copilot", "claude-sonnet-4.6");
await testToolCallWithoutResult(model, { apiKey: githubCopilotToken });
},
);

View File

@@ -666,10 +666,10 @@ describe("totalTokens field", () => {
);
it(
"google/gemini-2.0-flash-001 - should return totalTokens equal to sum of components",
"google/gemini-2.5-flash - should return totalTokens equal to sum of components",
{ retry: 3, timeout: 60000 },
async () => {
const llm = getModel("openrouter", "google/gemini-2.0-flash-001");
const llm = getModel("openrouter", "google/gemini-2.5-flash");
console.log(`\nOpenRouter / ${llm.id}:`);
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.OPENROUTER_API_KEY });
@@ -706,10 +706,10 @@ describe("totalTokens field", () => {
describe("GitHub Copilot (OAuth)", () => {
it.skipIf(!githubCopilotToken)(
"gpt-4o - should return totalTokens equal to sum of components",
"claude-sonnet-4.6 - should return totalTokens equal to sum of components",
{ retry: 3, timeout: 60000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
console.log(`\nGitHub Copilot / ${llm.id}:`);
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: githubCopilotToken });

View File

@@ -396,28 +396,28 @@ describe("AI Providers Unicode Surrogate Pair Tests", () => {
describe("GitHub Copilot Provider Unicode Handling", () => {
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle emoji in tool results",
"claude-sonnet-4.6 - should handle emoji in tool results",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testEmojiInToolResults(llm, { apiKey: githubCopilotToken });
},
);
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle real-world LinkedIn comment data with emoji",
"claude-sonnet-4.6 - should handle real-world LinkedIn comment data with emoji",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testRealWorldLinkedInData(llm, { apiKey: githubCopilotToken });
},
);
it.skipIf(!githubCopilotToken)(
"gpt-4o - should handle unpaired high surrogate (0xD83D) in tool results",
"claude-sonnet-4.6 - should handle unpaired high surrogate (0xD83D) in tool results",
{ retry: 3, timeout: 30000 },
async () => {
const llm = getModel("github-copilot", "gpt-4o");
const llm = getModel("github-copilot", "claude-sonnet-4.6");
await testUnpairedHighSurrogate(llm, { apiKey: githubCopilotToken });
},
);

View File

@@ -426,7 +426,15 @@ Response:
Execute a shell command and add output to conversation context.
```json
{"type": "bash", "command": "ls -la"}
{"type": "bash", "command": "ls -la", "excludeFromContext": false}
```
Optional `excludeFromContext` (default `false`): when `true`, the command output is shown in the UI but excluded from the LLM context on the next prompt (equivalent to `!!cmd` in the TUI).
During execution, `bash_update` events stream partial stdout:
```json
{"type": "bash_update", "command": "ls -la", "partialOutput": "total 48\n"}
```
Response:

View File

@@ -1321,8 +1321,7 @@
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
},
"peer": true
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
@@ -1775,8 +1774,7 @@
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
},
"peer": true
}
},
"node_modules/zod-to-json-schema": {
"version": "3.25.2",

View File

@@ -546,7 +546,15 @@ export async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<neve
// =================================================================
case "bash": {
const result = await session.executeBash(command.command);
let partialOutput = "";
const result = await session.executeBash(
command.command,
(chunk) => {
partialOutput += chunk;
output({ type: "bash_update", command: command.command, partialOutput });
},
{ excludeFromContext: command.excludeFromContext },
);
return success(id, "bash", result);
}

View File

@@ -50,7 +50,7 @@ export type RpcCommand =
| { id?: string; type: "abort_retry" }
// Bash
| { id?: string; type: "bash"; command: string }
| { id?: string; type: "bash"; command: string; excludeFromContext?: boolean }
| { id?: string; type: "abort_bash" }
// Session