Commit graph

4129 commits

Author SHA1 Message Date
Vegard Stikbakke
62752fd4c3
fix(coding-agent): serve catalogs without version redirect (#11690)
Update the model catalog protocol to pi.dev after a change to stop
pi.dev redirecting released clients to `?pi-version=` URLs and serve
their catalog directly. The released CLI is unaffected.
2026-11-02 14:42:17 +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
7e7c98a0f5 docs(coding-agent): note branch summary context edits 2026-11-02 11:38:20 +01:00
Armin Ronacher
ddbe02cd20 docs: audit changelogs and add release highlights 2026-11-02 11:03:52 +01:00
robopipi[bot]
d0fe213089
fix(coding-agent): apply context edits to branch summary input (#11680)
When a partial assistant reply fails and automatic retry succeeds, pi
appends a `context_edit` that hides the failed attempt from model
context. Branch summarization (`/tree` with summarize) built its input
from raw session entries through `getMessageFromEntry`, which ignored
`context_edit` entries. As a result, the summarizer received the hidden
attempt again. Content replacements were ignored the same way.

What the change does:
- `prepareBranchEntries` now collects the latest `context_edit` for each
target in the branch.
- It applies those edits to the entries it converts, using the same
`projectContextEntry` logic as the live context and compaction:
  - an omission (`replacement: null`) drops the entry;
  - a replacement swaps its content.
- `projectContextEntry` is now exported from `session-manager.ts` so
both paths share it.
- The existing branch-summary behavior is unchanged: tool results are
still skipped, and compaction or branch-summary entries still become
summary messages.

Testing:
- Added `applies context edits to branch entries` to
`test/branch-summarization.test.ts`. It covers one omitted assistant
entry and one replaced assistant entry. The test fails without the
change and passes with it.
- Ran `branch-summarization`, `branch-summary-extensions`,
`agent-session-branching` and the compaction tests: all pass.
- `npm run check`: biome and the coding-agent checks pass. `tsc` reports
6 errors in `packages/ai` model-catalog tests, and the same 6 errors
occur on unmodified `main`.
- End-to-end reproduction with the faux provider and `AgentSession`: a
failed attempt, a successful retry, then `navigateTree(target, {
summarize: true })`. Before the change, the summary request contained
the failed attempt's text. After the change, it contains only the
successful retry.

Fixes #11602

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-11-02 10:55:30 +01:00
Armin Ronacher
0f21a3319d fix(coding-agent,tui): reuse syntax highlighting and rendered Markdown blocks
Codemode and write rows keep their last highlighting in a CodeHighlighter
that checks theme and grammar changes itself. Markdown reuses the top-level
blocks of its last render, held weakly, and assistant messages reuse their
Markdown instances while streaming. Text.setText() keeps its wrapped lines
for unchanged text.

closes #10971
2026-11-01 14:40:56 +01:00
Mario Zechner
6291b9bcc3 feat(agent): pi-schema's Static types TypeBox-built schemas too, without depending on TypeBox 2026-11-01 11:12:44 +00: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
Petr Houška
e3da248ea9
fix(coding-agent): keep updating git packages when git clean fails (#11648)
Closes #11597

## Problem

On Windows, `pi update` stopped with `git clean -fdx failed with code 1`
for git extensions such as pi-lens.

1. Another pi session has the extension loaded, so a native module
(`@ast-grep/napi` `.node`) or `ast-grep.exe` is open in that process.
2. npm renames the in-use package dir to
`node_modules/@ast-grep/.napi-win32-x64-msvc-<hash>`. Windows allows the
rename, but npm cannot delete the dir afterwards.
3. The next `pi update` runs `git reset --hard`, then `git clean -fdx`.
git prints `warning: failed to remove ...: Invalid argument` and exits
1.
4. pi threw, leaving the checkout at the new commit with the old
dependencies.

The suggested `-ffdx` does not help. Reproduced locally (git 2.52,
Windows): with a running `.exe`, or a `.node` loaded by another node
process and its dir renamed, both `-fdx` and `-ffdx` fail with `Invalid
argument`. Once the process exits, `-fdx` succeeds.

## Fix

In `cleanAndInstallGitDependencies`, a failed `git clean` no longer
aborts the update. pi:

- prints a warning that files may be in use by another pi session
- still runs `npm install`, so dependencies match the new commit
- keeps the `.pi-update-incomplete` marker, so the next update retries
the clean

## Tests

- `test/git-update.test.ts`: new regression test. A failing clean still
updates and installs dependencies, warns, and keeps the marker. The next
update cleans and removes the marker.
- `test/package-manager.test.ts`: updated the test that expected the
throw.

`npm run check` reports `install-lock is out of date`, and
`package-manager.test.ts > should expand ~ in exclusion and exact
patterns` fails on Windows. Both also happen on `main` without this
change.
2026-10-31 18:19:44 +01:00
Petr Houška
70ca8e8960
docs(coding-agent): list external limitations as known issues with remedies (#11647)
Restructures external limitations in the tmux and Windows guides as
"Known issues" with a remedy for each entry.

- `tmux.md`: the tmux 3.6 color reply section becomes a Known issues
table (issue, affected versions, remedy).
- `windows.md`: the conhost problem table moves to a Known issues
section at the end of the page, with a Remedy column instead of the
cause column. The terminal table links to it. Issue links are reduced to
one per row.

Known issues are limited to things outside Pi's control or deliberate
limitations. Bugs Pi can fix stay in the issue tracker.
2026-10-31 17:55:58 +01: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
Petr Houška
6de14ec098
docs(coding-agent): note tmux 3.6 color reply leak in tmux guide (#11645)
Adds a troubleshooting section to the tmux guide
(`packages/coding-agent/docs/tmux.md`, published at
https://pi.dev/docs/latest/tmux) for the color-code text that tmux 3.6
types into the editor at startup.

tmux 3.6 forwards Pi's palette queries to the outer terminal. When a
reply reaches tmux split across reads, tmux treats its bytes as key
presses and sends them to Pi as typed text. tmux 3.7 fixes this
([tmux#4749](https://github.com/tmux/tmux/issues/4749),
[tmux#4793](https://github.com/tmux/tmux/issues/4793)). Pi cannot filter
the text because it arrives exactly like typing.

The section also notes that switching to a fixed theme does not help:
`applyFromSettings()` queries terminal colors regardless of the theme,
which reporters confirmed in #10679 (`--use-theme dark`) and #10256
(`"theme": "dark"`).

Related: #10250, #10679, #10256.
2026-10-31 17:29:36 +01:00
Petr Houška
25426f72ae
docs(coding-agent): document Windows terminal and conhost limitations (#11644)
Adds a "Choose a terminal" section to
`packages/coding-agent/docs/windows.md`
(https://pi.dev/docs/latest/windows), with two tables (last updated
October 2026):

- Terminals and which conhost they use: Windows Terminal (recommended),
VS Code, WezTerm nightly (bundled, up to date); WezTerm 20240203
(bundled, old); Alacritty (built-in unless `conpty.dll` +
`OpenConsole.exe` are provided); classic console window, mintty,
ConEmu/Cmder, Windows OpenSSH (built-in).
- Known problems by conhost type:
- Built-in: crash when switching fullscreen -> regular or using `Ctrl+G`
from fullscreen (#9186, microsoft/terminal#17515).
- Built-in behind another terminal (mintty, OpenSSH): query replies leak
into the prompt, BEL opens the external editor (#10256, #10362, #10742).
- Old bundled (WezTerm 20240203): reply fragments in the prompt (#10717,
#10970).
- Built-in, classic console: reported regular-mode redraw issue (#6300).
- Notes that the fixes exist upstream and reach the built-in conhost
with a future Windows update, timing unknown.

Sources: VS Code defaults `terminal.integrated.windowsUseConptyDll` to
`true` (bundled conpty v1.25). Alacritty loads `conpty.dll` only if
present (`alacritty_terminal/src/tty/windows/conpty.rs`) and its
installer ships only `alacritty.exe`.

The crash was verified locally in the classic console window on conhost
10.0.26100.9807: fullscreen -> regular crashed 3/3 runs and `Ctrl+G`
from fullscreen crashed 2/2 runs (`0xC0000005`).
2026-10-31 16:15:42 +01:00
Armin Ronacher
705d047cb5 fix(coding-agent): tighten system prompt 2026-10-31 16:11:43 +01:00
xu0o0
79006f0619
fix(durable,coding-agent): support Unicode word segmentation in tool search (#11037)
This PR uses unicode-aware segmentation and normalization to handle tool
search for multilingual text.

Fixes #10816

---------

Co-authored-by: Petr Houška <houskape@gmail.com>
2026-10-30 23:34:00 +01:00
Petr Houška
e033f255f0
fix(coding-agent): rank tools matching more query terms first in tool search (#11632)
Follow-up to #11625 (#11331). Same change in durable's copy: #11631.

Problem: #11625 made a query term in a tool's name outweigh any number
of description mentions. That regressed queries where another tool
matches more of the query in its description:
- `screenshot window`: `peekaboo__window` (name has `window`) beat
`peekaboo__image` ("Take a screenshot of the screen, an app, or a
window.").
- `describe app`: `peekaboo__app` (whole name `app`) beat
`peekaboo__see` ("... describe the UI elements of the app.").

Fix: `Bm25Ranker` sorts by coverage first: a tool matching more of the
query's distinct terms, in any field (name, description and schema,
namespace), ranks above one matching fewer, whatever the scores. Within
the same coverage, the score decides as before. A query that is exactly
a tool's name still ranks that tool first.

On the 16-tool set from #11631 (5 servers, 16 queries), this fixes both
regressions and changes nothing else; `search github issues` stays at
rank 5, since `list_issues` lacks "search" and ties on coverage with
`search_code` and `linear__search_issues`.

Changelog: updates the unreleased #11331 entry from #11625.
2026-10-30 23:07:43 +01:00
Petr Houška
9376c8d17c
fix(coding-agent): pass host keybinding hints to extension renderers (#11630)
Fixes #4748.

## Problem

Extensions that import `keyText`/`keyHint` from
`@earendil-works/pi-coding-agent` can resolve their own copy of the
package (their own `node_modules`, or the bundled `cli.js` vs
`dist/index.js`). That copy's keybindings are never set by the host, so
it falls back to a manager with only `tui.*` bindings, and
`keyHint("app.tools.expand", "to expand")` renders as ` to expand`.

## Change

The host passes its own `keyText` and `keyHint` to extension renderers,
the same way it passes `theme`:

- `ToolRenderContext` (`renderCall`/`renderResult` context):
`context.keyHint(...)`
- `MessageRenderOptions` and `EntryRenderOptions`:
`options.keyHint(...)`

All three extend a new exported `KeybindingHints` interface. The docs
and the `debug-provider` example now use the injected functions and warn
against importing them in renderers.

Not done:

- Moving the `pi-tui` keybindings singleton to `globalThis`. Per the
discussion in #7011, extension-facing state should be host-owned and
passed through the extension API instead of shared through process-wide
module state.
- `ctx.ui.keyText`/`keyHint`. No reported case needs them outside
renderers, RPC and print modes never install app keybindings, and adding
them to `ExtensionUIContext` would break SDK hosts that implement it.

## Tests

- `test/extension-keybinding-hints.test.ts`: tool, message, and entry
renderers format a user-overridden `app.tools.expand` binding.
- `npm run check` passes.
2026-10-30 23:01:20 +01:00
Petr Houška
8bc94b19ff
feat(coding-agent): add PI_SELF_UPDATE_COMMAND and selfUpdateCommand for pi update (#11629)
Refs #7756, #5607.

Problem: when a wrapper or another package manager provides Pi (mise, a
`PNPM_HOME` wrapper such as nub, Nix), `pi update` cannot update it and
prints "pi cannot self-update this installation." Users with several
machines have to remember which tool updates Pi on which machine.

Example:

```sh
export PI_SELF_UPDATE_COMMAND="mise use -g npm:{packageName}@{version}"
pi update
# Updating pi with mise use -g npm:@earendil-works/pi-coding-agent@1.4.0...
# Updated pi from 1.3.0 to 1.4.0
```

or in `~/.pi/agent/settings.json`:

```json
{ "selfUpdateCommand": ["nix", "profile", "upgrade", "pi"] }
```

Behavior:

- `pi update` still checks pi.dev first. The command runs only when a
newer release exists, or with `--force`, and the release note prints
first.
- Placeholders come from that release: `{packageName}`, `{version}`,
`{installSpec}` (`<packageName>@<version>`). A package rename announced
by pi.dev flows into them.
- `PI_SELF_UPDATE_COMMAND` takes precedence and is split on whitespace
(no quoting). The setting is argv, for arguments with spaces, and is
read only from global settings, so a project's `.pi/settings.json`
cannot inject a command.
- The configured command replaces Pi's own update logic for every
install method, including installer-managed installs: it is explicit
user intent.
- Exit code 0 does not prove an update. Pi reports "Updated pi from X to
Y" when its own `package.json` now shows Y (in-place updates) or when
`pi --version` from PATH reports Y (wrappers such as mise or Nix that
install each version into a new directory). Otherwise it warns: ``Ran
<cmd>, but `pi --version` reports X, not Y.``
- "cannot self-update this installation" now mentions the env var and
setting.

Docs: `docs/cli.md` (new "Custom update command" section),
`docs/settings.md`, `docs/environment-variables.md`, `--help` env list,
regenerated `schemas/settings.schema.json`.

Tests: unit tests for argv resolution and placeholders in
`test/config.test.ts`; CLI tests in `test/package-command-paths.test.ts`
for env precedence over the setting and the managed install, global-only
setting gated by the version check, and command failure. `npm run check`
passes.

Manual end-to-end check with real mise 2026.10.23 in a sandboxed HOME,
running this branch as the mise-installed 1.3.0 (`PI_PACKAGE_DIR`
pointing at mise's install):

| Scenario | Result |
|---|---|
| `mise use -g npm:{packageName}@{version}` | mise installs 1.4.0;
`Updated pi from 1.3.0 to 1.4.0` |
| Already 1.4.0 | `pi is already up to date (v1.4.0)`, mise not run |
| In-place `npm install -g --prefix <dir> {installSpec}` via the setting
| `Updated pi from 1.3.0 to 1.4.0` |
| Command `true` (exit 0, installs nothing) | ``Ran true, but `pi
--version` reports 1.3.0, not 1.4.0.`` |

This PR is AI-generated.
2026-10-30 22:30:39 +01:00
Petr Houška
cc44fa2169
fix(coding-agent): weigh tool names above descriptions in tool search (#11625)
Follow-up to #11440 for the latest comment on #11331.

Problem: the ranker scored a tool as one bag of words. A query term in
the tool's name counted like a mention in its description, and the
server's instructions were copied into every tool's text, which
lengthened it and lowered its score. Models often add words to a tool
name, so `peekaboo app` ranked `mcp__peekaboo__app` 14th: other peekaboo
tools mention "app" in shorter descriptions, and its own description
says "applications".

Fix: `Bm25Ranker` scores three fields separately:
- name: a query term in the name counts 3x its IDF; the description part
of BM25 is at most 2.2x, so a name match beats any number of description
mentions;
- description and schema text: BM25 as before, length-normalized on this
field only;
- namespace name, description, and instructions: 0.5x IDF per matching
term, without length normalization.

A tool whose whole local name (last `__` segment) is in the query gets a
bonus worth one more match of every query term, so it mostly wins
(`peekaboo app`, `describe app`, `read thread_read state`). A query that
is exactly a tool's name still ranks it first (#11440). Applies to
`tool_search` and codemode `searchTools()`.

Not included: the comment's second point (loading a deferred tool on a
direct call instead of `Tool X not found`). That error comes from
`prepareToolCall` in `packages/agent`, which has no hook for unknown
tools; it needs an agent-loop API change.
2026-10-30 21:56:03 +01:00
Petr Houška
6454d07ec3
fix(coding-agent): start WSL interop helpers from / and report external editor failures (#11627)
Fixes #11592.

## Problem

WSL starts Windows programs in the current directory, translated to
`\\wsl.localhost\<distro>\<cwd>`. When that path exceeds MAX_PATH, every
Windows program fails to start with `Invalid argument`. pi inherits its
cwd when spawning `powershell.exe`, so in a deep directory:

- clipboard image paste returns `null` (the PowerShell backend fails,
stderr is discarded)
- copy via `powershell.exe` (WSL without WSLg) fails
- opening the browser for sign-in fails (`xdg-open` on WSL runs
`explorer.exe`)

Reproduced on WSL (FedoraLinux-44): `powershell.exe` and `cmd.exe` work
from a 225-char cwd and fail from 230. With an image on the Windows
clipboard, `readClipboardImage()` returns the PNG from `/tmp` and `null`
from a 245-char cwd; with this change it returns the PNG from both.
`LongPathsEnabled=1` does not help.

## Changes

- `runClipboardCommand` and `openBrowser` run from `/` on POSIX. These
tools never use the cwd (temp files are passed by absolute path), and
`/` is always the shortest UNC path. `tmpdir()` was not used because
`$TMPDIR` can itself be deep.
- The external editor keeps pi's cwd on purpose, so project-relative
paths and integrated terminals keep working. A Windows editor such as
`code` can still fail from a deep WSL directory, as with git's
`core.editor`. A comment documents this.
- External editor failures were silent: the UI redrew over the error
immediately. pi now shows `Failed to launch external editor: <cmd>`,
`External editor exited with code N; prompt unchanged`, or the signal
name. This shows in the status line for the main editor and inline in
the extension editor dialog.

## Testing

- New tests: clipboard commands run from `/` (POSIX); an editor that
cannot be launched is reported (POSIX); the existing failure test checks
the message.
- Verified on WSL by running the real modules: clipboard image paste
from a deep cwd, and editor success, exit code 1, launch failure, and
SIGTERM.
- On Windows, the 3 existing `external-editor.test.ts` tests already
fail on `main`: the editor command is split on spaces, so `C:\Program
Files\nodejs\node.exe` breaks. Not addressed here.

This PR is AI-generated.
2026-10-30 21:47:58 +01:00
Petr Houška
c2760d32cc
fix(coding-agent): name the package manager in pi update only when it owns the install (#11628)
Fixes #7756.

Problem: `detectInstallMethod()` classifies any path containing `/pnpm/`
as pnpm. An install that only lives under `PNPM_HOME`, for example a
wrapper under `$PNPM_HOME/global-nub/5/node_modules/...`, is detected as
pnpm. `pi update` then correctly refuses to self-update (the path is not
under `pnpm root -g`), but `getSelfUpdateUnavailableInstruction()`
printed:

```
This installation is not managed by a global pnpm install. Update it with the package manager, wrapper, or source checkout that provides it.
```

Fix: name the package manager only when it owns the install (managed but
not writable). All other cases fall through to the neutral message:

```
Update @earendil-works/pi-coding-agent using the package manager, wrapper, or source checkout that provides this installation.
```

`detectInstallMethod()` is unchanged: every user-facing path already
goes through the ownership check, and returning `unknown` would trip the
Windows "only supported for npm and pnpm" guard with a worse message.
The update banner already says `Run pi update` and no longer suggests
`pnpm install -g`.

The pi.dev installer is not affected: it verifies npm ownership with
`npm ls -g` and never infers pnpm from paths.

Tests: regression test in `test/config.test.ts` (fails without the fix).
`npm run check` passes.

This PR is AI-generated.
2026-10-30 21:23:10 +01:00
Christian Klotz
2a46c42080
docs(coding-agent): document which working directory resumed sessions use (#11617)
Closes #11532.

The docs and CLI help did not say which working directory a resumed
session runs in. That directory controls tools, `@path` references,
project configuration, and context files.

## Changes

- `docs/sessions.md`: states that a session keeps the working directory
it was created in, adds a table mapping each entry point to its working
directory, and describes what happens when that directory no longer
exists. Removes a paragraph that repeated the `--session`/`--fork`
descriptions.
- `docs/cli.md` and `--help`: clearer `--continue`, `--resume`,
`--session`, `--session-id`, and `--fork` descriptions. The help only
mentions the directory where it is not obvious (`--fork`).
- `/resume` and `/import` command descriptions, mirrored in
`docs/slash-commands.md`. `/import` notes that it preserves the session
ID and working directory.

| Entry point | Working directory |
|---|---|
| `--continue` | The current directory |
| `--resume`, `/resume`, `--session`, `/import` | The session's working
directory, which can differ from the current directory |
| `--fork`, or a `--session` ID from another directory that you choose
to fork | The current directory, in a new session |

Behavior was verified against `createSessionManager` in `src/main.ts`,
`SessionManager.open`/`continueRecent`/`forkFrom`, and
`AgentSessionRuntime.importFromJsonl`.
2026-10-30 17:22:05 +00:00
Armin Ronacher
be15fe3c17 fix(coding-agent): accept -l in pi mcp login and logout
Report unknown options as typed and suggest a configured server for a
mistyped name.
2026-10-30 14:15:25 +01:00
Armin Ronacher
908aa7b74b feat(coding-agent): prefer the client ID metadata document for MCP OAuth
oauth.clientRegistration defaults to auto: pi's Client ID Metadata Document
when the authorization server supports it for public clients, otherwise
dynamic client registration. Registered clients stay in use. pi mcp add
gains --oauth-client-registration.

closes #11426
2026-10-30 10:36:22 +01:00
Petr Houška
1be312b46a
fix(coding-agent): compare paths case-insensitively on Windows (#11588)
Fixes #10488

## Problem

On Windows, `c:\Users\me` and `C:\Users\me` are the same directory, but
pi compared paths as exact strings (`===`, `startsWith`, map and set
keys). `realpathSync` keeps the input casing, so canonicalizing does not
help.

The reported case, with cwd `c:\Users\me\project` and home
`C:\Users\me`:

1. Ancestor discovery finds `c:\Users\me\.agents\skills`. The exclusion
of the user dir `C:\Users\me\.agents\skills` does not match, so global
skills load as project skills.
2. The same dir loads again as user skills. Dedup does not match either,
so every skill is reported as a name collision.
3. `hasTrustRequiringProjectResources` treats `~/.agents/skills` as a
project resource that requires trust.

The same pattern affected other places:

- Saved trust decisions in `trust.json` were not found when cwd casing
differed, so pi asked for trust again.
- With a custom session dir, `--continue`, resume by id, and the session
list hid sessions recorded with different cwd casing.
- Git worktree `AGENTS.md` shadowing failed when cwd casing differed
from git's paths.
- Local package matching (`pi remove c:\pkg` vs configured `C:\pkg`) and
package identity across global and project settings.
- Extension preload matching, skill scope classification, resource
source info, the config and session selector keys, and the session
import self-check.

Separately, home-to-`~` shortening used an unanchored `startsWith(home)`
in four places, so `/home/me2/x` was shown as `~2/x`.

## Fix

New helpers in `utils/paths.ts`, all based on one comparison key:

- `getPathComparisonKey(path)`: the resolved path, lowercased on
Windows. It does not touch the filesystem.
- `pathsEqual(a, b)`
- `isPathWithin(parent, child)`: true for equal paths or paths inside
`parent`, matching whole path segments.
- `shortenHomePath(path, home?)`: replaces a leading home dir with `~`.

The helpers pick `path.win32` or `path.posix` from `process.platform` at
call time, so tests can exercise real Windows paths (drive letters,
UNC).

`getResourcePathKey(path)` in `source-info.ts` is the dedup key for
resources. Synthetic identifiers (`builtin:<name>`, `<inline:...>`) stay
unchanged, because they are not filesystem paths and their lookup is
case-sensitive. File paths go through realpath plus the comparison key.

The helpers are used only for comparison; stored and displayed paths are
unchanged. Trust store lookups and `set` match keys case-insensitively
on Windows.

Unchanged on purpose:

- The package install-root guards (`resolveManagedPath`,
`pruneEmptyGitParents`, the `node_modules` dependency check) keep exact
string checks. Both sides come from the same root string, and exact is
the stricter option.
- Checks based on `path.relative()` (`getCwdRelativePath`, the changelog
containment check) were already correct: Node's `path.win32.relative`
ignores case.

NTFS can enable per-directory case sensitivity, but it is rarely used
outside WSL interop, so it is ignored (see the comment on
`getPathComparisonKey`).

## Tests

- `test/paths.test.ts`: Windows drive-letter, UNC and root cases, POSIX
case sensitivity, segment-boundary containment, and home shortening,
including root and UNC homes.
- `test/source-info.test.ts`: file paths fold case on Windows; builtin
and inline identifiers keep their case.
- `test/trust-manager.test.ts`: the `.agents/skills` exclusion with a
lowercase `HOME`, plus saved trust decisions matched and replaced across
casing (simulated win32).

An end-to-end test of the reported loader scenario is not feasible on
Linux: the loader resolves paths with POSIX `node:path`, and the
filesystem is case-sensitive.

`npm run check` passes. The coding-agent suite passes, except for 13
failures that also occur without this change: clipboard tool detection
in this environment, and one test that needs built `dist/` output.
2026-10-29 21:53:06 +01:00
Petr Houška
1a09925d39 docs(coding-agent): add changelog entry for -e package refresh
closes #11375
2026-10-29 21:43:11 +01:00
Petr Houška
bec1e0f5a4 docs(coding-agent): document how -e packages are cached and refreshed 2026-10-29 21:43:11 +01:00
Petr Houška
bec60c54ba fix(coding-agent): refresh temporary git sources pinned to a branch ref
A temporary -e git source with a ref was treated as pinned and never refreshed,
so git:host/repo@main stayed on its first checkout. Refresh refs too, skipping
only a commit-hash ref that is already checked out.
2026-10-29 21:43:11 +01:00
triagent
cd6a8f7f3c fix(coding-agent): refresh unpinned temporary npm extensions when a newer version exists
Temporary -e npm: sources were installed once and reused forever. Like
temporary git sources, check npm on load (unless offline) and reinstall
with name@latest or the configured spec when a newer version exists,
keeping the cached install if the refresh fails.
2026-10-29 21:43:11 +01:00
Petr Houška
31ade0e0dc fix(coding-agent): honor saved project trust for projects without trust-requiring resources
A saved Do not trust decision was ignored when the project had no
trust-requiring resources, so ctx.isProjectTrusted() returned true.
The saved decision now takes precedence over auto-trust.

closes #10947
2026-10-29 21:10:03 +01:00
Petr Houška
86dc686cf2 fix(tui): treat ESC+Return with native Shift held as Shift+Enter
Some Apple Terminal setups send ESC+CR for Shift+Enter, which parsed as
alt+enter and queued a follow-up. The native Shift fallback now covers
ESC+CR as well as plain CR.

fixes #11559
2026-10-29 20:17:54 +01:00
robopipi[bot]
e7a0df27c0
fix(coding-agent): pick the smaller of PNG and JPEG when resizing images (#11577)
When an image needs resizing, `resizeImageInProcess` builds a list of
encodings: PNG first, then JPEG at decreasing qualities. It returned the
first one under `maxBytes`. Since PNG was always first, it was used
whenever it fit, even when it was many times larger than the JPEG. The
doc comment says "Try both PNG and JPEG formats, pick the smaller one",
and the code worked that way before v0.62.0. Because of this, photos
were sent as multi-MB PNGs, which made requests large enough to fail or
time out.

This change compares PNG with JPEG at the configured quality at each
size step and tries the smaller one first. It tries the other one next,
and the lower JPEG qualities only after that. Screenshots and other
images where PNG is smaller still go out as PNG.

**Testing**
- Added a regression test to `test/image-processing.test.ts`: a
generated photo-like 1200×800 JPEG resized to 1000 px must come back as
`image/jpeg`. The test fails without the fix (PNG is returned) and
passes with it.
- `node ../../node_modules/vitest/dist/cli.js --run
test/image-processing.test.ts test/image-resize-callers.test.ts`: all
pass.
- `npm run check`: no issues in the changed files. It does report type
errors, all in `packages/ai/test` (model ID unions), which this change
does not touch.
- Reproduction with a photo-like 2400×1620 JPEG (460 KB base64):
  - before: `image/png`, 4,008,480 bytes base64, at 2000×1350
  - after: `image/jpeg`, 255,316 bytes base64, at 2000×1350

Fixes #11551

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-29 16:14:56 +01:00
robopipi[bot]
0d23d53457
fix(coding-agent): warn when an npm package update leaves the installed version unchanged (#11571)
## What was wrong

`pi update --extensions` first looks up the newest version of each npm
package with `npm view`. If that version is newer than the installed
one, pi runs `npm install <name>@latest`. The two commands read
different registry documents, and each is cached separately. Shortly
after a publish, `npm view` can already report the new version while
`npm install` still resolves the old one. npm then exits successfully,
pi prints "Updated packages", and the startup banner lists the same
package again on the next launch. The user can't tell that nothing was
actually updated.

## What the change does

- `shouldUpdateNpmSource` now returns the installed version and the
registry version along with its decision. Both are carried on
`NpmUpdateTarget`.
- After `npm install`, `updateNpmBatch` re-reads each package's
installed version. If the registry lookup succeeded and the version did
not change, it emits a `progress` event with the message: `<source> is
still X (registry reports Y); npm registry metadata may be stale, try
again in a few minutes`.
- The package CLI now prints `progress` events in yellow. Until now
nothing emitted them.

Install behavior is unchanged: pi still installs `@latest`.

## Testing

- New test in `test/package-manager.test.ts`: two packages are flagged
for update and only one is actually upgraded. The warning is emitted
only for the unchanged one. The test fails without the change.
- `test/package-manager.test.ts` passes (129 tests), and `npm run check`
passes.
- Ran the built CLI with an `npmCommand` stub whose `view` reports
`1.0.1` and whose `install` changes nothing:
  ```
  Updating npm:demo-pkg...
npm:demo-pkg is still 1.0.0 (registry reports 1.0.1); npm registry
metadata may be stale, try again in a few minutes
  Updated packages
  ```

Fixes #11536

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-29 15:39:48 +01:00
robopipi[bot]
f14813e965
fix(coding-agent): label skill diagnostics as [Skill warnings] (#11566)
At startup, interactive mode printed every skill diagnostic under the
heading `[Skill conflicts]`. Most of these are validation warnings for
skills that still load, for example `description exceeds 1024 characters
(1508)` or name-format warnings. The heading made them look like name
collisions.

This change renames the heading to `[Skill warnings]`. Validation and
loading behavior are unchanged. The prompt and theme headings are left
as they are.

**Testing**
- Updated the assertion in
`packages/coding-agent/test/interactive-mode-status.test.ts` ("still
shows diagnostics on quiet startup when requested") to expect `[Skill
warnings]`. It fails against the old heading and passes with the change.
All 37 tests in that file pass.
- `npm run check` reports six type errors in `packages/ai` tests about
model IDs. Unchanged `origin/main` reports the same six errors, so they
are unrelated to this change. It reports nothing in `coding-agent`.

Fixes #11521

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-29 14:25:50 +01:00
robopipi[bot]
19ce8059c3
fix(tui): stop writing OSC 133 prompt markers in regular mode (#11565)
In regular TUI mode, user and assistant messages wrote OSC 133
(`A`/`B`/`C`) shell-integration markers straight to the terminal.
Terminals that display OSC 7501 program status (Rex, iTerm2 3.7.5+) read
`133;A` as a new shell prompt and drop the current status. As a result,
the `working` indicator disappeared right after a run started and only
`done` showed up at the end. Fullscreen mode was not affected because it
already strips these markers before writing.

## Change
- `TuiMainScreen.doRender()` (`packages/tui/src/tui-main-screen.ts`) now
strips OSC 133 markers from the start of each rendered line before
compositing and writing. It uses the same pattern as fullscreen
(`OSC133_ZONE_PREFIX`).
- The message components still produce the markers. Fullscreen uses them
internally for prompt navigation and already strips them before writing,
so its behavior is unchanged.
- In regular mode, the terminal's own jump-to-prompt shortcuts no longer
stop at pi messages (added in #1805).

## Testing
- New test in `packages/tui/test/tui-render.test.ts`: no `ESC]133;`
reaches the terminal across a full render and a differential render,
with both BEL and ST terminators. It fails without the change and passes
with it.
- These pass:
  - `packages/tui` `npm test`
- `test/user-message.test.ts` and `test/assistant-message.test.ts` in
coding-agent
  - `npm run check`
- I ran `pi -ne --tui-mode regular` with `PI_PROGRAM_STATUS=1` and a
streaming test provider, capturing all terminal output with `script`.
- Before: `7501 working`, then repeated `133;A`/`133;B`/`133;C`, then
`7501 done`.
  - After: only `7501 idle` → `working` → `done`, and no OSC 133.

Fixes #11519

Requested by @davidbrai

Co-authored-by: triagent <triagent@localhost>
2026-10-29 14:00:16 +01:00
robopipi[bot]
6fed3cfb99
fix(coding-agent): let the SDK stream resolve auth for summaries (#11556)
In sessions created by `createAgentSession`, compaction, branch
summaries and bug-report summaries resolved the stored OAuth credential
in `AgentSession._getSummarizationRequestAuth`, then passed the access
token on as `options.apiKey`. The SDK's stream function calls
`modelRuntime.streamSimple`, which resolves auth again. Because it was
given an explicit `apiKey`, `resolveProviderAuth` took the API-key
override branch instead of using stored OAuth. Built-in providers accept
the token either way. A provider whose API-key path differs from its
OAuth path, such as one that refuses explicit keys, failed every summary
while normal prompts worked.

`createAgentSession` now marks its stream function with
`markRuntimeStreamFunction`. This records it in a module-level `WeakMap`
keyed by the function, mapped to its `ModelRuntime`.
`_getSummarizationRequestAuth` skips pre-resolution when the agent's
stream function is marked for the session's own runtime, so that runtime
resolves auth once, as it does for normal prompts. The check runs after
virtual-model routing. Bare `streamSimple` and custom `streamFn`s still
receive resolved credentials, so the fixes for #6324 and #7579 are
unchanged. If a caller replaces `agent.streamFunction`, the old behavior
applies.

**Testing**
- Added `test/suite/regressions/11520-sdk-summary-oauth-auth.test.ts`.
It builds an SDK session on a provider that has stored OAuth and also an
API-key method, runs a branch summary, and asserts two things: no
`getAuth` call carries an `apiKey` override, and the provider receives
the OAuth access token. It fails without the change and passes with it.
- `npm run check` passes and `./test.sh` passes. I also ran the related
tests: 6324 branch-summary auth, compaction model overrides,
auto-compaction cancellation, SDK stream options, virtual models, and
the compaction and branch-summary tests.
- Manual reproduction: an SDK script with an `openai` OAuth credential
that logs `ModelRuntime.getAuth` during `navigateTree(..., { summarize:
true })`. Before the change there were two calls, the second with
`apiKey = <oauth token>`. After the change there is one call, with no
override.

Fixes #11520

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-29 09:24:53 +01:00
Christian Klotz
c466a35957
fix(coding-agent): cap pi.exec output at 256 MiB (#11546)
Closes #11360.

## Summary
- Cap combined stdout/stderr with `ExecOptions.maxBuffer`, defaulting to
the smaller of 256 MiB and the runtime string limit. Invalid explicit
limits reject before spawning.
- Terminate oversized commands and return `truncated: true`, `killed:
true`, and a nonzero exit code. Close output pipes so writing
descendants cannot keep the call pending, and discard incomplete UTF-8
suffixes rather than adding replacement characters.
- Fix SIGKILL escalation for children that ignore SIGTERM, and simplify
completion to `await` with one cleanup block and result.
- Document the API and add regressions for byte limits, shell
descendants, UTF-8 chunk boundaries, and 32-bit runtime limits.

`ExecResult.truncated` is required. Existing extensions reading
execution results continue working; typed mocks or wrappers constructing
results must add the field.

## Verification
- `npm run build` and the post-rebase `npm run build:offline`
- `npm run check`
- 39 focused execution and extension tests
- `./test.sh` passes after building the env daemon. Earlier runs hit
remote-cleanup and reftable-watcher timeouts; both passed in focused
reruns and the final full run.
2026-10-29 00:13:41 +02:00
Petr Houška
b5fc6dc84f fix(coding-agent): strip private-parameter CSI sequences in stripAnsi
stripAnsi treated <, = and > after ESC [ as final bytes, so ESC[<35;59;1M
(SGR mouse report) left 35;59;1M in tool output. Accept one private prefix
byte only directly after a CSI introducer (ESC [ or 0x9B). After a bare ESC
these bytes stay final bytes, so ESC= and ESC> (keypad modes) keep the
digits of the text that follows.

closes #11377
2026-10-28 20:14:09 +01:00
Petr Houška
3bae85274a fix(tui,coding-agent): handle selection page keys in all scrolling lists
tui.select.pageUp/pageDown were configurable but ignored by SelectList,
SettingsList, editor autocomplete, and most coding-agent selectors. In
fullscreen mode, the default PageUp/PageDown also scrolled the transcript
instead of reaching the focused list.

- add getSelectPageIndex() and handle page keys in SelectList, SettingsList,
  editor autocomplete, and the model, scoped models, auth, user message,
  settings submenu, llama search, and /mcp selectors
- add Component.capturesViewportInput: a focused component that sets it
  receives fullscreen viewport keys instead of the transcript, as a focused
  overlay already does
- let page keys move an open autocomplete menu before app actions, so a
  remapped Ctrl+V pages the menu instead of pasting an image

closes #7629

Co-authored-by: muyiyr <940955635@qq.com>
2026-10-28 19:05:21 +01:00
Cristina Poncela Cubeiro
b4b72a3510
feat(coding-agent): extensions changelog (#8790)
Addresses #5958.

Adds `changelogPath` to `PiManifest`. Safe package-relative changelog
resolution, with `CHANGELOG.md` fallback, and carries it through
`PathMetadata`/`SourceInfo`.

Added extension changelog handling like Pi’s own changelog.

If the package exposes `pi.changelogPath` or has `CHANGELOG.md`, Pi
stores the last seen package version in settings. On each fresh
interactive launch, we compare the stored version to the installed
package version. Then parses the changelog entries in between, shows an
Extension Updates section, then marks the current version as seen. The
first time an extension is seen, its version is recorded without showing
a changelog, same as Pi's own on a fresh install. `collapseChangelog`
condenses this to one line per package.

Any update that bumps the extension's version shows this on the next
launch, e.g.:
```bash
 pi update --extensions
 pi update --all
 pi update <extension-source>
 pi update --extension <extension-source>
 pi update --self --extensions
 pi update pi --extensions
 pi update self --extensions
```

<img width="798" height="626" alt="Screenshot 2026-08-28 at 16 34 15"
src="https://github.com/user-attachments/assets/c882f01c-94b9-4857-b9b7-243d88c0d437"
/>
2026-10-28 11:25:04 +01:00
Petr Houška
e509d7edc7
docs(quickstart): add Windows installer tab (#11231)
The Quickstart only showed the macOS/Linux `curl` installer, even though
`install.ps1` installs Pi the same way: a managed install pinned by the
published lockfile, updated with `pi update`. This PR adds the Windows
installer as a second tab next to the existing one. All other sections
stay the same.

````markdown
Install Pi with the installer on macOS, Linux, or Windows:

```bash tab="macOS / Linux"
curl -fsSL https://pi.dev/install.sh | sh
```

```powershell tab="Windows"
powershell -c "irm https://pi.dev/install.ps1 | iex"
```

Both installers pin all dependencies and update Pi with `pi update`. ...
````

The tabs are labelled by platform rather than by shell: newcomers know
their OS, and the command also works when pasted into cmd.

Tab rendering on pi.dev comes from earendil-works/pi.dev#183. This PR
can merge before or after it. Until the renderer ships, and on GitHub,
the page shows two ordinary code blocks, and the prose still reads
correctly.

## Screenshots (with earendil-works/pi.dev#183)

| | Default | Windows tab selected |
|---|---|---|
| Dark | ![dark
default](https://gist.githubusercontent.com/petrroll/27308d9d8522c90ea5b290b0c3ba9862/raw/cad05d1cf643903955af7282763e22ff2a101e73/dark-unix.png)
| ![dark
windows](https://gist.githubusercontent.com/petrroll/27308d9d8522c90ea5b290b0c3ba9862/raw/cad05d1cf643903955af7282763e22ff2a101e73/dark-windows-tab.png)
|
| Light | ![light
default](https://gist.githubusercontent.com/petrroll/27308d9d8522c90ea5b290b0c3ba9862/raw/cad05d1cf643903955af7282763e22ff2a101e73/light-unix.png)
| ![light
windows](https://gist.githubusercontent.com/petrroll/27308d9d8522c90ea5b290b0c3ba9862/raw/cad05d1cf643903955af7282763e22ff2a101e73/light-windows-tab.png)
|
2026-10-28 10:10:44 +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]
f6ee9ce9c1
fix(coding-agent): summarize union and multi-line codemode output types in tool descriptions (#11513)
With `codemode.mode: "on"`, each declared tool gets a description suffix
that says what a script call resolves to. For an object output schema,
`describeOutput` shortens this to field names. Any other schema fell
back to the whole rendered TypeScript type, collapsed onto one line. A
tool with a union output schema therefore put its full output type,
nested fields and comments included, into every request. The reporter's
`process` tool, a union of 7 variants, added about 8,000 characters.
Collapsing the type onto one line also left `//` comments in the middle
of the text.

This change:
- **Unions:** for `anyOf`/`oneOf` schemas where every variant is an
object, the suffix lists each variant's field names, joined by ` | `.
- **`const` fields:** `describeFields` now shows the value of `const`
fields, so a discriminator like `action` stays visible. For example: ``
`{ action: "start", process } | { action: "list", processes, truncated?
}` ``.
- **Other multi-line types**, such as an array of objects: instead of
the inlined type, the suffix says `` a value; `await
describeTool("name")` shows its type ``. `describeTool()` already
returns the full declaration.
- **Single-line types** such as `number` are unchanged.

Testing:
- I added a test in `test/suite/agent-session-codemode.test.ts` that
checks the suffix for three tools: one with a union output, one with an
array-of-objects output, and one with a `number` output. It fails
without the change and passes with it.
- `node ../../node_modules/vitest/dist/cli.js --run
test/suite/agent-session-codemode.test.ts
test/suite/agent-session-mcp.test.ts` passes, including the existing MCP
description assertions.
- `npm run check` is clean.

Fixes #11501

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-28 09:58:20 +01:00
robopipi[bot]
7391dbb0f8
fix(coding-agent): show model scope in the fullscreen header instead of the shell (#11512)
In fullscreen mode, the startup `Model scope: …` notice was still in the
shell after pi exited. With `fullscreenExitOutput: "resume-hint"`, the
shell showed it above the resume command.

Cause: `InteractiveMode.init()` printed the notice with `console.log`
before the renderer started. In fullscreen mode that output goes to the
terminal's normal screen. Pi then draws on the alternate screen, and
when it exits the terminal switches back to the normal screen, where the
notice is still visible.

The change:
- Building the notice text moves into `getModelScopeNotice()`.
- Regular mode prints it with `console.log` before the TUI starts, as
before.
- Fullscreen mode adds it to the header container below the built-in
header. It uses a `ThemedText`, so the text is built after the terminal
reports its colors and follows theme changes.

Since fullscreen is the default `tuiMode`, this applies to every user
who has `enabledModels` set and quiet startup off.

Testing:
- Added
`packages/coding-agent/test/interactive-mode-model-scope.test.ts`. It
checks two things for fullscreen: the notice renders in the header and
`console.log` is not called, and nothing is added when startup details
are hidden.
- `npm run check` passes.
- Manual check in tmux with `enabledModels` set:
- fullscreen + `resume-hint`: before the change, `Model scope:
claude-sonnet-4-5, claude-opus-4-5 (Ctrl+P to cycle)` was left in the
shell after `/quit`. Now the line shows inside pi's header and the shell
is clean after exit.
- `tuiMode: "regular"`: unchanged, the line is still printed above the
header.

Fixes #11508

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-28 09:54:06 +01:00
robopipi[bot]
88ffcdc734
fix(coding-agent): report a deleted working directory instead of crashing on startup (#11511)
Starting `pi` from a directory that has been deleted crashed with an
uncaught Node `ENOENT ... uv_cwd` stack trace. This also happened for
`--version` and `--help`. The cause is the unguarded `process.cwd()`
call in `main()` (`packages/coding-agent/src/main.ts`).

`main()` now catches `ENOENT` from that call, prints one line telling
the user to change to an existing directory, and exits with code 1.
Other errors are rethrown unchanged. Argument handling is otherwise
unchanged: `--version` still needs a valid cwd, but it now gets the
readable error instead of a crash.

**Testing**
- Added `test/deleted-cwd.test.ts`. It stubs `process.cwd()` to throw
ENOENT and checks that `main()` prints the message and exits with code
1. It also checks that a non-ENOENT error is rethrown. The first test
fails without the change and passes with it.
- `npm run check` passes.
- Manual check with the built CLI. Before, all three commands below
printed the `uv_cwd` stack trace. After, each prints `Error: The current
working directory no longer exists. Change to an existing directory and
try again.` and exits 1:
  ```sh
  d=$(mktemp -d); cd "$d"; rmdir "$d"
  pi -ne --version; pi -ne --help; pi -ne
  ```

Fixes #11509

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-10-28 09:51:39 +01:00
Christian Klotz
85c2ee3e76
docs(coding-agent): restore the full extensions guide, corrected against current source (#11498)
## Summary

The docs refresh (#9898) cut `docs/extensions.md` from about 3,100 lines
to 130, dropping API reference and tool guidance. #11039 restored part
of it. This PR restores the full guide and corrects it against current
source.

- **Structure:** the original sections, with the ASCII lifecycle diagram
replaced by three Mermaid diagrams (run, turn, session) and ordered hook
lists, plus a handler composition table.
- **Merged:** content added after the cut: tool exposure, calling other
tools, structured output, MCP servers, `registerToolRenderer`, virtual
models, `provider_stream_event`, `before_agent_start` `messageType`.
- **Corrected:** removed or renamed APIs (`modelRegistry.stream()`,
prompt sections are `<tools>`/`<rules>`, `builtin:read` paths,
`highlightCode`), wrong behavior claims (errors only by throwing,
`model_select` on restore, `sendUserMessage` always triggering a turn,
override warning, `truncateLine` bytes), examples that did not
type-check, links to removed `tui.md` patterns and `rpc.md` anchors, and
wrong API lists in the examples table.
- **Kept:** anchors that other docs and changelogs link to, as `<a id>`
tags.
- **Fixed example:** `reload-runtime.ts` tool queued `/reload-runtime`
with `deliverAs: "followUp"`. Queued text reaches the model as a plain
user message, so the command never ran and Pi never reloaded. The tool
now sets a flag and returns `terminate: true`; an `agent_settled`
handler sends `/reload-runtime` with `expandPromptTemplates: true`,
which runs after the settled handlers finish, while the agent is idle.

In the long run it may be better to revisit how API reference is,
provided to the agent, especially since more capable models do explore
the source without it.

## Validation

- `npm run check`
- Edited TypeScript examples type-check against current types
- All relative links and anchors in `extensions.md` resolve, including
inbound anchors from other docs
- `reload-runtime.ts`: faux-provider harness test (temporary, not
committed) shows one reload after the run settles and no extra model
request; the old version never reloads. Also verified in interactive
mode with a real model.
- Reviewed by GPT-6 Astra and GPT-5.6 Sol against source; all findings
applied
2026-10-28 00:31:24 +02:00
Julien Chaumond
59ca40cf58
docs(coding-agent): update llama.cpp setup for llama.app (#10179)
update the llama.cpp docs to use the llama.app installer and the new
`llama serve` command

commit by @hanouticelina

disclaimer: my julien-cto agent helped me write this

---------

Co-authored-by: Celina Hanouti <hanouticelina@gmail.com>
Co-authored-by: Mario Zechner <contact@badlogicgames.com>
2026-10-27 16:21:37 +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