新增 AGC 模型 Agent 模式与 Claude Agent SDK sidecar
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 57s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled

后台模型目录增加 codex/cc 执行模式绑定,旧目录缺省兼容 Codex

AGC 客户端按模型绑定路由 Codex app-server 或 Claude Agent SDK sidecar

随包携带 Node runtime、Claude Agent SDK 与匹配平台 Claude runtime

补充模型配置、客户端选择、运行时适配、MCP 取消和定向测试

同步 AGC 模型配置规范、里程碑计划与共享决策记录
This commit is contained in:
2026-09-30 13:49:09 +08:00
parent 5e2d47919e
commit fb130d1847
38 changed files with 3417 additions and 118 deletions
+3
View File
@@ -1075,11 +1075,14 @@ export interface AdminRechargeRefundActionResponse {
export interface AdminWalletRestrictionResponse {
wallet: AdminProfileWalletPayload;
}
export type AdminAgcAgentMode = 'codex' | 'cc';
export interface AdminAgcModel {
id: string;
alias: string;
modelId: string;
enabled: boolean;
agentMode: AdminAgcAgentMode;
}
export interface AdminAgcModelCatalog {
@@ -23,14 +23,23 @@ vi.mock('../components/useAdminWriteConfirm', () => ({
confirmDialog: null,
}),
}));
afterEach(cleanup);
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
test('edits alias and upstream model without changing the stable identifier or revision', async () => {
const catalog = {
revision: 3,
defaultModelId: 'quality',
models: [
{ id: 'quality', alias: '高质量', modelId: 'gpt-6-astra', enabled: true },
{
id: 'quality',
alias: '高质量',
modelId: 'gpt-6-astra',
enabled: true,
agentMode: 'codex' as const,
},
],
};
vi.mocked(getAgcModelCatalog).mockResolvedValue(catalog);
@@ -54,3 +63,44 @@ test('edits alias and upstream model without changing the stable identifier or r
expect(screen.getAllByText('已保存').length).toBeGreaterThanOrEqual(2);
});
});
test('new models default to Codex and save the selected Agent mode', async () => {
const catalog = {
revision: 3,
defaultModelId: 'quality',
models: [
{
id: 'quality',
alias: '高质量',
modelId: 'gpt-6-astra',
enabled: true,
agentMode: 'codex' as const,
},
],
};
vi.mocked(getAgcModelCatalog).mockResolvedValue(catalog);
vi.mocked(saveAgcModelCatalog).mockImplementation(async (_, input) => ({
...input,
revision: 4,
}));
render(<AdminAgcModelsPage token="test" onUnauthorized={vi.fn()} />);
await screen.findByDisplayValue('gpt-6-astra');
fireEvent.click(screen.getByRole('button', { name: '添加模型' }));
const agentMode = screen.getByLabelText('模型 2 Agent 模式');
expect((agentMode as HTMLSelectElement).value).toBe('codex');
fireEvent.change(agentMode, { target: { value: 'cc' } });
fireEvent.click(screen.getByRole('button', { name: '保存' }));
await waitFor(() => {
const calls = vi.mocked(saveAgcModelCatalog).mock.calls;
const payload = calls[calls.length - 1]?.[1];
expect(payload?.models).toHaveLength(2);
expect(payload?.models[1]).toMatchObject({
alias: '',
modelId: '',
enabled: true,
agentMode: 'cc',
});
});
});
@@ -116,6 +116,7 @@ export function AdminAgcModelsPage({
columns={[
{ key: 'alias', label: '别名' },
{ key: 'modelId', label: '实际模型名' },
{ key: 'agentMode', label: 'Agent 模式' },
{ key: 'enabled', label: '启用' },
{ key: 'default', label: '默认' },
{ key: 'actions', label: '操作' },
@@ -155,6 +156,7 @@ export function AdminAgcModelsPage({
alias: '',
modelId: '',
enabled: true,
agentMode: 'codex',
},
],
},
@@ -211,6 +213,21 @@ export function AdminAgcModelsPage({
}
/>
</td>
<td>
<select
aria-label={`模型 ${index + 1} Agent 模式`}
value={model.agentMode}
disabled={busy}
onChange={(e) =>
update(model.id, {
agentMode: e.target.value as AdminAgcModel['agentMode'],
})
}
>
<option value="codex">Codex</option>
<option value="cc">Claude Code</option>
</select>
</td>
<td>
<input
aria-label={`模型 ${index + 1} 启用`}