@earendil-works/pi-ai 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +78 -3
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +7 -10
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.js +8 -4
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +12 -0
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/classifier-shared.d.ts.map +1 -1
  12. package/dist/api/classifier-shared.js +4 -3
  13. package/dist/api/classifier-shared.js.map +1 -1
  14. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  15. package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
  16. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  17. package/dist/api/google-generative-ai.js +4 -4
  18. package/dist/api/google-generative-ai.js.map +1 -1
  19. package/dist/api/google-shared.js +1 -1
  20. package/dist/api/google-shared.js.map +1 -1
  21. package/dist/api/google-vertex.d.ts.map +1 -1
  22. package/dist/api/google-vertex.js +9 -9
  23. package/dist/api/google-vertex.js.map +1 -1
  24. package/dist/api/lazy.d.ts +7 -2
  25. package/dist/api/lazy.d.ts.map +1 -1
  26. package/dist/api/lazy.js +24 -5
  27. package/dist/api/lazy.js.map +1 -1
  28. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  29. package/dist/api/llama-cpp-classify.js +2 -1
  30. package/dist/api/llama-cpp-classify.js.map +1 -1
  31. package/dist/api/mistral-conversations.js +2 -2
  32. package/dist/api/mistral-conversations.js.map +1 -1
  33. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  34. package/dist/api/openai-codex-responses.js +17 -11
  35. package/dist/api/openai-codex-responses.js.map +1 -1
  36. package/dist/api/openai-completions.d.ts.map +1 -1
  37. package/dist/api/openai-completions.js +14 -8
  38. package/dist/api/openai-completions.js.map +1 -1
  39. package/dist/api/openai-responses-shared.d.ts +16 -0
  40. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  41. package/dist/api/openai-responses-shared.js +61 -9
  42. package/dist/api/openai-responses-shared.js.map +1 -1
  43. package/dist/api/openai-responses.d.ts.map +1 -1
  44. package/dist/api/openai-responses.js +17 -9
  45. package/dist/api/openai-responses.js.map +1 -1
  46. package/dist/api/openrouter-images.d.ts.map +1 -1
  47. package/dist/api/openrouter-images.js +4 -3
  48. package/dist/api/openrouter-images.js.map +1 -1
  49. package/dist/api/pi-messages.d.ts.map +1 -1
  50. package/dist/api/pi-messages.js +3 -3
  51. package/dist/api/pi-messages.js.map +1 -1
  52. package/dist/api/simple-options.d.ts.map +1 -1
  53. package/dist/api/simple-options.js +2 -1
  54. package/dist/api/simple-options.js.map +1 -1
  55. package/dist/api/system-one-shared.d.ts +29 -2
  56. package/dist/api/system-one-shared.d.ts.map +1 -1
  57. package/dist/api/system-one-shared.js +55 -3
  58. package/dist/api/system-one-shared.js.map +1 -1
  59. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  60. package/dist/api/typesafe-system-one.js +10 -0
  61. package/dist/api/typesafe-system-one.js.map +1 -1
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +2 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/models.d.ts +4 -0
  67. package/dist/models.d.ts.map +1 -1
  68. package/dist/models.js +19 -6
  69. package/dist/models.js.map +1 -1
  70. package/dist/providers/compat-schema.d.ts +168 -0
  71. package/dist/providers/compat-schema.d.ts.map +1 -1
  72. package/dist/providers/compat-schema.js +15 -2
  73. package/dist/providers/compat-schema.js.map +1 -1
  74. package/dist/providers/data/.manifest.json +1 -1
  75. package/dist/providers/data/amazon-bedrock.json +1 -1
  76. package/dist/providers/data/azure.json +1 -1
  77. package/dist/providers/data/baseten.json +1 -1
  78. package/dist/providers/data/cerebras.json +1 -1
  79. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  80. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  81. package/dist/providers/data/fireworks.json +1 -1
  82. package/dist/providers/data/github-copilot.json +1 -1
  83. package/dist/providers/data/google.json +1 -1
  84. package/dist/providers/data/groq.json +1 -1
  85. package/dist/providers/data/huggingface.json +1 -1
  86. package/dist/providers/data/minimax-cn.json +1 -1
  87. package/dist/providers/data/minimax.json +1 -1
  88. package/dist/providers/data/mistral.json +1 -1
  89. package/dist/providers/data/moonshotai-cn.json +1 -1
  90. package/dist/providers/data/nvidia.json +1 -1
  91. package/dist/providers/data/openai-codex.json +1 -1
  92. package/dist/providers/data/openai.json +1 -1
  93. package/dist/providers/data/opencode-go.json +1 -1
  94. package/dist/providers/data/opencode.json +1 -1
  95. package/dist/providers/data/openrouter.json +1 -1
  96. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  97. package/dist/providers/data/qwen-token-plan.json +1 -1
  98. package/dist/providers/data/radius.json +1 -1
  99. package/dist/providers/data/together.json +1 -1
  100. package/dist/providers/data/typesafe.json +1 -1
  101. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  102. package/dist/providers/data/xai.json +1 -1
  103. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  104. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  105. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  106. package/dist/providers/data/xiaomi.json +1 -1
  107. package/dist/providers/data/zai-coding-cn.json +1 -1
  108. package/dist/providers/model-schema.d.ts.map +1 -1
  109. package/dist/providers/model-schema.js +4 -2
  110. package/dist/providers/model-schema.js.map +1 -1
  111. package/dist/telemetry.d.ts +288 -0
  112. package/dist/telemetry.d.ts.map +1 -0
  113. package/dist/telemetry.js +191 -0
  114. package/dist/telemetry.js.map +1 -0
  115. package/dist/types.d.ts +13 -5
  116. package/dist/types.d.ts.map +1 -1
  117. package/dist/types.js.map +1 -1
  118. package/dist/utils/identity.d.ts +65 -0
  119. package/dist/utils/identity.d.ts.map +1 -0
  120. package/dist/utils/identity.js +141 -0
  121. package/dist/utils/identity.js.map +1 -0
  122. package/dist/utils/overflow.d.ts +1 -0
  123. package/dist/utils/overflow.d.ts.map +1 -1
  124. package/dist/utils/overflow.js +6 -1
  125. package/dist/utils/overflow.js.map +1 -1
  126. package/dist/utils/retry.d.ts.map +1 -1
  127. package/dist/utils/retry.js +16 -7
  128. package/dist/utils/retry.js.map +1 -1
  129. package/dist/utils/transcript.d.ts +22 -8
  130. package/dist/utils/transcript.d.ts.map +1 -1
  131. package/dist/utils/transcript.js +29 -18
  132. package/dist/utils/transcript.js.map +1 -1
  133. package/dist/utils/version.d.ts +2 -0
  134. package/dist/utils/version.d.ts.map +1 -0
  135. package/dist/utils/version.js +3 -0
  136. package/dist/utils/version.js.map +1 -0
  137. package/package.json +2 -2
  138. package/dist/utils/pi-user-agent.d.ts +0 -2
  139. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  140. package/dist/utils/pi-user-agent.js +0 -12
  141. package/dist/utils/pi-user-agent.js.map +0 -1
