From 9f6ec0ead6e21f368c8ff08d048ae2f73505f326 Mon Sep 17 00:00:00 2001 From: pku-xht Date: Mon, 10 Aug 2026 13:34:10 +0800 Subject: [PATCH] test(subagent): exercise native Windows Claude shim --- ...code-and-codex-subagent-backends.i18n.yaml | 4 +-- ...claude-code-and-codex-subagent-backends.md | 4 --- ...ude-code-and-codex-subagent-backends.zh.md | 4 --- apps/cli/tests/web-agent-presets.e2e.ts | 18 ------------- .../tests/real-product.spec.ts | 25 +++++++++++-------- 5 files changed, 16 insertions(+), 39 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml index 2a66f8d5e4..8c39ed7525 100644 --- a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md -2026-08-04-claude-code-and-codex-subagent-backends.md: 783a8a6537d2b05ab99f1962bda88787e2e4e938 -2026-08-04-claude-code-and-codex-subagent-backends.zh.md: f331dc698001e24df62733d219f28b59d2358fac +2026-08-04-claude-code-and-codex-subagent-backends.md: eb4c4ab0116cdf1e8b9e6dd53655035a78afdfa0 +2026-08-04-claude-code-and-codex-subagent-backends.zh.md: c14b7977a267c2b7325e4a4382593c8f30dddea4 diff --git a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md index 783a8a6537..eb4c4ab011 100644 --- a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md +++ b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md @@ -79,10 +79,6 @@ The project owner's distribution authorization is scoped to the official `@anthr **A model-visible product selector.** Product availability and authentication are deployment facts. Two fixed tools keep each schema and provider binding explicit and avoid adding dynamic selection state to the common service. -**Global product enable settings and a product-specific Web page.** Those controls make Codex and Claude Code exceptions to the Agent Preset composition that already owns one agent's tool set, and one process-wide choice cannot represent two sessions using different presets. The host always supplies the providers; the preset alone decides which fixed tools its agent receives. - -**One shipped preset per product combination.** Four preset variants encode a two-boolean choice in preset identities and multiply every future standard-preset change. Independent ordinary rows express the same result in the user's copied preset without adding a roster taxonomy. - **Product doubles as required evidence.** Doubles cover exhaustive private protocol branches but do not prove package exports, official distributions, authentication, or real process behavior. Required evidence drives each official product against a loopback model fixture. **Plugin-managed login, product home, models, settings, or permissions.** Those choices would create another authority beside each product's native configuration and enlarge a one-shot provider into account management. The providers expose only an explicit environment overlay and teardown grace; unattended interaction fails closed. diff --git a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md index f331dc6980..c14b7977a2 100644 --- a/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md +++ b/.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md @@ -79,10 +79,6 @@ Claude Code 证据锁定 Agent SDK 0.3.220 及与其查询协议兼容的原生 **面向模型的产品选择器。** 产品可用性和身份验证属于部署事实。两个固定工具使各自的 schema 与提供方绑定保持明确,也避免在通用服务中添加动态选择状态。 -**全局产品启用设置与产品专属 Web 页面。** 这类控制会让 Codex 与 Claude Code 成为 Agent Preset 组装的例外,而后者本就拥有单个 agent 的工具集;一个进程级选择也无法表达两个会话使用不同 preset。宿主始终提供 provider,只有 preset 决定其 agent 获得哪些固定工具。 - -**为每种产品组合交付一份 preset。** 四个 preset 变体把两个布尔选择编码成 preset 身份,并让未来每次标准 preset 修改都要同步多份副本。用户复制的 preset 中两条独立普通行已经能表达同一结果,无需新增名单分类。 - **以产品替身作为强制证据。** 替身可以穷尽覆盖私有协议分支,但无法证明包导出、官方发行版、身份验证或真实进程行为。强制证据会驱动每个官方产品连接回环模型 fixture(测试前置数据)。 **由插件管理登录、产品主目录、模型、设置或权限。** 这些选择会在每个产品的原生配置之外建立另一套权威来源,并将一次性提供方扩张为账户管理功能。提供方只公开显式环境覆盖项和清理宽限期;无人值守交互会以默认拒绝方式失败。 diff --git a/apps/cli/tests/web-agent-presets.e2e.ts b/apps/cli/tests/web-agent-presets.e2e.ts index 8f54d6ddf6..4e0dcc3a1d 100644 --- a/apps/cli/tests/web-agent-presets.e2e.ts +++ b/apps/cli/tests/web-agent-presets.e2e.ts @@ -132,24 +132,6 @@ describe('the shipped Web composition', () => { expect(ctx.agentPresets.defaultId).toBe('standard') }) - it('keeps product providers on the host while shipped presets leave their tools disabled', async () => { - expect(ctx.subagents.list()).toEqual(expect.arrayContaining([ - 'spawn', 'fork', 'codex', 'claude-code', - ])) - - const handle = await ctx.agents.create({ - sessionId: SessionId('preset-products-disabled'), - setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'standard').then(() => undefined), - }) - try { - expect(toolNames(ctx, handle.agent)).not.toEqual(expect.arrayContaining([ - 'subagent_codex', 'subagent_claude_code', - ])) - } finally { - await handle.dispose() - } - }) - it('composes the full agent from `standard`', async () => { const handle = await ctx.agents.create({ sessionId: SessionId('preset-standard'), diff --git a/packages/subagent/subagent-claude-code/tests/real-product.spec.ts b/packages/subagent/subagent-claude-code/tests/real-product.spec.ts index 6048c930de..d7555b9ed8 100644 --- a/packages/subagent/subagent-claude-code/tests/real-product.spec.ts +++ b/packages/subagent/subagent-claude-code/tests/real-product.spec.ts @@ -1,6 +1,5 @@ import { execFile } from 'node:child_process' import { - copyFileSync, mkdirSync, mkdtempSync, readFileSync, @@ -116,14 +115,17 @@ async function realHarness(behavior: MessagesBehavior): Promise<{ const workspace = join(root, 'workspace') const claudeConfig = join(root, 'claude-config') const xdgConfig = join(root, 'xdg') - const nativeBin = join(root, 'native-bin') + const nativeBin = join(root, 'native bin') mkdirSync(workspace) mkdirSync(claudeConfig) mkdirSync(xdgConfig) mkdirSync(nativeBin) - const executable = join(nativeBin, process.platform === 'win32' ? 'claude.exe' : 'claude') - if (process.platform === 'win32') copyFileSync(claudeBin, executable) - else symlinkSync(claudeBin, executable) + const executable = join(nativeBin, process.platform === 'win32' ? 'claude.cmd' : 'claude') + if (process.platform === 'win32') { + writeFileSync(executable, `@echo off\r\n"${claudeBin}" %*\r\n`) + } else { + symlinkSync(claudeBin, executable) + } writeFileSync( join(claudeConfig, 'settings.json'), `${JSON.stringify({ model: settingsModel }, null, 2)}\n`, @@ -207,7 +209,7 @@ describe('real Claude Agent SDK 0.3.220 and Claude Code 2.1.220', { expect(sdkPackage.version).toBe('0.3.220') expect(sdkPackage.claudeCodeVersion).toBe('2.1.220') expect(sdkPackage.optionalDependencies[platformPackage]).toBe('0.3.220') - const version = await execFileAsync(harness.executable, ['--version'], { + const version = await execFileAsync(process.platform === 'win32' ? claudeBin : harness.executable, ['--version'], { env: { ...process.env, ...harness.env }, }) expect(version.stdout.trim()).toBe('2.1.220 (Claude Code)') @@ -224,11 +226,12 @@ describe('real Claude Agent SDK 0.3.220 and Claude Code 2.1.220', { message.type === 'system' && message.subtype === 'init', ) expect(initMessage?.claude_code_version).toBe('2.1.220') - const spawnedExecutable = harness.spawnSpecs[0]?.argv[0] - expect(spawnedExecutable).toBeDefined() - if (spawnedExecutable !== undefined) { - expect(process.platform === 'win32' ? spawnedExecutable.toLowerCase() : spawnedExecutable) - .toBe(process.platform === 'win32' ? harness.executable.toLowerCase() : harness.executable) + if (process.platform === 'win32') { + expect(harness.spawnSpecs[0]?.argv.slice(0, 5)).toEqual([ + 'cmd.exe', '/d', '/s', '/c', harness.executable, + ]) + } else { + expect(harness.spawnSpecs[0]?.argv[0]).toBe(harness.executable) } expect(fixture.requests).toHaveLength(1)