diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts index 5cf3ab51ad9..95dffebac31 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts @@ -95,6 +95,20 @@ const defaultCapabilityRunnerdBinary = () => process.env.PAPERCLIP_ATTACH_TRANSITION_RUNNER ?? qualifiedCapabilityRunnerdBinary(); +it("attributes opened candidate sessions to the harness biller instead of the model vendor", () => { + const provider = runnerdLaunchProfileInternals.openedThreadModelProvider; + for (const model of ["gpt-5.6-luna[context=272k,reasoning=medium,fast=false]", "claude-sonnet-5"]) { + expect(provider("acpx", "cursor", model)).toBe("cursor"); + expect(provider("acpx", "copilot", model)).toBe("github"); + } + expect(provider("acpx", "pi", "openrouter/deepseek/deepseek-v4-flash-0731")).toBe("openrouter"); + expect(provider("acpx", "claude", "claude-sonnet-5")).toBe("anthropic"); + expect(provider("acpx", "codex", "gpt-5.6-sol")).toBe("openai"); + expect(provider("opencode", "codex", "openrouter/openai/gpt-6-astra")).toBe("openrouter"); + expect(provider("claude_managed", "codex", "claude-sonnet-5")).toBe("anthropic"); + expect(provider("aws_agentcore", "codex", "global.anthropic.claude-sonnet-4-6")).toBe("aws"); +}); + async function expectTurnStarted( notifications: AsyncIterator<{ method: string }>, ) { diff --git a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts index ca2a56ed3d2..4323214bdb0 100644 --- a/packages/paperclip-runner/src/live/runnerd-codex-transport.ts +++ b/packages/paperclip-runner/src/live/runnerd-codex-transport.ts @@ -4763,20 +4763,7 @@ class DurablePrpCodexTransport implements CodexAppServerTransport { ? {} : { providerIdentity: structuredClone(this.#providerIdentity) }), model: params.model, - modelProvider: - provider === "opencode" && typeof params.model === "string" - ? params.model.split("/", 1)[0] - : provider === "claude_managed" - ? "anthropic" - : provider === "aws_agentcore" - ? "aws" - : provider === "acpx" - ? acpxAgent === "pi" - ? "openrouter" - : acpxAgent === "claude" - ? "anthropic" - : "openai" - : "openai", + modelProvider: openedThreadModelProvider(provider, acpxAgent, params.model), }, }; } @@ -6673,7 +6660,26 @@ export function createCapabilityRunnerdCodexTransport( export const createRunnerdCodexTransport = createCapabilityRunnerdCodexTransport; +/** The transport provider identifies the biller, independently of model family. */ +function openedThreadModelProvider( + provider: CapabilityRunnerdCodexTransportOptions["provider"], + acpxAgent: QualifiedAcpxAgent, + model: unknown, +): string { + if (provider === "opencode" && typeof model === "string") return model.split("/", 1)[0]!; + if (provider === "claude_managed") return "anthropic"; + if (provider === "aws_agentcore") return "aws"; + if (provider === "acpx") { + if (acpxAgent === "pi") return "openrouter"; + if (acpxAgent === "claude") return "anthropic"; + if (acpxAgent === "cursor") return "cursor"; + if (acpxAgent === "copilot") return "github"; + } + return "openai"; +} + export const runnerdLaunchProfileInternals = Object.freeze({ + openedThreadModelProvider, acpxProviderPackageAuthority, acpxRunnerLaunchProfile, resolveBuildOwnedCliArtifact,