package/README.md CHANGED
@@ -18,6 +18,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
18
18
  - [Auth](#auth)
19
19
  - [How Auth Resolves](#how-auth-resolves)
20
20
  - [Transforming Request Headers](#transforming-request-headers)
21
+ - [Client Identity](#client-identity)
21
22
  - [Credential Store](#credential-store)
22
23
  - [Environment Variables](#environment-variables)
23
24
  - [Tools](#tools)
@@ -34,6 +35,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
34
35
  - [Provider-Specific Options](#provider-specific-options-streamcomplete)
35
36
  - [Streaming Thinking Content](#streaming-thinking-content)
36
37
  - [Stop Reasons](#stop-reasons)
38
+ - [Telemetry](#telemetry)
37
39
  - [Error Handling](#error-handling)
38
40
  - [Aborting Requests](#aborting-requests)
39
41
  - [Continuing After Abort](#continuing-after-abort)
@@ -424,6 +426,33 @@ Header names are merged case-insensitively. Explicit headers override auth/model
424
426
 
425
427
  `transformHeaders` belongs to `Models`, not `Provider`. A `Models` implementation must consume it and remove it before calling `Provider.stream*()`. Provider implementations continue receiving ordinary `ApiStreamOptions` or `SimpleStreamOptions` and never handle the transform themselves. Use this option instead of calling `getAuth(model)` before `stream*()`, which would resolve request auth twice.
426
428
 
429
+ ### Client Identity
430
+
431
+ `identity` controls how requests identify the calling app to providers. Set it on `createModels()` as a default, or per request; a request's fields override the `Models` default field by field:
432
+
433
+ ```typescript
434
+ const models = createModels({
435
+ identity: {
436
+ userAgent: 'detailed',
437
+ version: '2.1.0',
438
+ attribution: { name: 'my-app', url: 'https://my-app.example', categories: ['cloud-agent'] },
439
+ },
440
+ });
441
+
442
+ await models.completeSimple(model, context, { identity: { attribution: null } });
443
+ ```
444
+
445
+ - `userAgent`: `"detailed"` (default) sends `pi/<version> (<platform>; <runtime>; <arch>)`, `"short"` sends `pi`, and `{ custom: "..." }` sends the given value. On Bedrock the value is appended to the AWS SDK user agent.
446
+ - `version`: the version in the detailed `User-Agent`. Default: the pi-ai version.
447
+ - `attribution`: provider attribution headers. Absent (default) or `null` sends none; `null` also turns off attribution from a merged identity. `{ name, url, categories? }` maps to each provider's headers:
448
+ - OpenRouter: `HTTP-Referer: <url>` (the app's identity in OpenRouter's rankings, grouped by origin), `X-OpenRouter-Title: <name>`, and `X-OpenRouter-Categories` (comma-separated).
449
+ - NVIDIA NIM: `X-BILLING-INVOKE-ORIGIN: <name>` with its first letter upper-cased.
450
+ - OpenCode: `x-opencode-client: <name>`.
451
+
452
+ `PI_ATTRIBUTION` is pi's: `{ name: "pi", url: "https://pi.dev", categories: ["cli-agent"] }`.
453
+
454
+ Identity headers are defaults below provider, model, and request headers, so configured headers win and a `null` request header removes one. Providers that send a fixed client identity, such as GitHub Copilot and Anthropic OAuth, keep it. `withClientIdentity(models, identity)` returns a wrapper of a `Models` (or `MutableModels`) whose requests use `identity` over the one the `Models` was created with, for libraries that receive a `Models` from their host. `identity` is plain JSON, so it can be persisted with other request options.
455
+
427
456
  ### Credential Store
428
457
 
429
458
  Stored credentials (API keys entered interactively, OAuth tokens) live in a `CredentialStore` — one type-tagged credential per provider. pi-ai ships an in-memory default; apps inject persistent storage:
@@ -866,8 +895,8 @@ Classifier models consume structured JSON state, and optionally images, and answ
866
895
  | Provider | Model IDs | Auth |
867
896
  | --- | --- | --- |
868
897
  | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
869
- | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or OpenRouter OAuth |
870
- | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
898
+ | `openrouter` | `typesafe/jev-1.13`, `typesafe/jev-1.14`, `~typesafe/jev-latest`, `cloudflare/clef`, `cloudflare/clef-flash`, `cloudflare/clef-omni` | `OPENROUTER_API_KEY` or OpenRouter OAuth |
899
+ | `cloudflare-workers-ai` | `typesafe/jev`, `typesafe/jev-1.14.0`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash`, `@cf/cloudflare/clef-omni` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
871
900
  | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
872
901
  | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
873
902
  | `openai` | `gpt-6-luna` | `OPENAI_API_KEY` |
@@ -912,6 +941,18 @@ When the service reports token counts, `result.usage` carries them with their co
912
941
 
913
942
  `ClassifierContext.images` holds image blocks (`{ type: "image", data, mimeType }`) judged together with the state. Only models whose `input` includes `"image"` accept them; `classify()` returns an error result for other models.
914
943
 
944
+ ### System One images
945
+
946
+ System One services take images in three forms. The model's `compat.imageInput` chooses the form (`"assets"`, `"images"`, or `"state-image-urls"`), and `compat.sendImageOptInHeader` controls TypeSafe's opt-in header:
947
+
948
+ - **TypeSafe** (`typesafe`, and Jev on Workers AI) takes PNG and JPEG images as `assets`, `{ "<image-1>": { type: "image", format: "png", data_base64 } }`. Every asset must be referenced from the request, so the state is sent as `["<image-1>", ..., state]`. Images are experimental on TypeSafe's own API and need the `TypeSafe-Danger-Zone-Images: true` header, which the API sends with requests that carry images.
949
+ - **Clef on Workers AI** takes up to four PNG, JPEG, or WebP images as an `images` list of data URLs, next to the unchanged state.
950
+ - **OpenRouter** takes images as `{ type: "image_url", image_url: { url } }` parts before the state: `[part, ..., state]`. It answers requests with `assets` but drops the images, without an error.
951
+
952
+ Built-in catalog entries set `compat` explicitly, so they keep their form when `baseUrl` points at a proxy. For models without it, the APIs detect the form: OpenRouter by provider `openrouter` or a `baseUrl` containing `openrouter.ai`, Clef by a model ID starting with `@cf/cloudflare/clef`, and TypeSafe's form with the opt-in header otherwise. A custom provider that reaches OpenRouter through a proxy matches neither check and must set `compat.imageInput: "state-image-urls"` and `compat.sendImageOptInHeader: false`; otherwise OpenRouter drops its images.
953
+
954
+ Vercel AI Gateway and OpenCode list their Jev models with text input only.
955
+
915
956
  ### OpenAI Decisions
916
957
 
917
958
  The `openai-decisions` API calls OpenAI's [Decisions API](https://developers.openai.com/api/docs/guides/decisions) (`POST /v1/decisions`). GPT-6 Luna is its only model. The state is sent as JSON text; with images, the input is one user message with the state followed by the images as data URLs (at most 128). `choice` and `score` questions map to the Decisions types of the same name. `bool` questions become `predicate` questions; predicates have no criteria field, so the meanings of true and false are appended to the instructions. If OpenAI refuses to answer a question, the whole result is an error. Usage reports input tokens only, priced at the catalog input rate with OpenAI's long-context multiplier.
@@ -1070,6 +1111,40 @@ Every `AssistantMessage` includes a `stopReason` field that indicates how the ge
1070
1111
 
1071
1112
  `AssistantMessage` may also include `responseId`, a provider-specific upstream response or message identifier when the underlying API exposes one. Do not assume it is always present across providers.
1072
1113
 
1114
+ ## Telemetry
1115
+
1116
+ Pass an explicit parent span (`TelemetrySpan`, not an adapter) through `telemetry`; omitting it uses the no-op span:
1117
+
1118
+ ```typescript
1119
+ import { InMemoryTelemetryContext } from '@earendil-works/pi-telemetry';
1120
+
1121
+ const telemetry = new InMemoryTelemetryContext();
1122
+ await telemetry.startSpan({ name: 'application.turn' }, async (span) => {
1123
+ await models.completeSimple(model, context, { telemetry: span });
1124
+ });
1125
+ ```
1126
+
1127
+ `Models.stream()` and `streamSimple()` create `pi.ai.request`; `streamDeferred()` creates one `pi.ai.poll` per poll. Their completion helpers use the same spans. These spans include auth/setup, end at the terminal result, and do not require the caller to consume events. Direct provider/API calls are not instrumented.
1128
+
1129
+ `AI_TELEMETRY_SCHEMA`, exported from the root and `@earendil-works/pi-ai/models`, defines the typed Pi-owned vocabulary:
1130
+
1131
+ - Start attributes: `provider`, `api`, `model`, and `reasoning` only when `options.reasoning` is a string. Provider-native reasoning objects are ignored.
1132
+ - End attributes: `stop`, `usage.input`, `usage.output`, `usage.cache_read`, `usage.cache_write`, `cost.total`, and optional `response.id`. `stop: "error"` sets error status with the fixed category `provider_error` and `AssistantMessage.errorMessage` truncated to 500 characters (empty when absent). Cancellation keeps status `ok` with `stop: "aborted"`.
1133
+ - `pi.ai.first_chunk` marks the first normalized event, including setup errors. Each completed text, thinking, or tool-call block emits `pi.ai.block` with `type`, `index`, and `chars`; only tool calls include `tool`. Character counts use JavaScript string length; tool counts sum argument delta lengths without serializing arguments.
1134
+
1135
+ When `options.telemetry.metrics` is available, the same requests record metrics:
1136
+
1137
+ | Metric | Unit | Attributes |
1138
+ | --- | --- | --- |
1139
+ | `pi.ai.request.duration` | `ms` | `provider`, `model`, `stop` |
1140
+ | `pi.ai.first_chunk.duration` | `ms` | `provider`, `model` |
1141
+ | `pi.ai.tokens` | `token` | `provider`, `model`, `kind` (`input`, `output`, `cache_read`, `cache_write`) |
1142
+ | `pi.ai.cost` | `USD` | `provider`, `model` |
1143
+
1144
+ Durations use `performance.now()`. Pending/deferred polls do not add tokens or cost; final completed/error polls do. A parent span without metric capabilities remains valid and simply records no metrics. Adapter-created spans carry the same optional metric and propagation capabilities as their root.
1145
+
1146
+ Telemetry does not record prompts, generated text, reasoning content, or arguments in attributes/events. Error status includes up to 500 characters of provider error text, which may contain sensitive data; apply your export policy accordingly. Telemetry observes the existing forwarding path without another stream consumer or per-delta events. Response identifiers and error messages never become metric attributes.
1147
+
1073
1148
  ## Error Handling
1074
1149
 
1075
1150
  Request failures after a stream is returned never throw: when a request ends with an error (including aborts and tool call validation errors), the streaming API emits an error event and the final message carries the details. Setup failures may emit `error` without `start`; failures after generation begins emit `start`, any observed updates, then `error`. Direct API `streamSimple()` calls throw synchronously when request auth is missing:
@@ -1569,7 +1644,7 @@ getCurrentTools(messages); // []
1569
1644
 
1570
1645
  A custom `Provider` or `ProviderStreams` implementation reads the prompt and tools the same way from `context.messages`; `context.systemPrompt` and `context.tools` do not exist at that layer.
1571
1646
 
1572
- Models that accept system messages mid-conversation (`supportsMidConvoSystemMessages` in the model's compat settings, set by the generated catalog for verified models) receive each later system message in place, so the cached prefix stays intact; section changes are framed by name for the model. Every other model receives `collapseSystemMessages(transcript)`: the replayed prompt and current tools as the leading system message, with later system messages dropped. Anthropic models that also set `supportsMidConvoToolChanges` send tool changes as native blocks (`inline-tools-2026-09-15` beta): the top-level tool list holds the initial tools plus a stable deferred placeholder and never changes, later tools are defined by value in `tool_addition` blocks (a new definition under an existing name replaces the old one), and removals are `tool_removal` references, so tool changes do not invalidate the prompt cache. That needs at least one initial tool; otherwise the current tool list is sent at the top level with the system text only. OpenAI Responses models with `supportsAdditionalTools` or `supportsToolSearch` anchor additive tool changes at their message; everything else sends the current tool list at the top level.
1647
+ Models that accept system messages mid-conversation (`supportsMidConvoSystemMessages` in the model's compat settings, set by the generated catalog for verified models) receive each later system message in place, so the cached prefix stays intact; section changes are framed by name for the model. Every other model receives `collapseSystemMessages(transcript)`: the replayed prompt and current tools as the leading system message, with later system messages dropped. Anthropic models that also set `supportsMidConvoToolChanges` send tool changes as native blocks (`inline-tools-2026-09-15` beta): the top-level tool list holds the initial tools plus a stable deferred placeholder and never changes, later tools are defined by value in `tool_addition` blocks (a new definition under an existing name replaces the old one), and removals are `tool_removal` references, so tool changes do not invalidate the prompt cache. That needs at least one initial tool; otherwise the current tool list is sent at the top level with the system text only. OpenAI Responses models with `supportsAdditionalTools` declare every tool in `additional_tools` items, like Codex: the initial tools in one at the head of the input and later additions and redefinitions at their message, with no top-level tool list. A later item replaces same-name definitions from earlier items but cannot replace a top-level tool or remove any tool, so removals are announced as text, later updates restate the tools that are still unavailable, and calls to removed tools fail at execution. OpenAI Responses models with only `supportsToolSearch`, and Kimi models with `supportsMidConvoToolAdditions`, anchor new tools at their message; a removal or a redeclared name moves the tool state up to that point into the top-level list once. Everything else sends the current tool list at the top level.
1573
1648
 
1574
1649
  ## Context Serialization
1575
1650
 
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAuJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAwRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA0WzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
1
+ {"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAiC,MAAM,mBAAmB,CAAC;AAoBlE,OAAO,KAAK,EASX,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAuJrB,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA2ChE,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACtD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB;AAoRD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CA2WzE,CAAC;AA2BF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAgDlF,CAAC"}
@@ -4,8 +4,8 @@ import { calculateCost } from "../models.js";
4
4
  import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
5
5
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
6
6
  import { headersToRecord } from "../utils/headers.js";
7
+ import { getIdentityHeaders } from "../utils/identity.js";
7
8
  import { parseJsonWithRepair, parseStreamingJson, parseToolCallArguments } from "../utils/json-parse.js";
8
- import { getPiUserAgent } from "../utils/pi-user-agent.js";
9
9
  import { getProviderEnvValue } from "../utils/provider-env.js";
10
10
  import { retryProviderRequest } from "../utils/provider-retry.js";
11
11
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
@@ -160,9 +160,6 @@ function mergeHeaders(...headerSources) {
160
160
  }
161
161
  return merged;
162
162
  }
163
- function mergeClientHeaders(...headerSources) {
164
- return mergeHeaders({ "User-Agent": getPiUserAgent() }, ...headerSources);
165
- }
166
163
  function hasHeader(headers, name) {
167
164
  if (!headers)
168
165
  return false;
@@ -422,7 +419,7 @@ export const stream = (model, context, options) => {
422
419
  }
423
420
  const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
424
421
  const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
425
- const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation);
422
+ const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation, options?.identity);
426
423
  client = created.client;
427
424
  isOAuth = created.isOAuthToken;
428
425
  }
@@ -783,7 +780,7 @@ export const streamSimple = (model, context, options) => {
783
780
  function isOAuthToken(apiKey) {
784
781
  return apiKey.includes("sk-ant-oat");
785
782
  }
786
- function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sessionId, federation) {
783
+ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sessionId, federation, identity) {
787
784
  // Copilot: Bearer auth.
788
785
  if (model.provider === "github-copilot") {
789
786
  const client = new PiAnthropic({
@@ -792,14 +789,14 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
792
789
  baseURL: model.baseUrl,
793
790
  dangerouslyAllowBrowser: true,
794
791
  fetch,
795
- defaultHeaders: mergeClientHeaders({
792
+ defaultHeaders: mergeHeaders(getIdentityHeaders(model, identity), {
796
793
  accept: "application/json",
797
794
  "anthropic-dangerous-direct-browser-access": "true",
798
795
  }, model.headers, dynamicHeaders, optionsHeaders),
799
796
  });
800
797
  return { client, isOAuthToken: false };
801
798
  }
802
- // OAuth: Bearer auth, Claude Code identity headers
799
+ // OAuth: Bearer auth, Claude Code identity headers instead of the client identity.
803
800
  if (apiKey && isOAuthToken(apiKey)) {
804
801
  const client = new PiAnthropic({
805
802
  apiKey: null,
@@ -807,7 +804,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
807
804
  baseURL: model.baseUrl,
808
805
  dangerouslyAllowBrowser: true,
809
806
  fetch,
810
- defaultHeaders: mergeClientHeaders({
807
+ defaultHeaders: mergeHeaders({
811
808
  accept: "application/json",
812
809
  "anthropic-dangerous-direct-browser-access": "true",
813
810
  "user-agent": `claude-cli/${claudeCodeVersion}`,
@@ -823,7 +820,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
823
820
  const header = compat.sessionAffinityFormat === "openrouter" ? "x-session-id" : "x-session-affinity";
824
821
  sessionAffinityHeaders[header] = sessionId;
825
822
  }
826
- const defaultHeaders = mergeClientHeaders({
823
+ const defaultHeaders = mergeHeaders(getIdentityHeaders(model, identity), {
827
824
  accept: "application/json",
828
825
  "anthropic-dangerous-direct-browser-access": "true",
829
826
  }, sessionAffinityHeaders, model.headers, optionsHeaders);