diff --git a/package-lock.json b/package-lock.json index 229d927f..51b40d09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6555,7 +6555,7 @@ "chalk": "^5.5.0" }, "bin": { - "pi": "dist/cli.js" + "pi-pods": "dist/cli.js" }, "devDependencies": {}, "engines": { diff --git a/packages/ai/src/models.generated.ts b/packages/ai/src/models.generated.ts index 8ddaa1aa..e3f83808 100644 --- a/packages/ai/src/models.generated.ts +++ b/packages/ai/src/models.generated.ts @@ -3225,7 +3225,7 @@ export const MODELS = { cost: { input: 0.24, output: 0.38, - cacheRead: 0.19, + cacheRead: 0.11, cacheWrite: 0, }, contextWindow: 163840, diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts index 0e5e94ca..d817b25a 100644 --- a/packages/ai/src/providers/openai-completions.ts +++ b/packages/ai/src/providers/openai-completions.ts @@ -302,9 +302,11 @@ function createClient(model: Model<"openai-completions">, context: Context, apiK const headers = { ...model.headers }; if (model.provider === "github-copilot") { // Copilot expects X-Initiator to indicate whether the request is user-initiated - // or agent-initiated. It's an agent call if ANY message in history has assistant/tool role. + // or agent-initiated (e.g. follow-up after assistant/tool messages). If there is + // no prior message, default to user-initiated. const messages = context.messages || []; - const isAgentCall = messages.some((msg) => msg.role === "assistant" || msg.role === "toolResult"); + const lastMessage = messages[messages.length - 1]; + const isAgentCall = lastMessage ? lastMessage.role !== "user" : false; headers["X-Initiator"] = isAgentCall ? "agent" : "user"; headers["Openai-Intent"] = "conversation-edits"; } diff --git a/packages/ai/src/providers/openai-responses.ts b/packages/ai/src/providers/openai-responses.ts index b10ad93d..58538055 100644 --- a/packages/ai/src/providers/openai-responses.ts +++ b/packages/ai/src/providers/openai-responses.ts @@ -310,9 +310,11 @@ function createClient(model: Model<"openai-responses">, context: Context, apiKey const headers = { ...model.headers }; if (model.provider === "github-copilot") { // Copilot expects X-Initiator to indicate whether the request is user-initiated - // or agent-initiated. It's an agent call if ANY message in history has assistant/tool role. + // or agent-initiated (e.g. follow-up after assistant/tool messages). If there is + // no prior message, default to user-initiated. const messages = context.messages || []; - const isAgentCall = messages.some((msg) => msg.role === "assistant" || msg.role === "toolResult"); + const lastMessage = messages[messages.length - 1]; + const isAgentCall = lastMessage ? lastMessage.role !== "user" : false; headers["X-Initiator"] = isAgentCall ? "agent" : "user"; headers["Openai-Intent"] = "conversation-edits"; } diff --git a/packages/ai/test/copilot-initiator.test.ts b/packages/ai/test/copilot-initiator.test.ts index 4098f640..9cd2cab8 100644 --- a/packages/ai/test/copilot-initiator.test.ts +++ b/packages/ai/test/copilot-initiator.test.ts @@ -175,7 +175,7 @@ describe("GitHub Copilot Headers", () => { expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("user"); }); - it("sets X-Initiator: agent when assistant message exists in history", async () => { + it("sets X-Initiator: agent when last message is assistant", async () => { const context: Context = { messages: [{ role: "user", content: "Hello", timestamp: Date.now() }, assistantMessage], }; @@ -186,7 +186,7 @@ describe("GitHub Copilot Headers", () => { expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("agent"); }); - it("sets X-Initiator: agent when toolResult exists in history", async () => { + it("sets X-Initiator: agent when last message is toolResult", async () => { const context: Context = { messages: [{ role: "user", content: "Hello", timestamp: Date.now() }, toolResultMessage], }; @@ -197,7 +197,7 @@ describe("GitHub Copilot Headers", () => { expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("agent"); }); - it("sets X-Initiator: agent for multi-turn conversation (last is user, but assistant in history)", async () => { + it("sets X-Initiator: user for multi-turn conversation when last message is user", async () => { const context: Context = { messages: [ { role: "user", content: "Hello", timestamp: Date.now() }, @@ -209,7 +209,7 @@ describe("GitHub Copilot Headers", () => { const stream = streamOpenAICompletions(copilotCompletionsModel, context, { apiKey: "test-key" }); await consumeStream(stream); - expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("agent"); + expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("user"); }); it("sets X-Initiator: user when there are no messages", async () => { @@ -259,7 +259,7 @@ describe("GitHub Copilot Headers", () => { expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("user"); }); - it("sets X-Initiator: agent when assistant message exists in history", async () => { + it("sets X-Initiator: agent when last message is assistant", async () => { const context: Context = { messages: [ { role: "user", content: "Hello", timestamp: Date.now() }, @@ -273,7 +273,7 @@ describe("GitHub Copilot Headers", () => { expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("agent"); }); - it("sets X-Initiator: agent when toolResult exists in history", async () => { + it("sets X-Initiator: agent when last message is toolResult", async () => { const context: Context = { messages: [{ role: "user", content: "Hello", timestamp: Date.now() }, toolResultMessage], }; @@ -284,7 +284,7 @@ describe("GitHub Copilot Headers", () => { expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("agent"); }); - it("sets X-Initiator: agent for multi-turn conversation (last is user, but assistant in history)", async () => { + it("sets X-Initiator: user for multi-turn conversation when last message is user", async () => { const context: Context = { messages: [ { role: "user", content: "Hello", timestamp: Date.now() }, @@ -296,7 +296,7 @@ describe("GitHub Copilot Headers", () => { const stream = streamOpenAIResponses(copilotResponsesModel, context, { apiKey: "test-key" }); await consumeStream(stream); - expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("agent"); + expect(lastOpenAIConfig?.defaultHeaders?.["X-Initiator"]).toBe("user"); }); it("sets X-Initiator: user when there are no messages", async () => {