@code-yeongyu/senpi-ai 2026.9.12 → 2026.9.13
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.
- package/README.md +39 -4
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +8 -1
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/cloudflare-ai-binding.d.ts +77 -0
- package/dist/api/cloudflare-ai-binding.d.ts.map +1 -0
- package/dist/api/cloudflare-ai-binding.js +72 -0
- package/dist/api/cloudflare-ai-binding.js.map +1 -0
- package/dist/api/cursor-agent/measure.d.ts +11 -0
- package/dist/api/cursor-agent/measure.d.ts.map +1 -1
- package/dist/api/cursor-agent/measure.js +18 -0
- package/dist/api/cursor-agent/measure.js.map +1 -1
- package/dist/api/cursor-agent.d.ts +1 -1
- package/dist/api/cursor-agent.d.ts.map +1 -1
- package/dist/api/cursor-agent.js +5 -1
- package/dist/api/cursor-agent.js.map +1 -1
- package/dist/api/mistral-conversations.js +4 -1
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +6 -3
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +4 -1
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +13 -3
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/cursor/context-limit-store.d.ts +32 -0
- package/dist/cursor/context-limit-store.d.ts.map +1 -0
- package/dist/cursor/context-limit-store.js +62 -0
- package/dist/cursor/context-limit-store.js.map +1 -0
- package/dist/cursor/store-migration.d.ts.map +1 -1
- package/dist/cursor/store-migration.js +2 -1
- package/dist/cursor/store-migration.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +1 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +5 -2
- package/dist/models.js.map +1 -1
- package/dist/openai-responses-compat.d.ts +1 -1
- package/dist/openai-responses-compat.d.ts.map +1 -1
- package/dist/openai-responses-compat.js.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.js +4 -0
- package/dist/providers/cloudflare-ai-gateway.js.map +1 -1
- package/dist/providers/cursor.d.ts.map +1 -1
- package/dist/providers/cursor.js +2 -1
- package/dist/providers/cursor.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/azure-openai-responses.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/deepseek.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/openai-codex.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan-individual.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/providers/data/xai.json +1 -1
- package/dist/providers/faux.js +5 -5
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +4 -3
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/opencode-headers.d.ts +4 -0
- package/dist/providers/opencode-headers.d.ts.map +1 -0
- package/dist/providers/opencode-headers.js +22 -0
- package/dist/providers/opencode-headers.js.map +1 -0
- package/dist/providers/opencode.d.ts.map +1 -1
- package/dist/providers/opencode.js +5 -4
- package/dist/providers/opencode.js.map +1 -1
- package/dist/types.d.ts +16 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/assistant-message-frame.d.ts +77 -0
- package/dist/utils/assistant-message-frame.d.ts.map +1 -0
- package/dist/utils/assistant-message-frame.js +427 -0
- package/dist/utils/assistant-message-frame.js.map +1 -0
- package/dist/utils/cursor-context-limit.d.ts +12 -0
- package/dist/utils/cursor-context-limit.d.ts.map +1 -0
- package/dist/utils/cursor-context-limit.js +93 -0
- package/dist/utils/cursor-context-limit.js.map +1 -0
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +25 -5
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/prompt-cache-ttl.d.ts +1 -1
- package/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
- package/dist/utils/prompt-cache-ttl.js +5 -1
- package/dist/utils/prompt-cache-ttl.js.map +1 -1
- package/dist/utils/retry-profile/types.d.ts +10 -1
- package/dist/utils/retry-profile/types.d.ts.map +1 -1
- package/dist/utils/retry-profile/types.js.map +1 -1
- package/dist/utils/retry.d.ts +13 -2
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +18 -7
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/uuid.d.ts +2 -2
- package/dist/utils/uuid.d.ts.map +1 -1
- package/dist/utils/uuid.js +36 -31
- package/dist/utils/uuid.js.map +1 -1
- package/package.json +6 -6
- package/dist/api/cloudflare-gateway-binding.d.ts +0 -69
- package/dist/api/cloudflare-gateway-binding.d.ts.map +0 -1
- package/dist/api/cloudflare-gateway-binding.js +0 -159
- package/dist/api/cloudflare-gateway-binding.js.map +0 -1
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
|
|
|
26
26
|
- [Streaming Tool Calls with Partial JSON](#streaming-tool-calls-with-partial-json)
|
|
27
27
|
- [Validating Tool Arguments](#validating-tool-arguments)
|
|
28
28
|
- [Complete Event Reference](#complete-event-reference)
|
|
29
|
+
- [Compact Assistant Message Frames](#compact-assistant-message-frames)
|
|
29
30
|
- [Image Input](#image-input)
|
|
30
31
|
- [Image Generation](#image-generation)
|
|
31
32
|
- [Thinking/Reasoning](#thinkingreasoning)
|
|
@@ -662,6 +663,10 @@ for await (const event of s) {
|
|
|
662
663
|
|
|
663
664
|
### Complete Event Reference
|
|
664
665
|
|
|
666
|
+
Successful generation follows `start → updates* → done`. A failure after generation starts follows `start → updates* → error`. Request setup may fail before generation starts, in which case the stream contains only `error`; `done` and update events are invalid before `start`. Direct API `streamSimple()` calls throw synchronously when request auth is missing.
|
|
667
|
+
|
|
668
|
+
Every non-terminal event's `partial` is the shared live response-so-far helper. It is intentionally not an event-time snapshot: providers may mutate the same message and content blocks as generation advances, including while older events wait in the stream queue. Inspect it when handling an event instead of retaining it as historical state. Text and ordinary thinking blocks are empty when their `*_start` event is emitted and grow only through matching `*_delta` events until the authoritative `*_end`; redacted thinking may be complete at start and emit no deltas. Tool-call arguments at `toolcall_start` are provider-specific; `toolcall_delta` carries subsequent JSON updates.
|
|
669
|
+
|
|
665
670
|
All streaming events emitted during assistant message generation:
|
|
666
671
|
|
|
667
672
|
| Event Type | Description | Key Properties |
|
|
@@ -675,12 +680,40 @@ All streaming events emitted during assistant message generation:
|
|
|
675
680
|
| `thinking_end` | Thinking block complete | `content`: Full thinking, `contentIndex`: Position |
|
|
676
681
|
| `toolcall_start` | Tool call begins | `contentIndex`: Position in content array |
|
|
677
682
|
| `toolcall_delta` | Tool arguments streaming | `delta`: JSON chunk, `partial.content[contentIndex].arguments`: Partial parsed args |
|
|
678
|
-
| `toolcall_end` | Tool call complete | `toolCall`: Complete validated tool call with `id`, `name`, `arguments` |
|
|
683
|
+
| `toolcall_end` | Tool call complete | `toolCall`: Complete, but not schema-validated, tool call with `id`, `name`, `arguments` |
|
|
679
684
|
| `done` | Stream complete | `reason`: Stop reason ("stop", "length", "toolUse"), `message`: Final assistant message |
|
|
680
685
|
| `error` | Error occurred | `reason`: Error type ("error" or "aborted"), `error`: AssistantMessage with partial content |
|
|
681
686
|
|
|
682
687
|
Streaming events for different content blocks are not guaranteed to be contiguous. Providers may emit deltas for text, thinking, and tool calls in the same upstream chunk, and pi may surface corresponding events interleaved, for example `text_start`, `text_delta`, `toolcall_start`, `text_delta`, `toolcall_delta`. Consumers must use `contentIndex` to associate each delta/end event with its block and must not assume that a block's `*_start`/`*_delta`/`*_end` sequence is uninterrupted by events for other blocks.
|
|
683
688
|
|
|
689
|
+
### Compact Assistant Message Frames
|
|
690
|
+
|
|
691
|
+
`AssistantMessageFrameEncoder` converts one stream into compact, persistable `AssistantMessageFrame` values. Create one encoder per stream and feed it every event in order. The encoder understands that `partial` is live: a block-start event consumed after the provider has already queued later deltas snapshots the current block once, and covered queued text/thinking deltas produce no duplicate frame. It retains only per-open-block counters plus, temporarily, the raw prefix needed to synchronize an already-advanced tool call. It never clones the growing full partial per token.
|
|
692
|
+
|
|
693
|
+
The start frame contains message metadata with empty content. Text and thinking frames store each generated character at most once before the authoritative end frame. Tool calls that were already advanced when their start event was consumed use one compact JSON checkpoint before ordinary deltas resume. Terminal `done` and `error` events produce no frame because final message settlement is separate. A pre-generation `error` therefore produces no frames.
|
|
694
|
+
|
|
695
|
+
`reduceAssistantMessageFrames()` is the canonical pure reducer. It reconstructs text, thinking, and tool-call arguments, including interleaved blocks identified by `contentIndex`, and rejects malformed sequences. It performs a single pass over the iterable and returns `undefined` when there is no start frame. End frames replace blocks with the provider's authoritative completed content and metadata. The reducer does not validate tool arguments against a TypeBox schema; call `validateToolCall` before execution.
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
import {
|
|
699
|
+
AssistantMessageFrameEncoder,
|
|
700
|
+
reduceAssistantMessageFrames,
|
|
701
|
+
type AssistantMessageFrame,
|
|
702
|
+
} from '@earendil-works/pi-ai';
|
|
703
|
+
|
|
704
|
+
const encoder = new AssistantMessageFrameEncoder();
|
|
705
|
+
const frames: AssistantMessageFrame[] = [];
|
|
706
|
+
for await (const event of s) {
|
|
707
|
+
const frame = encoder.encode(event);
|
|
708
|
+
if (frame) frames.push(frame);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const reconstructedPartial = reduceAssistantMessageFrames(frames);
|
|
712
|
+
const finalMessage = await s.result(); // Persist terminal settlement separately.
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
An encoder rejects duplicate starts, updates before start, `done` before start, events after a terminal event, duplicate block starts, and block-kind mismatches. An `error` before start is valid and returns no frame.
|
|
716
|
+
|
|
684
717
|
## Image Input
|
|
685
718
|
|
|
686
719
|
Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
|
|
@@ -905,7 +938,7 @@ Every `AssistantMessage` includes a `stopReason` field that indicates how the ge
|
|
|
905
938
|
|
|
906
939
|
## Error Handling
|
|
907
940
|
|
|
908
|
-
Request failures
|
|
941
|
+
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:
|
|
909
942
|
|
|
910
943
|
```typescript
|
|
911
944
|
// In streaming
|
|
@@ -927,7 +960,7 @@ if (message.stopReason === 'error' || message.stopReason === 'aborted') {
|
|
|
927
960
|
}
|
|
928
961
|
```
|
|
929
962
|
|
|
930
|
-
|
|
963
|
+
When using a provider collection, auth failures (OAuth refresh failed, unknown provider) surface as a stream error with `stopReason: "error"`. Direct API `streamSimple()` calls instead throw synchronously when their required auth is absent.
|
|
931
964
|
|
|
932
965
|
### Aborting Requests
|
|
933
966
|
|
|
@@ -1187,7 +1220,7 @@ interface OpenAICompletionsCompat {
|
|
|
1187
1220
|
supportsUsageInStreaming?: boolean; // Whether provider supports `stream_options: { include_usage: true }` (default: true)
|
|
1188
1221
|
supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: true)
|
|
1189
1222
|
supportsOpenAIGrammarTools?: boolean; // Whether to emit OpenAI custom Lark/regex grammar tools; false falls back to normal function tools (default: false; the generated catalog enables it for capable models)
|
|
1190
|
-
sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: false)
|
|
1223
|
+
sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: true for OpenRouter, false otherwise)
|
|
1191
1224
|
sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Format for session affinity: 'openai' uses `prompt_cache_key`, `session_id`, `x-client-request-id`, and `x-session-affinity`; 'openai-nosession' uses `prompt_cache_key`, `x-client-request-id`, and `x-session-affinity`; 'openrouter' uses `x-session-id` (default: auto-detected)
|
|
1192
1225
|
maxTokensField?: 'max_completion_tokens' | 'max_tokens'; // Which field name to use (default: max_completion_tokens)
|
|
1193
1226
|
requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
|
|
@@ -1213,6 +1246,8 @@ interface OpenAIResponsesCompat {
|
|
|
1213
1246
|
}
|
|
1214
1247
|
```
|
|
1215
1248
|
|
|
1249
|
+
OpenRouter requests send `x-session-id` from `sessionId` when prompt caching is enabled. Chat Completions and Anthropic Messages both auto-detect OpenRouter endpoints unless `sendSessionAffinityHeaders` is explicitly false. On Anthropic-compatible models, `sessionAffinityFormat: "openrouter"` selects `x-session-id`; when unset, the existing `x-session-affinity` format is used. Explicit request headers take precedence over generated headers.
|
|
1250
|
+
|
|
1216
1251
|
If `compat` is not set, the library falls back to URL-based detection. If `compat` is partially set, unspecified fields use the detected defaults. This is useful for:
|
|
1217
1252
|
|
|
1218
1253
|
- **LiteLLM proxies**: May not support `store` field
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAMX,+BAA+B,EAK/B,MAAM,uDAAuD,CAAC;AAG/D,OAAO,KAAK,EACX,wBAAwB,EAKxB,OAAO,EAGP,KAAK,EAGL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAGb,IAAI,EAIJ,MAAM,aAAa,CAAC;AAsCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA0ElE,eAAO,MAAM,gBAAgB,SAAU,MAAM,WAC2B,CAAC;AACzE,eAAO,MAAM,kBAAkB,SAAU,MAAM,UAAU,IAAI,EAAE,WAS9D,CAAC;AA4EF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA+FhE,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,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C;;;;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;AAmvBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAudzE,CAAC;AAyFF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA+ClF,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-messages.d.ts","sourceRoot":"","sources":["../../src/api/anthropic-messages.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAMX,+BAA+B,EAK/B,MAAM,uDAAuD,CAAC;AAG/D,OAAO,KAAK,EACX,wBAAwB,EAKxB,OAAO,EAGP,KAAK,EAGL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAGb,IAAI,EAIJ,MAAM,aAAa,CAAC;AAsCrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AA0ElE,eAAO,MAAM,gBAAgB,SAAU,MAAM,WAC2B,CAAC;AACzE,eAAO,MAAM,kBAAkB,SAAU,MAAM,UAAU,IAAI,EAAE,WAS9D,CAAC;AA4EF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE1E,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG,SAAS,CAAC;AA+FhE,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,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C;;;;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;AAmvBD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,CAudzE,CAAC;AAyFF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CA+ClF,CAAC;AAmJF,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,gBAAgB,GACxB,+BAA+B,CAkBjC"}
|
|
@@ -1432,7 +1432,14 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
|
|
|
1432
1432
|
return { client, isOAuthToken: true };
|
|
1433
1433
|
}
|
|
1434
1434
|
// API key auth
|
|
1435
|
-
const
|
|
1435
|
+
const affinityCompat = getAnthropicCompat(model);
|
|
1436
|
+
const sessionAffinityHeaders = {};
|
|
1437
|
+
if (sessionId && affinityCompat.sendSessionAffinityHeaders) {
|
|
1438
|
+
// OpenRouter routes prompt-cache affinity through its own header name and
|
|
1439
|
+
// rejects x-session-affinity (earendil-works/pi#9102).
|
|
1440
|
+
const header = affinityCompat.sessionAffinityFormat === "openrouter" ? "x-session-id" : "x-session-affinity";
|
|
1441
|
+
sessionAffinityHeaders[header] = sessionId;
|
|
1442
|
+
}
|
|
1436
1443
|
const client = new Anthropic({
|
|
1437
1444
|
apiKey: apiKey ?? null,
|
|
1438
1445
|
authToken: null,
|