Commit graph

2128 commits

Author SHA1 Message Date
robopipi[bot]
90b42bca6e
fix(ai): remove Codex SSE retry abort listener after backoff (#11725)
The Codex Responses provider's local `sleep()` helper attached an
anonymous `abort` listener to the caller's signal and never removed it
when the timer fired. The SSE retry loop calls this helper before each
retry, so every completed backoff left one listener on the signal. Three
429 responses followed by a success left three listeners behind.

The helper now uses a named `onAbort` handler, removes it when the timer
fires, and registers it with `{ once: true }`. It still rejects with
`Error("Request was aborted")`, which the retry loop's catch block
depends on.

### Testing
- New regression test in `packages/ai/test/openai-codex-stream.test.ts`.
A stub fetch returns 429 with `retry-after: 0` three times and then a
completed SSE stream, using `maxRetries: 3` and the caller's own
`AbortController`. The test asserts that `getEventListeners(signal,
"abort")` is empty afterwards. It fails without the change ("got 3") and
passes with it.
- `node ../../node_modules/vitest/dist/cli.js --run
test/openai-codex-stream.test.ts`: 41/41 pass.
- A standalone script against the built package printed `listeners 3`
before the change and `listeners 0` after.
- `npm run check`: biome and the repo scripts pass. `tsc` fails, but
only in files this change does not touch: generated model ids in
`packages/ai` tests, `github-copilot.ts`, and missing bench dependencies
in `packages/schema`. I did not verify whether those failures also
happen on a clean checkout.

Fixes #11708

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-03 09:32:52 +01:00
David Brailovsky
0a4fad5489
fix(ai): leave room for OpenRouter's input overcount when clamping max tokens (#11712)
OpenRouter checks input + max_tokens against the context window before
routing, using its own tokenizer. For prose it counted up to 12% more
input tokens than the provider. pi left only 4096 tokens spare, so
models whose output limit is close to the window (e.g. DeepSeek flash)
were rejected with a 400, which pi then treated as a context overflow
and compacted.

The default output limit now leaves 15% of the estimated input unused
(at least 4096). When that would leave less than 32768 tokens of output,
only the fixed 4096 margin is kept and output is capped at 32768.

fixes #10497
2026-11-02 22:43:27 +01:00
m3
0ec20e157b
feat(ai): add Microsoft-Decision-1 classifier to GitHub Copilot (#11679)
Closes #11678

Changes:
- `catalog/providers/github-copilot.ts`: add `microsoft-decision-1` as a
`typesafe-system-one` classifier (text input, 32768 context, $0.042/M
input, Copilot's static headers).
- `src/providers/github-copilot.ts`: register the classifier and wrap
`typesafeSystemOneApi()` to add `/v1` to the base URL, since Copilot
serves `/v1/systemone` and the OAuth login replaces the base URL with
the account endpoint.
- README and `docs/models.md`: add the model to the classifier tables.

Tests:
- `test/classifier-models.test.ts`: with an OAuth credential, the
request goes to `https://api.enterprise.githubcopilot.com/v1/systemone`
with the Copilot headers, and the model stays available although
`availableModelIds` lists only chat models. With an API key, it goes to
`https://api.individual.githubcopilot.com/v1/systemone`. Both tests fail
without the wrapper.
- Ran against `api.enterprise.githubcopilot.com` with a Copilot login:
bool, choice, and score questions return answers and usage.

Co-authored-by: Hongwei Ma <11957602+Marvae@users.noreply.github.com>
2026-11-02 16:12:43 +01:00
David Brailovsky
4a6a58eb59 fix(ai): keep root $defs in Anthropic non-strict tool schemas
The non-strict input_schema kept only type/properties/required, so
property $refs into root $defs or definitions pointed at nothing. Claude
then produced wrong arguments (a JSON string instead of an object), and
some Anthropic-compatible gateways rejected the request.

Root anyOf/oneOf/allOf are still left out: Anthropic rejects them with a
400. That part of #9557 and #9134 stays open.
2026-11-02 13:12:50 +01:00
David Brailovsky
9ee7586a95 fix(ai): send drop_block for OpenCode Zen and Go Claude models
Enable per-message effort for OpenCode Claude models that support it, which
also sends thinking.block_binding drop_block. OpenCode Go rejected replayed
thinking with a 400 after the tool list or system prompt changed.

closes #10954
2026-11-02 12:37:52 +01:00
Armin Ronacher
db6c084455 Add [Unreleased] section for next cycle 2026-11-02 11:38:21 +01:00
Armin Ronacher
f9a2636556 Release v1.5.0 2026-11-02 11:38:21 +01:00
Armin Ronacher
ddbe02cd20 docs: audit changelogs and add release highlights 2026-11-02 11:03:52 +01:00
Mario Zechner
6536f80129 feat(ai,agent,coding-agent): validate tool arguments with pi-schema, drop TypeBox
pi-schema: opt-in coercion, compile(schema, { coerce: true }) with coerce() and
parse(), the superset of TypeBox's Value.Convert rules and pi-ai's old coercion;
unresolvedRefs option; url and json-pointer-uri-fragment formats.
pi-ai: Tool takes any JSON Schema; validation through pi-schema; Type, Static
and TSchema are no longer exported (breaking). pi-agent-core and pi-durable:
tools on pi-schema, no TypeBox dependency. Coding agent: extensions keep Type,
Static, TSchema and StringEnum through an extension view of pi-ai; built-in
tools stay on TypeBox.
2026-11-01 10:08:42 +00:00
Armin Ronacher
b4c2e92acb fix: remove vulnerable dependency chains
Replace build-time shell utilities with Node filesystem operations and retire the native sandbox example in favor of existing isolation options. Upgrade glob matching and worker-test dependencies and refresh the lockfiles.
2026-10-31 17:54:29 +01:00
robopipi[bot]
c719d5b08e
fix(ai): do not retry Zhipu usage limit errors (code 1308) (#11572)
When a Zhipu/Z.ai (`open.bigmodel.cn`) plan hits its usage cap, the API
returns HTTP 429 with error code `1308`. The cap resets after hours, but
`isRetryableAssistantError()` still marked the error as retryable,
because the bare `429` in the message matched the transient-error
pattern. The agent-level retry then re-sent a request that could not
succeed and slowed down the final error. With default settings that is 3
attempts, about 14 s.

This change adds the Zhipu `"code":"1308"` error code to the
non-retryable provider-limit patterns in
`packages/ai/src/utils/retry.ts`, next to the existing quota and
subscription-limit entries. The pattern matches the JSON code field, not
a bare number, so request IDs or byte counts that contain 1308 do not
trigger it.

Not changed: the provider-level HTTP retry (`retry.provider.maxRetries`,
off by default). It still retries every 429 based on the status code
alone.

Testing:
- `npm run check` passes.
- `test/retry.test.ts` passes.
- Calling `isRetryableAssistantError` directly on the reported message,
`429 {"code":"1308","message":"已达到 5 小时的使用上限。…"}`, returned `true`
before the change and returns `false` after.

Fixes #11537

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-29 15:37:07 +01:00
Armin Ronacher
2e0f175bd5
feat(ai): refactor provider definitions (#11368)
This builds the model catalog from per-provider definitions and
models.dev v2 data.

It eplacer `generate-models.ts` with one `defineProvider()` file per
built-in provider, shared model-family rules (Claude, GPT, DeepSeek,
Kimi, Google), and source adapters. models.dev is read through its v2
API. Outputs and CLI flags are unchanged.
2026-10-28 11:06:10 +01:00
David Brailovsky
068e972c7b fix(ai): remove adaptive thinking metadata test that duplicated generator logic
The test copied the generator's model-ID rule and snapshotted upstream catalog IDs, so it broke on catalog changes without describing behavior beyond the code.
2026-10-28 10:37:22 +01:00
David Brailovsky
44f6851b7f fix(ai): use Anthropic Messages for OpenRouter ~anthropic latest aliases
The ~anthropic/claude-*-latest aliases used Chat Completions, where pi
dropped the thinking signature and OpenRouter ignored the unsigned
thinking on replay. Route them through Anthropic Messages with adaptive
thinking, like the versioned Claude models.

refs #9534
2026-10-28 09:45:28 +01:00
robopipi[bot]
d3398f826a
fix(ai): detect LiteLLM ContextWindowExceededError as context overflow (#11494)
LiteLLM's pre-call context check rejects requests that are too large
with HTTP 400 and a message like:

```
litellm.ContextWindowExceededError: litellm.BadRequestError: litellm._pre_call_checks: Context Window exceeded for given call. ... Max Input Tokens=262144, Got=265262 ...
```

None of the `OVERFLOW_PATTERNS` matched this wording, so
`isContextOverflow()` returned false. As a result, the agent session
never ran overflow compaction and retry, and the turn failed with the
400 error.

This change adds `/context window exceeded/i` to `OVERFLOW_PATTERNS` and
documents the error in the doc comment that lists providers.

Testing:
- Added a regression test in `packages/ai/test/overflow.test.ts` with
the exact error message from the issue. It fails without the pattern and
passes with it.
- `node ../../node_modules/vitest/dist/cli.js --run
test/overflow.test.ts` in `packages/ai`: 32 passed.
- `npm run check` passes.
- Running `isContextOverflow()` directly on the reported message
returned `false` before this change and returns `true` after it.

Fixes #11492

Requested by @rwachtler

Co-authored-by: triagent <triagent@localhost>
2026-10-27 20:27:30 +01:00
David Brailovsky
842c73170a fix(ai): fall back on Bedrock strict tool limits
Retry Bedrock strict tool limit rejections once with strict: "prefer" tools non-strict, as the Anthropic provider already does. Share the limit error messages and the prefer check between both providers.

closes #11416
2026-10-27 14:45:23 +01:00
David Brailovsky
81d6759fc9 fix(ai): declare GPT-5.4 and GPT-5.5 tools at the top level
GPT-5.5 and older namespace tools loaded through additional_tools or tool
search and reject replayed calls without the namespace, so switching to them
after another model's tool calls failed with 400. Drop dynamic tool loading
for these models on OpenAI and OpenCode; keep mid-conversation system messages.

closes #11415
2026-10-27 14:25:45 +01:00
Victor Navarro
177e2ad7e4
fix(ai): use OpenCode protocol-specific endpoints (#11434)
closes #11132

pi sends every opencode and opencode-go request to the shared base
(`https://opencode.ai/inference/v1`, `/inference/go/v1`), Jev included.
this switches them to the documented protocol-specific endpoints, which
is also what models.dev lists per model:

| api | before | after |
|---|---|---|
| openai-completions / openai-responses | `/inference/v1` |
`/inference/openai/v1` |
| anthropic-messages | `/inference` | `/inference/anthropic` (SDK
appends `/v1/messages`) |
| google-generative-ai | `/inference/v1` | `/inference/google/v1beta` |
| jev-1.13 / jev-1.13-free | `/inference/v1` | `/inference/systemone/v1`
|

go uses the same paths under `/inference/go/...`.

changes: base URLs in `generate-models.ts`, the jev URL expectations in
`test/classifier-models.test.ts`, and a stale comment in
`providers/opencode.ts`. `models.generated.ts` has no diff since the
catalog data is gitignored.

verified with the regenerated catalog:
- all 111 opencode/opencode-go models hit their new URL, none 404
- real requests with an OpenCode key returned replies on
anthropic-messages, google-generative-ai, openai-responses,
openai-completions and jev
- go returned 403 "subscription required" from the new paths (my key has
no Go sub), so routing works there too

`npm run check` passes. `./test.sh` passes for ai and coding-agent; the
only remaining failures were timeouts that pass in isolation and pi-env
tests that need cargo locally.

---

_Drafted with AI assistance, reviewed by me._
2026-10-27 10:40:41 +01:00
Christian Klotz
531340f100
fix(ai): remove unused root bedrock-provider shims
./bedrock-provider has resolved to dist/ since a7559f01e (v0.59.0), and
the root files are not in the published package.
2026-10-27 10:39:28 +02:00
Armin Ronacher
363ad78bf0 feat(ai): add GPT-6.1 Terra
Add gpt-6.1-terra to the OpenAI, Azure OpenAI Responses, and OpenAI
Codex providers with official pricing ($1 input, $0.05 cached input,
$1.25 cache writes, $5 output, long-context tier above 272k), tool
search, additional tools, and mid-conversation effort changes, as for
GPT-6.1 Sol. Unlike Sol it accepts reasoning.effort "none", so off is
supported; both reject "minimal" (verified against Codex). Codex serves
it with a 500K context window.
2026-10-26 21:27:08 +01:00
Armin Ronacher
89c921d8b5 fix: harden handling of untrusted URLs, terminal text, and streamed data
- ai: OAuth logins only accept http(s) URLs from servers (shared
  trustedHttpUrl, now also checked by Radius); pi-messages rejects
  contentIndex values that are not the next or an existing block; the
  CLI auth file is written with mode 0600
- ai: OpenCode DeepSeek V4.1 Flash (renamed from V4 Flash upstream) no
  longer sends prompt_cache_retention
- tui: setTitle() replaces control characters; export
  replaceControlCharacters()
- coding-agent: the login dialog only opens web URLs and never prints
  control characters from sign-in, device-code, or info URLs
- durable: partial commits no longer merge a new __proto__ member into
  Object.prototype
2026-10-26 20:46:31 +01:00
Armin Ronacher
1de2d7adea fix(ai): treat Kimi K3.1 Pro models as Kimi K3 family
Kimi Coding, Moonshot, and OpenCode Go generated K3.1 Pro and K3.1 Pro Highspeed with Kimi K2 settings because K3 detection matched exact IDs. Detect the K3 family by ID pattern, and Kimi Coding K3-style models by their effort options. Add implied subscription costs and clamp gateway output limits like K3.
2026-10-24 13:46:57 +02:00
Mario Zechner
f715a15c10 fix(ai,chord,durable,coding-agent): parse streamed tool-call arguments incrementally (fixes #9265)
- ai: StreamingJsonParser / ToolCallArgumentsStream read each argument delta once (tolerant,
  immutable snapshots, linear cost); every provider, pi-messages and the faux provider use it
  and keep stream state (partialJson, partialArgs, index, customInput, streamIndex, Bedrock
  reasoning bytes) off the content blocks; grammar tool input appends per delta
- agent: streamProxy uses the same parser
- chord: Delta append ops own their text (ownedSlice), so ops and replicas no longer keep the
  whole growing string alive
- durable, ai, mcp: bounded error excerpts and summaries own their characters
- coding-agent: tool calls render streamed arguments once per frame, not once per delta
2026-10-24 08:00:07 +00:00
Petr Houška
acf83acfd8 docs: restore existing changelog attributions 2026-10-23 22:10:30 +02:00
Petr Houška
e9c3ac9d98 docs: drop author attribution from backfilled changelog entries 2026-10-23 22:10:30 +02:00
Petr Houška
a8fe8e0adc docs: add missing changelog entries for #9301, #11289, #11290, #11293, #11296, #11347 2026-10-23 22:10:30 +02:00
Petr Houška
a7b94a03f0 fix(ai): preserve literal data in Gemini tool schemas
Restrict const rewriting to schema-bearing keywords and preserve __proto__ names when copying schema maps. Add regression coverage.

Closes #11308
2026-10-23 21:53:34 +02:00
triagent
7314819f0f fix(ai): send const as enum in Gemini tool schemas 2026-10-23 21:53:34 +02:00
triagent
f3fa8bd924 fix(ai): do not count faux cache writes as input tokens
With a sessionId, the faux provider reported newly cached prompt tokens
both as input and as cacheWrite, so input + cacheRead + cacheWrite
exceeded the estimated prompt. Every prompt token now goes either to
cacheRead or to cacheWrite, and input is 0 while caching is simulated.
2026-10-23 21:22:02 +02:00
Christian Klotz
ede2c9b468
docs: fix provider checklist, MCP anchors, and missing commands/settings (#11338)
Fix docs and `--help` drift: update the provider checklist to the
`src/api` layout, fix broken MCP anchors, and document `/mcp`, `pi mcp
add|remove`, `PI_CLEAR_ON_SHRINK`, and `"disabled"` timeouts.

closes #11243
2026-10-23 17:02:41 +02:00
David Brailovsky
e3bc05e871 fix(ai): send fixed low top-level effort with Anthropic per-message effort
Organizations can cap effort, and the previous fixed top-level high was
rejected even when the requested level was low or medium.

closes #10063
2026-10-23 12:11:54 +02:00
Vegard Stikbakke
14c8118412
feat(ai): carry responseModel on pi-messages terminal events (#11265)
This commit makes it so we store the responseModel (i.e. the "phyiscal"
model used) on each PiMessage. This allows e.g. Radius to know which
physical model was used by virtual models (like `cheap`). It's
especially important after we merged
https://github.com/earendil-works/pi/pull/11000
(9113094291), where Pi will drop thinking
traces when the model name doesn't match - with this change we can check
the physical model instead.
2026-10-23 10:54:18 +02:00
Petr Houška
7151099d02 feat(coding-agent): confirm device-code browser and clipboard actions
Knowing how annoying re-logins in corporate environments can be, I think
we can open the browser and copy the code to the clipboard (what the user
asks for) without forcing it upon users (which I assume was the reason
it was removed in 8f0e9251b9).

This change adds optional browser opening and code copying during
device-code login after an additional confirmation (usually Enter).
It should work for all providers, with an optional opt-out controlled
by the provider author, not user settings.

Fixes #9282.
2026-10-22 21:38:58 +02:00
David Brailovsky
3bc6eebacf fix(ai): hold Anthropic system updates past dropped assistant messages
A mid-conversation system message was placed before checking whether the
next assistant message converts to any blocks. When it did not (empty text,
or another model's reasoning-only reply), the update landed before a user
message and Claude rejected the request with a 400.

closes #11091
2026-10-22 12:43:42 +02:00
Armin Ronacher
2685de7a17 fix: update deps for security advisories
Bump vitest to 4.1.12 (vite 8.3.4) and the gondolin example extension to 0.14.0, which drops node-forge. Gondolin's undici moves to 6.29.1.
2026-10-22 11:24:50 +02:00
Vegard Stikbakke
cae8d69e57
fix(ai): use OpenRouter-reported total cost (#10286)
Use authoritative OpenRouter billing for Chat Completions and Anthropic Messages while retaining catalog component estimates and fallback behavior.

Fixes #9980
2026-10-22 11:01:39 +02:00
Vegard Stikbakke
7c22a93d0e
feat(ai): add totalFromProvider flag to usage cost (#11178)
Add Usage.cost.totalFromProvider to record whether cost.total is the
billed cost reported by the provider or an estimate from catalog rates.
Catalog-derived totals set it to false; a missing value also means
false. combineUsage marks a sum as provider-reported only when both
parts are.
2026-10-22 09:02:01 +02:00
Armin Ronacher
58c0b02ce5 Add [Unreleased] section for next cycle 2026-10-22 00:29:24 +02:00
Armin Ronacher
4a080252f0 Release v1.4.0 2026-10-22 00:29:13 +02:00
Armin Ronacher
18e56a069a fix(ai): expect compat on OpenRouter decision models in catalog test 2026-10-22 00:20:24 +02:00
Armin Ronacher
f76b248cae docs(ai,tui,coding-agent): audit changelogs since v1.3.0
Move two post-release entries out of the 1.3.0 sections, add missing entries, duplicate user-facing ai and tui changes into coding-agent, and add New Features.
2026-10-22 00:16:31 +02:00
Armin Ronacher
7f225f25d8 feat(ai,coding-agent): image input for System One classifiers
Jev (TypeSafe, Workers AI), Clef (Workers AI), and OpenRouter decision
models now accept ClassifierContext.images. Each service takes its own
wire form: TypeSafe assets with the opt-in header, Clef images data URLs,
or OpenRouter image_url parts in the state. A new SystemOneCompat
(imageInput, sendImageOptInHeader) selects the form; catalog entries set
it explicitly and models.json provider compat now applies to classifiers.

Adds Clef Omni and Jev 1.14 on Workers AI and fixes Clef Flash and
Workers AI Jev catalog pricing.
2026-10-21 23:53:47 +02:00
Mario Zechner
04a93bad8f 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.
2026-10-21 11:18:16 +00:00
Armin Ronacher
adc5fa9f80 feat(ai,agent,coding-agent): move client identity and provider attribution into pi-ai
pi-ai gets a ClientIdentity request option and createModels() default that choose the User-Agent (detailed with version, short, or custom) and provider attribution headers for OpenRouter, NVIDIA NIM, and OpenCode. Attribution is off by default in pi-ai and on by default in pi-agent-core and pi-durable (HarnessOptions.identity). pi-coding-agent sets its identity from the telemetry setting instead of adding headers itself.
2026-10-21 11:17:02 +00:00
David Brailovsky
6e491cfa95 fix(ai): match retryable status codes only as standalone numbers
Status codes inside request ids or other numbers no longer make an error retryable.

closes #11057
2026-10-21 11:59:38 +02:00
David Brailovsky
29057ece3e fix(ai): retry Codex CLI stream disconnect errors relayed by proxies
closes #11057
2026-10-21 11:54:47 +02:00
David Brailovsky
cedefc5326 fix(ai): handle ChatGPT Business workspace usage limit errors
Sign in with ChatGPT on a ChatGPT Business plan can fail with
subscription_sharing_workplace_individual_usage_limit_exceeded. Only the
workspace admin can raise this limit, so retrying never helps.

- Mark the error as non-retryable
- Add a hint to the error message telling the user to ask their
  workspace admin to increase the limit
2026-10-21 10:11:54 +02:00
David Brailovsky
e5fe9777b9 fix(ai,coding-agent): drop models retired upstream
models.dev deprecated several Baseten, GitHub Copilot, and Together models,
which broke type checks and the default-model test.

- Default GitHub Copilot model: gpt-5.4 -> gpt-6.1-sol
- Move Copilot live tests to gpt-6-luna/gpt-6-sol
- Remove tests and generator entries for retired models, including the
  Together reasoning-only model list
2026-10-21 10:10:55 +02:00
Lucas Meijer
fdce48eb40
fix(ai): drop Codex models rejected for ChatGPT accounts (#11173)
The openai-codex provider authenticates with a ChatGPT account, and the
Codex backend rejects gpt-5.5 and gpt-5.3-codex-spark for those accounts
("not supported when using Codex with a ChatGPT account"). Remove them
from the catalog so model pickers stop offering models that always fail.

Codex e2e tests that used openai-codex/gpt-5.5 now use gpt-6.1-sol, the
openai-codex default. The context overflow test uses gpt-5.6-sol to keep
its prompt smaller.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:55:48 +02:00
Ramiz Wachtler
fbc456d827 fix(ai): expect xhigh thinking for Grok 4.3 and Grok 4.5 2026-10-20 12:44:55 +02:00