diff --git a/packages/agent/src/server/agent-server.test.ts b/packages/agent/src/server/agent-server.test.ts index efa55a17b..32c60f5f3 100644 --- a/packages/agent/src/server/agent-server.test.ts +++ b/packages/agent/src/server/agent-server.test.ts @@ -213,6 +213,9 @@ interface TestableServer { ): string | { append: string }; buildCodexInstructions(systemPrompt: string | { append: string }): string; getRuntimeAdapter(): "claude" | "codex"; + buildClaudeCodeSessionMeta( + runtimeAdapter: "claude" | "codex", + ): { claudeCode: { options: Record } } | undefined; } let nextTestPort = 20000; @@ -1140,6 +1143,68 @@ describe("AgentServer HTTP Mode", () => { }); }); + describe("buildClaudeCodeSessionMeta", () => { + it("sends claude reasoning effort even when no plugins are configured", () => { + const s = createServer({ reasoningEffort: "high" }); + + const meta = (s as unknown as TestableServer).buildClaudeCodeSessionMeta( + "claude", + ); + + expect(meta?.claudeCode.options).toEqual({ effort: "high" }); + }); + + it("does not send claudeCode effort for codex runs", () => { + const s = createServer({ reasoningEffort: "high" }); + + const meta = (s as unknown as TestableServer).buildClaudeCodeSessionMeta( + "codex", + ); + + expect(meta).toBeUndefined(); + }); + + it("returns undefined when neither plugins nor effort are set", () => { + const s = createServer(); + + const meta = (s as unknown as TestableServer).buildClaudeCodeSessionMeta( + "claude", + ); + + expect(meta).toBeUndefined(); + }); + + it("includes both plugins and effort for claude runs", () => { + const s = createServer({ + reasoningEffort: "medium", + claudeCode: { plugins: [{ type: "local", path: "/tmp/plugin" }] }, + }); + + const meta = (s as unknown as TestableServer).buildClaudeCodeSessionMeta( + "claude", + ); + + expect(meta?.claudeCode.options).toEqual({ + plugins: [{ type: "local", path: "/tmp/plugin" }], + effort: "medium", + }); + }); + + it("returns only plugins when effort is not set", () => { + const s = createServer({ + claudeCode: { plugins: [{ type: "local", path: "/tmp/plugin" }] }, + }); + + const meta = (s as unknown as TestableServer).buildClaudeCodeSessionMeta( + "claude", + ); + + expect(meta?.claudeCode.options).toEqual({ + plugins: [{ type: "local", path: "/tmp/plugin" }], + }); + }); + }); + describe("detectedPrUrl tracking", () => { it("stores PR URL when gh pr create produces it", () => { const s = createServer(); diff --git a/packages/agent/src/server/agent-server.ts b/packages/agent/src/server/agent-server.ts index 4d164b080..2b1a57a20 100644 --- a/packages/agent/src/server/agent-server.ts +++ b/packages/agent/src/server/agent-server.ts @@ -1067,19 +1067,7 @@ export class AgentServer { jsonSchema: preTask?.json_schema ?? null, permissionMode: initialPermissionMode, ...(this.config.baseBranch && { baseBranch: this.config.baseBranch }), - ...(this.config.claudeCode?.plugins?.length && { - claudeCode: { - options: { - ...(this.config.claudeCode?.plugins?.length && { - plugins: this.config.claudeCode.plugins, - }), - ...(runtimeAdapter === "claude" && - this.config.reasoningEffort && { - effort: this.config.reasoningEffort, - }), - }, - }, - }), + ...this.buildClaudeCodeSessionMeta(runtimeAdapter), }, }); @@ -1670,6 +1658,32 @@ export class AgentServer { : systemPrompt.append; } + /** + * Builds the optional `claudeCode` session meta. Reasoning effort and plugins + * are independent: effort must reach Claude even when no plugins are set, so + * it cannot sit behind a plugins guard. + */ + private buildClaudeCodeSessionMeta( + runtimeAdapter: "claude" | "codex", + ): { claudeCode: { options: Record } } | undefined { + const plugins = this.config.claudeCode?.plugins; + const effort = + runtimeAdapter === "claude" ? this.config.reasoningEffort : undefined; + + if (!plugins?.length && !effort) { + return undefined; + } + + const options: Record = {}; + if (plugins?.length) { + options.plugins = plugins; + } + if (effort) { + options.effort = effort; + } + return { claudeCode: { options } }; + } + private getCloudInteractionOrigin(): string | undefined { return ( process.env.POSTHOG_CODE_INTERACTION_ORIGIN ??