Commit graph

1323 commits

Author SHA1 Message Date
Armin Ronacher
63dfe49bfa feat(ai,coding-agent): add DeepInfra, Nebius, Ollama Cloud, Scaleway, SiliconFlow, and StepFun providers
Adds deepinfra, nebius, ollama-cloud, scaleway, siliconflow, siliconflow-cn, stepfun, and stepfun-cn from models.dev. Hosts without verified support for OpenAI-only request features leave out store, developer messages, long cache retention, and max_completion_tokens, and send reasoning_effort only where models.dev lists effort levels. SiliconFlow toggles thinking with enable_thinking; StepFun uses DeepSeek-style reasoning.
2026-10-28 11:06:14 +01:00
Armin Ronacher
538f1e1f6c feat(ai,coding-agent): add Alibaba Model Studio and Z.AI pay-as-you-go providers
Adds alibaba and alibaba-cn (DashScope compatible-mode, Qwen thinking format shared with the Qwen Token Plan) and zai-api and zai-api-cn (Z.AI and bigmodel.cn pay-as-you-go, sharing the GLM Coding Plan compat). They read their own key variables so a plan key never selects metered endpoints.
2026-10-28 11:06:14 +01:00
robopipi[bot]
b9e3eef6c2
fix(coding-agent): keep active tool order when the same tools are set again (#11514)
The system prompt lists tool snippets and per-tool guidelines in the
order of the active tools. When an extension called
`pi.setActiveTools()` with the same tools in a different order (for
example `[...pi.getActiveTools()].reverse()`), the "tools" and "rules"
sections came out different. pi then recorded a "System Prompt Update:
tools, rules" message even though the active tools had not changed. On
providers without mid-conversation system messages, this also rewrites
the prompt head and loses the cache.

`_applyToolLoadout` in `agent-session.ts` now treats the requested names
as a set:
- tools that stay active keep their current order;
- newly activated tools are appended at the end.

Reordering the same set therefore leaves the prompt unchanged. Adding a
tool only appends its bullet. The same rule covers tool lists changed by
`before_agent_start` handlers, since they go through the same function.

Testing:
- Added a test in `test/system-prompt-updates.test.ts`: it reverses the
active tools between two prompts and expects a single system message. It
fails without the change (a second message patches `tools` and `rules`)
and passes with it.
- `npm run check` passes.
- `./test.sh`: all coding-agent suites pass. The only failure is an
unrelated Anthropic model metadata test in `packages/ai`.

Fixes #11495

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-28 10:09:41 +01:00
robopipi[bot]
807a5c051f
fix(coding-agent): keep session memory in line with the file when a write fails (#11486)
When writing a session entry failed (ENOSPC, EBUSY, ETIMEDOUT on network
drives), `SessionManager._appendEntry` had already added the entry to
memory and made it the current leaf. The process kept running, and the
next entry written to disk pointed to a parent that was not in the file.
On resume, pi follows the history only back to that gap: everything
before it disappears. In #11460 that also dropped the first system
message, which declares the tools, so resume turned off every built-in
tool.

`_appendEntry` now undoes the in-memory append if writing fails, then
rethrows. The error reaches the caller as before; later entries chain to
the last entry actually on disk.

Reports of this problem:
- #11460: ENOSPC; resume dropped all built-in tools.
- #10166: failure injected with EBUSY; the next entry on disk points to
a missing parent.
- #10661: ETIMEDOUT on Windows + NFS; resumed sessions showed only the
last few messages. That report also describes NUL holes in the file,
which this change does not address.

**Testing**
- Two tests in `test/session-manager/file-operations.test.ts` make
writes fail with real file permissions: a read-only session file (failed
append) and a read-only session directory (failed first file creation).
They check that the entry is not kept, the leaf is unchanged, the next
entry chains to the last written one, and the reopened session's branch
contains both entries. Both fail without the change and pass with it.
- `test/session-manager/` and `test/default-tools-setting.test.ts` pass
(140 tests).
- `npm run check` passes.

Fixes #11460

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-27 15:58:08 +01:00
robopipi[bot]
269afc66ab
fix(coding-agent): pass thinking level to branch summarization (#11482)
The `/tree` "Summarize" option ignored the session's thinking level.
`AgentSession` already passes `thinkingLevel` into the
`generateBranchSummary` options, but `generateBranchSummary` never read
it, so the request was built without `reasoning`.

For reasoning models on OpenAI Responses and Azure Responses, a missing
`reasoning` becomes `effort: thinkingLevelMap.off ?? "none"`. Models
that don't accept `"none"` then reject the branch summary with a 400
error:

```
Unsupported value: 'none' is not supported with the 'gpt-6-astra-…' model.
```

This change adds `thinkingLevel` to `GenerateBranchSummaryOptions`. It
sets `reasoning` on the request when the model supports reasoning and
the level is not `off`, the same rule compaction uses in
`createSummarizationOptions`.

**Testing**
- Added a test to
`packages/coding-agent/test/branch-summarization.test.ts` that records
the stream options for three cases: a reasoning model at `xhigh`, a
reasoning model at `off`, and a non-reasoning model. The expected
`reasoning` values are `["xhigh", undefined, undefined]`. Without the
fix, the test fails and every case gets `undefined`. With the fix, it
passes.
- Ran `branch-summarization`, `agent-session-branching`,
`branch-summary-extensions` and `compaction-summary-reasoning`: all
pass.
- `npm run check` passes.
- Not tested against a real provider endpoint.

Fixes #11444

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-27 15:38:06 +01:00
robopipi[bot]
c1810bf70f
feat(coding-agent): let inline ctx.ui.custom() components report blocked program status (#11471)
Inline `ctx.ui.custom()` components never report pi as blocked through
OSC 7501 program status. This is on purpose, because the same API hosts
loaders. As a result, a custom dialog that waits for the user (an
approval prompt, the `question.ts` example) shows up as `working`, and
the terminal cannot tell that pi needs input.

The extension dialog stack already stores a blocked status for each
dialog and reports the visible one. This change adds an optional
`blocked` option to `ctx.ui.custom()`:

```ts
ctx.ui.custom(factory, { blocked: { kind: "question", message: "Pick a color" } });
```

The inline branch of `showExtensionCustom()` passes it to
`pushExtensionDialog()`. Components without the option behave as before.
Overlays ignore the option because they are not part of the dialog
stack. `docs/tui.md` documents the option.

Testing:
- Added a test to `test/interactive-mode-extension-dialogs.test.ts`:
  - A custom component that opts in reports `blocked`.
- A plain custom component stacked on top of it clears the blocked
status.
  - Closing the plain component restores the blocked status.
  - Closing the opted-in component clears it.
- The test fails without the change to `interactive-mode.ts` and passes
with it.
- `npm run check`: biome and lint pass, and there are no type errors in
coding-agent. The only errors are existing type errors in
`packages/ai/test` from locally generated model data. They are not
related to this change.

Fixes #11428

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-27 09:27:28 +01:00
robopipi[bot]
5fb9a0b110
fix(coding-agent): skip npm audit and fund output when installing pi packages (#11469)
`pi install`, `pi remove` and `pi update --extensions` run npm without
`--no-audit`, so npm prints its "N vulnerabilities … run `npm audit
fix`" advice after every install. pi gives users no way to clear that
warning, and following it causes harm. In `~/.pi/agent/npm`, `npm audit
fix` runs without `--legacy-peer-deps`, so npm installs the
host-provided `@earendil-works/pi-*` peers there: a second copy of pi.
The comment above `getNpmInstallArgs` warns against exactly that.

The change adds `--no-audit --no-fund` wherever pi already passes
`--legacy-peer-deps` to npm:
- `getNpmInstallArgs` (npm package installs and updates)
- `getGitDependencyInstallArgs` (dependency installs for git packages)
- npm uninstall

pi's managed self-update (`runManagedNpmCi`) already passes both flags.
bun and pnpm arguments are unchanged.

I did not edit `CHANGELOG.md`, per CONTRIBUTING.md.

**Testing**
- Updated the expected npm argument arrays in
`test/package-manager.test.ts`. With the source change reverted, 11
tests fail; with it, all 132 pass.
- `npm run check` passes.
- Ran the built CLI by hand with `PI_CODING_AGENT_DIR=/tmp/pa pi install
npm:lodash@4.17.20`:
- Before the change, the output ended with npm's "To address all issues,
run: npm audit fix" block.
- After, it shows only "added 1 package" and "Installed
npm:lodash@4.17.20".

Fixes #11409

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-27 09:17:54 +01:00
robopipi[bot]
f049787d9e
fix(coding-agent): default openai provider to gpt-6.1-sol (#11467)
The `openai` provider still defaulted to `gpt-5.5`. The same default
applies to API-key logins and to "Sign in with ChatGPT", so logging in
through ChatGPT selected `openai/gpt-5.5`. The reporter says that model
no longer works with a ChatGPT subscription. The `openai-codex` and
`github-copilot` providers already default to `gpt-6.1-sol`.

This change sets `defaultModelPerProvider.openai` to `gpt-6.1-sol` in
`packages/coding-agent/src/core/model-resolver.ts`. The new value
applies in three places: the model picked after `/login` when no model
was set, the fallback model at startup, and the default model for
`--provider openai`. API-key users get the new default too.

I did not test against a real ChatGPT account, so the claim that the
subscription rejects `gpt-5.5` is not checked here.

Testing:
- Updated the assertion in `test/model-resolver.test.ts` ("openai
defaults track current models"). It now expects `gpt-6.1-sol`; the
model-resolver tests pass.
- `npm run check` passes.
- `pi --list-models gpt-6.1` still lists `openai/gpt-6.1-sol`, so the
new default is in the catalog.

Fixes #11442

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-27 09:13:12 +01:00
robopipi[bot]
fecc19e4e9
fix(coding-agent): keep grep context lines aligned with ripgrep when a file has a lone CR (#11465)
When `context` is set, the grep tool reads the file again to print the
lines around each match. It turned CRLF into LF and also turned every
lone CR into LF before splitting the text into lines. ripgrep, which
supplies the match line numbers, counts lines only at LF. A lone CR
therefore added an extra line on pi's side, so every later line was off
by one.

For example, in a file containing `before\rprogress\nneedle\nafter`, a
search for `needle` with `context: 1` showed `progress` as the line-2
match and left out `after`.

The fix keeps the CRLF normalization and splits only on LF, so pi
numbers lines the same way ripgrep does. Displayed lines already have
`\r` removed, so the line before the match now shows as
`beforeprogress`.

**Testing**
- Added a test in `packages/coding-agent/test/tools.test.ts` for a file
with a lone CR searched with `context: 1`. It fails without the fix and
passes with it.
- `test/tools.test.ts`: all 87 tests pass.
- `npm run check` passes.
- Reproduction from the issue. Before the fix:
  ```
  f.txt-1- before
  f.txt:2: progress
  f.txt-3- needle
  ```
  After the fix:
  ```
  f.txt-1- beforeprogress
  f.txt:2: needle
  f.txt-3- after
  ```

Fixes #11398

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-27 09:07:23 +01:00
robopipi[bot]
984cf6643d
fix(coding-agent): resolve provider/id references when the provider id contains a slash (#11397)
`--model "<provider>/<id>"` returned "not found" when the provider id
itself contains a `/`. URL-derived ids such as
`llama-server=http://host:10800` are one example.
`splitProviderPrefix()` in `model-resolver.ts` split the reference at
the first `/`, which turned the prefix into `llama-server=http:`. No
provider has that id, so resolution fell through to not-found.

`splitProviderPrefix()` now compares the text against the provider ids
in the catalog. A provider matches when the text starts with its id
followed by `/`, and the longest matching id wins. Ordinary provider ids
resolve exactly as before.

Testing:
- I added two cases to the `resolveModelReference` table in
`test/model-resolver.test.ts`, using a provider
`llama=http://host:10800`: one plain, one with a `:high` thinking
suffix. Both fail without the change and pass with it. The test runs
every case against the catalog in both orders.
- `node ../../node_modules/vitest/dist/cli.js --run
test/model-resolver.test.ts`: 64 passed.
- `npm run check`: passes.
- Before the change, a standalone check of `resolveModelReference` with
the reporter's exact provider and model
(`llama-server=http://host:10800/unsloth/Qwen3.8-27B-GGUF:BF16`)
returned `not-found`. It now returns `resolved`.
- One test, `test/model-runtime-classifiers.test.ts` ("applies provider
compat from models.json to classifier models"), fails in my environment
both with and without this change. It is unrelated to this fix.

Fixes #11394

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-25 08:48:27 +01:00
robopipi[bot]
5f960a0e2b
fix(coding-agent): pass real path as npm prefix when agent dir is a symlink (#11396)
When the agent directory is reached through a symlink (for example
`~/.pi` linked into a dotfiles repo), pi passed the symlinked path as
`--prefix` to `npm install` and `npm uninstall`. npm then writes lock
entries such as `../../../dotfiles/pi/agent/npm/node_modules/foo`
instead of `node_modules/foo`. Each later install adds one more nesting
level, so `package-lock.json` keeps growing.

This change resolves the install root with `realpathSync` before passing
it to the package manager:
- in `getNpmInstallArgs()`, which covers install and update for npm,
pnpm and bun;
- in `uninstallNpm()`.

The paths pi loads extensions from are unchanged.

**Testing**
- Added a regression test in `test/package-manager.test.ts`. It uses a
symlinked agent dir and checks that install and uninstall get the real
path as `--prefix`. The test fails without the fix and passes with it.
- Wrapped the existing `--prefix`/`--cwd` expectations in
`realpathSync(...)`. They now match on systems where the temp dir is
itself a symlink, such as macOS.
- Ran `npm run check` (clean) and `test/package-manager.test.ts` plus
`test/package-manager-ssh.test.ts` (all pass).
- Manual reproduction with `~/.pi` as a symlink, running `pi install
npm:ms` several times:
- before: lock keys grew to
`../../../dotfiles/pi/agent/npm/node_modules/ms`,
`../../../dotfiles/dotfiles/pi/...`, and so on;
- after: `[ '', 'node_modules/ms' ]`, and `pi remove npm:ms` leaves `[
'' ]`.

Fixes #11358

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-25 08:47:34 +01:00
triagent
8ddc44ec8b fix(coding-agent): write forked session through one fd
forkFrom reopened the target file with appendFileSync for every entry,
which is slow where open/close is expensive. fixes #11253
2026-10-23 21:24:38 +02:00
robopipi[bot]
36be9c612e
fix(coding-agent): decode pi.exec output across UTF-8 chunk boundaries (#11326)
`execCommand()` (the function behind `pi.exec()`) decoded each stdout
and stderr chunk on its own with `data.toString()`. When a multi-byte
UTF-8 character was split across two pipe chunks, each half was turned
into U+FFFD, so the result was corrupted. For example, `"你好"` came back
as `"���好"` while the command still exited with code 0.

This change gives stdout and stderr each their own
`StringDecoder("utf8")`. A decoder holds back incomplete bytes until the
rest of the character arrives. Both decoders are flushed with `end()`
before the promise resolves, on both the success path and the error
path. `waitForChildProcess()` can resolve without a stream `end` event,
so the decoders are flushed there and not in an `end` handler. The
return shape, exit codes, and kill behavior are unchanged.

**Testing**
- Added a regression test to `packages/coding-agent/test/exec.test.ts`.
The child process splits `"你好"` across two writes on both stdout and
stderr. The test fails without the fix and passes with it: `node
../../node_modules/vitest/dist/cli.js --run test/exec.test.ts` reports 3
passed.
- `npm run check` passes.
- Reproduction from the issue: a child that writes the first byte of
`"你好"` and then the rest 100 ms later. Before the fix the result was
`{"stdout":"���好",...}`. After the fix it is
`{"stdout":"你好","stderr":"","code":0,"killed":false}`.

Fixes #11312

Requested by @davidbrai

---------

Co-authored-by: triagent <triagent@localhost>
Co-authored-by: David Brailovsky <davidbrai@gmail.com>
2026-10-23 14:47:34 +02:00
Lincoln
6dfb546785
fix(coding-agent): start when a configured package fails to install (#11304)
fix(coding-agent): start when a configured package fails to install

A package in settings.json that could not be installed (registry
unreachable, package missing, failing git prepare step) crashed startup
with an uncaught rejection and a raw Node stack.

Resolving packages now catches the install failure, skips that package
the same way offline mode does, and reports it as an extension warning:

  Warning: Extension package "npm:foo": failed to install (...).
  Run "pi remove npm:foo" to remove it from settings.json.

Project packages point at `pi remove <source> --local`. `-e` sources
say to check the source instead. Pi tries the install again on the next
start or /reload.

--help and --list-models now print runtime diagnostics, so the warning
shows there too. pi config prints it as well. pi install and
pi update --extensions still fail with a non-zero exit.

closes #11088
2026-10-23 12:27:45 +02:00
muyiyr
c60de42e98 fix(coding-agent): preserve pi.exec spawn errors
Fixes #11085
2026-10-22 21:14:23 +02:00
Petr Houška
1c720d2850 fix(coding-agent): render HTML export for sessions deeper than the call stack
Replace recursive tree walks in the export template (sortChildren, mapNodes,
markActive) with iterative traversals, and build getPath with push + reverse.

closes #11225
2026-10-22 20:51:38 +02:00
Ville Penttinen
64627838b2
feat(coding-agent): add messageId and timestamp to markdown transformer context
* feat(coding-agent): add messageId and timestamp to markdown transformer context

* fix(coding-agent): update compaction render test for messageMeta argument

---------

Co-authored-by: Petr Houška <houskape@gmail.com>
2026-10-22 20:49:38 +02:00
Petr Houška
9359adf0c6 fix(coding-agent): render system messages in HTML export
Render the leading system message as the system prompt and tool list,
and later system messages as named section and tool updates. The header
prompt/tools from agent state remain as a fallback for sessions without
system entries. CLI --export now shows the prompt and tools too (#3464).

closes #11261
2026-10-22 20:37:03 +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
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
Petr Houška
02726086d1 fix(coding-agent): recovery omits tool results with the truncated tool call
Pre-prompt recovery of a recoverable length stop omitted the assistant
message but not the tool results that answered its calls, because only
the end-of-run caller passed them. The orphaned results were persisted
and providers rejected every later request.

_omitRecoveryAttempt now finds the matching tool results in the
projected context itself, so every caller omits the pair together.

closes #11227
2026-10-21 22:44:44 +02:00
Petr Houška
8c4e873899 fix(coding-agent): grep tool ignores ripgrep config via --no-config
The built-in grep tool spawned rg with the inherited environment, so
RIPGREP_CONFIG_PATH (e.g. a config containing --fixed-strings) silently
changed tool semantics: literal: false searches became literal and the
agent got incomplete results with no error.

Pass --no-config so the tool contract is deterministic. Users who want
custom rg behavior can use the bash tool or wrap grep in an extension.

closes #11203
2026-10-21 22:19:03 +02:00
David Brailovsky
bd3c9b8237 fix(coding-agent): activate extension tools registered after the branch recorded its loadout
Resume and /tree restored exactly the tools the branch recorded, so extension
tools registered later stayed inactive. Registered extension tools that the
branch never added or removed are now activated unless they use
defaultActive: false. The whole branch is searched, so tools removed before a
compaction stay inactive. Also fixes the /tree case from #11075.

closes #11062
2026-10-21 16:39:13 +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
adfc14461a fix(coding-agent): apply /compact instructions to split-turn prefix summary
closes #11058
2026-10-21 12:42:17 +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
Petr Houška
22dc6c26f3 fix(coding-agent): expose @earendil-works/pi-ai/models in the extension loader
Extensions importing the public /models subpath (e.g. clampThinkingLevel)
failed with "Cannot find module" because the loader aliased/virtualized
only the pi-ai root, /compat, /oauth, and /providers/all entrypoints.
Add /models to the jiti aliases and the embedded virtual module map,
plus a regression test importing it without a local pi-ai install.

Closes #11156

Co-authored-by: Dante-dan <dantedan@users.noreply.github.com>
2026-10-20 21:47:52 +02:00
Ramiz Wachtler
26293e74f6
fix(coding-agent): render CJK emphasis in HTML export (#11168) 2026-10-20 13:20:37 +02:00
Christian Klotz
8efd314fa3
docs(ai,coding-agent): generate models.json reference from schemas (#11038)
* docs(ai,coding-agent): generate models.json reference from schemas

Add npm run generate:references, which renders docs/models-configuration.md
from the models.json TypeBox schemas, and describe provider and model fields
in those schemas. Overrides get their own descriptions without defaults, and
compat sections cover the Responses API aliases.

closes #10790

* Modify changelog

* docs(ai,coding-agent): use shared thinkingLevelMap and promptCache schemas

Replace the per-use schema helpers with fixed schemas whose descriptions
hold for both models and modelOverrides entries.
2026-10-16 21:53:00 +01:00
Carlos Villela
95364a54ef
feat(coding-agent): default NVIDIA to Nemotron 3.5 Super VL (#10910)
* feat(coding-agent): default NVIDIA to Nemotron 3.5 Super VL

Co-authored-by: prademacher-nvidia <235594174+prademacher-nvidia@users.noreply.github.com>

* docs(coding-agent): changelog for NVIDIA default model change

* fix(ai): run NVIDIA E2E tests against Nemotron 3.5 Super VL

---------

Co-authored-by: prademacher-nvidia <235594174+prademacher-nvidia@users.noreply.github.com>
2026-10-16 22:26:37 +02:00
David Brailovsky
6fcb48670f
fix(coding-agent): remove fixed output limits from summary requests (#11036)
Compaction summaries were limited to 0.8 * compaction.reserveTokens (13107 by default), turn-prefix summaries to 0.5 * reserveTokens, and branch summaries to a hardcoded 4096. On providers where reasoning shares the output limit (OpenAI Responses, adaptive Anthropic), high thinking levels used most of that limit and compaction failed with "generation hit the token cap", even though the model allows far more output.

Tying the limit to reserveTokens assumed the summary request is as large as the full context, so its output had to fit in the reserved space. It is usually much smaller: recent messages kept by keepRecentTokens are not sent, a split turn goes to a separate request, tool results are truncated, and tool definitions and the normal system prompt are not included. Manual compaction can also run long before the context is full. The provider layer already sizes output from the actual request, using the model's output limit reduced to fit the remaining context, as for normal turns.

Summary requests now set no maxTokens and rely on that. compaction.reserveTokens only controls when compaction starts.

generateSummary() and generateSummaryWithUsage() now ignore the reserveTokens parameter.

closes #9512, closes #9075, closes #10908
2026-10-16 13:53:16 +02:00
David Brailovsky
90d863f7c1 fix(coding-agent): use mimo-v2.6-pro as Xiaomi default model
The generated Xiaomi catalogs no longer include mimo-v2.5-pro.
2026-10-16 12:51:37 +02:00
David Brailovsky
9fe3f37162 fix(coding-agent): keep empty quoted prompt template arguments
Empty quoted arguments ("" or '') were dropped, shifting later positional
arguments and preventing positional defaults from applying.

closes #10867
2026-10-14 15:35:39 +02:00
Ramiz Wachtler
910149912c
fix(coding-agent): normalize read line-range labels (#10938)
- share range formatting across the TUI, exports, and examples
- add regression coverage without changing execution arguments
- fixes #9887
2026-10-14 15:20:04 +02:00
Christian Klotz
d7f7e0dfee
fix(coding-agent): resolve model references through one provider selection policy (#10913)
* fix(coding-agent): resolve model references through one provider selection policy

Model references from --model, --models/enabledModels, /model, and auth credential printing now share one resolver. It collects every candidate before choosing a provider, so catalog order never decides, a :<thinking> suffix cannot change the provider, and an ambiguous exact match no longer falls through to a fuzzy match.

provider/id still prefers the named provider's exact or fuzzy match. A gateway exposing provider/id as a raw model id is used when the named provider has no match, or lacks credentials while the gateway has them. Several gateways without a unique authenticated one are reported as ambiguous.

fixes #10160

* docs(coding-agent): shorten model selection notes in CLI reference

* docs(coding-agent): condense #10160 changelog entry

* fix(coding-agent): move model reference resolution into model-resolver

* fix(coding-agent): warn when a model scope pattern matches several providers

* docs(coding-agent): trim model resolver comments to the reasons

* fix(coding-agent): build custom model ids from the resolver's not-found target
2026-10-14 11:00:21 +01:00
David Brailovsky
6f2f164ab7
fix(coding-agent): restore active tools on session resume (#10887)
The SDK always passed initial tools, so the constructor never restored the
loadout recorded in the transcript, and resumed sessions dropped tools activated
during the session. Sessions that use the default tools now restore the recorded
loadout, with --tools +name/-name entries applied to it.

fixes #10785
2026-10-13 15:55:12 +02:00
Vegard Stikbakke
cea257439d feat(coding-agent): configure session share destination
closes #10870
2026-10-13 15:41:13 +02:00
David Brailovsky
e6fb2581df fix(coding-agent): retry transient Windows EPERM when acquiring auth lock
A just-removed auth.json.lock directory can stay delete-pending on Windows
while another process holds a handle, making mkdir fail with EPERM. Retry
this briefly (capped separately from ELOCKED) so real permission errors
still fail fast.

fixes #10801
2026-10-12 16:14:55 +02:00
Armin Ronacher
014873d7d5 feat(coding-agent): show elapsed and total time on codemode rows
The codemode row shows "Elapsed" while the script runs and "Took" once
it finishes, like bash rows. The timer logic moves from the bash renderer
to render-utils, and scripts send an initial update so the timer starts
before the first nested call.

closes #10708
2026-10-12 15:29:18 +02:00
David Brailovsky
6a51a39d42 fix(coding-agent): expand ~ in resource setting patterns
Entries like !~/skills/pdf, +~/x and -~/x in the extensions, skills,
prompts, and themes settings were compared with a literal ~ and never
matched.

fixes #10765
2026-10-12 14:03:34 +02:00
David Brailovsky
ee409a85f6
feat(ai,coding-agent): list only the OpenRouter models a key may use (#10672)
On each OpenRouter refresh, combine the catalog (built-in models plus a newer pi.dev catalog) with GET /models/user for the user's key: keep only the listed chat models and take their context size, max output and prices from it. Image and classifier models are kept. The combined list is saved and replaces the OpenRouter catalog, including offline.

- ai: add personalizeOpenRouterCatalog and RefreshModelsContext.baseUrl, so the request follows a base URL configured in models.json (regional endpoints)
- ai: share OpenRouter price conversion with the generator; negative prices (router models such as openrouter/auto) become 0
- coding-agent: add withPersonalizedCatalog next to withRemoteCatalog
- docs: OpenRouter section in providers.md

fixes #10353
2026-10-12 10:17:40 +02:00
David Brailovsky
a447ba11f3
fix(coding-agent): emit before_agent_start for runs started by custom messages (#10739)
Runs started by pi.sendMessage(..., { triggerTurn: true }) skipped before_agent_start. After their first tool call, the next-turn refresh fell back to the base prompt options and patched away the sections handlers had added, changing the system prompt mid-run and breaking the provider prompt cache.

These runs now go through the same setup as prompt(): compaction check, before_agent_start, tool loadout, nextTurn messages, handler messages, and the system patch. BeforeAgentStartEvent is a union on messageType ("user" or "custom"); custom events carry the starting message.

The session counts as busy from the start of setup, so messages sent while handlers run go into the starting run instead of starting a second one, and an abort requested meanwhile aborts the run once it starts.

closes #10267, closes #5581, closes #10639, closes #10289
partly fixes #10606
2026-10-12 09:53:24 +02:00
Armin Ronacher
24382bedd0 refactor(codemode,coding-agent): decouple MCP from codemode
Tools that reach other tools declare it with reachesTools(input); MCP uses it to wait for the servers a call needs and to decide which codemode or tool_search tool to activate, instead of parsing codemode scripts and comparing parameter schemas. Namespaces get aliases, replacing codemode's MCP-specific __ suffix rule.

pi-codemode no longer knows MCP: MCP_TYPESCRIPT_PREAMBLE and mcpStructuredContentSchema() are removed, and the MCP result schema describes itself, with the server's local $refs relocated so they resolve as before.
2026-10-12 09:19:30 +02:00
Mario Zechner
15e7bdc14c fix(coding-agent,durable): omit thinking from compaction summaries
Replayed thinking is often not part of the live context (for example,
radius drops it, and Anthropic drops blocks that do not match their
request), but serializeConversation included all of it. A Pi Mobile
session at ~996K of 1M context tokens produced 1.38M-token summarization
requests that failed on every attempt. Providers only return summaries of
the reasoning anyway; visible text, tool calls, and tool results record
what happened.
2026-10-11 15:45:13 +00:00
David Brailovsky
f1b2e77f5b fix(coding-agent): load symlinked AGENTS.md in nested git worktrees
The nested worktree dedup compared the context file's realpath with the
main repo's file, so a worktree AGENTS.md symlinked to the main repo's
copy was skipped along with it. Compare the walked directory's realpath
plus the file name instead.

fixes #10681
2026-10-09 09:40:08 +02:00
Christian Klotz
7f9e1198f2
feat(coding-agent): publish configuration schemas (#9880)
* feat(coding-agent): publish configuration schemas

* fix(coding-agent): preserve config schema metadata

* fix(coding-agent): align schemas with public contracts

* fix(ai): place compatibility schema with providers

* fix(ai,coding-agent): align configuration schema contracts

* fix(ai): reuse session affinity schema

* docs(coding-agent): document strict theme validation

* fix(ai,coding-agent): align runtime and generated schemas

* fix(ai,coding-agent): centralize configuration schema contracts

* fix(ai,coding-agent): preserve schema validation contracts

* fix(coding-agent): validate keybinding schema grammar

* fix(ai,coding-agent): refine configuration schemas

* fix(ai,tui,coding-agent): align schemas with runtime behavior

* fix(tui): normalize shifted symbol keybindings

* fix(ai): preserve compatibility auto-detection
2026-10-08 10:01:17 +02:00
Mario Zechner
503c605528 feat(tui,coding-agent): report program status via OSC 7501
Report idle, working, blocked, done, and error to terminals that support
the Program Status Protocol. ProcessTerminal sends the support query with
the keyboard query and reports only after the terminal answers before DA1;
PI_PROGRAM_STATUS=1|0 overrides detection. Interactive mode reports runs,
compaction, extension dialogs, and logins. agent_settled now carries
aborted so cancelled runs report idle.

closes #10607
2026-10-07 19:05:41 +00:00
David Brailovsky
27c7b6ff48 fix(coding-agent): strip ANSI sequences split across user bash output chunks
closes #10504
2026-10-07 15:02:59 +02:00