mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(ai,agent,coding-agent,durable): client identity follow-ups
- ClientAttribution is { name, url, categories? } with required name and url, so
OpenRouter always gets an HTTP-Referer. identity.ts maps the fields to each
provider's headers; NVIDIA gets the name with its first letter upper-cased.
attribution: null turns it off. PI_ATTRIBUTION is pi's, including the
cli-agent category; agent-core, durable, and the coding agent use it.
- Categories are a string array, joined for OpenRouter.
- withClientIdentity() is a typed wrapper instead of a Proxy and keeps a
MutableModels mutable. Durable no longer caches wrappers in a WeakMap.
- The Codex originator stays pi; login agentName no longer follows attribution.
- Models and the coding agent's ModelRuntime add x-opencode-session for custom
providers pointing at OpenCode, replacing the coding agent's transformHeaders.
This commit is contained in:
parent
adc5fa9f80
commit
04a93bad8f
20 changed files with 236 additions and 194 deletions
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
### Added
|
||||
|
||||
- Added `AgentOptions.identity` and `Agent.identity` (a pi-ai `ClientIdentity`). Agent loop requests use `DEFAULT_AGENT_IDENTITY` (`{ attribution: true }`) under it, so OpenRouter, NVIDIA NIM, and OpenCode requests are attributed to pi by default; pass `{ attribution: false }` to send no attribution headers.
|
||||
- Added `AgentOptions.identity` and `Agent.identity` (a pi-ai `ClientIdentity`) to set the `User-Agent` and provider attribution of agent loop requests.
|
||||
|
||||
## [1.3.0] - 2026-10-15
|
||||
|
||||
|
|
|
|||
|
|
@ -233,9 +233,9 @@ const agent = new Agent({
|
|||
sessionId: "session-123",
|
||||
|
||||
// How requests identify the app to providers (pi-ai ClientIdentity), merged over
|
||||
// DEFAULT_AGENT_IDENTITY ({ attribution: true }: provider attribution headers name pi).
|
||||
// Pass a name to attribute requests to your app, or false to send none.
|
||||
identity: { attribution: "my-app" },
|
||||
// DEFAULT_AGENT_IDENTITY ({ attribution: PI_ATTRIBUTION }: provider attribution headers name pi).
|
||||
// Pass your app's attribution, or null to send none.
|
||||
identity: { attribution: { name: "my-app", url: "https://my-app.example" } },
|
||||
|
||||
// Dynamic API key resolution (for expiring OAuth tokens)
|
||||
getApiKey: async (provider) => refreshToken(),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
getToolStateChanges,
|
||||
mergeClientIdentity,
|
||||
normalizeContext,
|
||||
PI_ATTRIBUTION,
|
||||
type SystemMessage,
|
||||
type ToolResultMessage,
|
||||
type ToolStateChanges,
|
||||
|
|
@ -37,7 +38,7 @@ export type AgentEventSink = (event: AgentEvent) => Promise<void> | void;
|
|||
* Request identity of agent loops: pi-ai's defaults with provider attribution to pi. `AgentLoopConfig.identity`
|
||||
* overrides it field by field.
|
||||
*/
|
||||
export const DEFAULT_AGENT_IDENTITY: ClientIdentity = { attribution: true };
|
||||
export const DEFAULT_AGENT_IDENTITY: ClientIdentity = { attribution: PI_ATTRIBUTION };
|
||||
|
||||
/**
|
||||
* Start an agent loop with a new prompt message.
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ export interface AgentOptions {
|
|||
toolExecution?: ToolExecutionMode;
|
||||
/**
|
||||
* How requests identify the app to providers, merged field by field over `DEFAULT_AGENT_IDENTITY`
|
||||
* (`{ attribution: true }`). Pass `{ attribution: false }` to send no attribution headers.
|
||||
* (`{ attribution: PI_ATTRIBUTION }`). Pass `{ attribution: null }` to send no attribution headers.
|
||||
*/
|
||||
identity?: ClientIdentity;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { ClientIdentity } from "@earendil-works/pi-ai";
|
||||
import { type ClientIdentity, PI_ATTRIBUTION } from "@earendil-works/pi-ai";
|
||||
import {
|
||||
type AssistantMessage,
|
||||
createAssistantMessageEventStream,
|
||||
|
|
@ -1214,15 +1214,16 @@ describe("Agent", () => {
|
|||
});
|
||||
|
||||
await agent.prompt("hello");
|
||||
agent.identity = { userAgent: "short", attribution: "acme" };
|
||||
const acme = { name: "acme", url: "https://acme.example" };
|
||||
agent.identity = { userAgent: "short", attribution: acme };
|
||||
await agent.prompt("hello again");
|
||||
agent.identity = { userAgent: "short" };
|
||||
await agent.prompt("and again");
|
||||
|
||||
expect(received).toEqual([
|
||||
{ attribution: true },
|
||||
{ attribution: "acme", userAgent: "short" },
|
||||
{ attribution: true, userAgent: "short" },
|
||||
{ attribution: PI_ATTRIBUTION },
|
||||
{ attribution: acme, userAgent: "short" },
|
||||
{ attribution: PI_ATTRIBUTION, userAgent: "short" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue