Commit graph

7505 commits

Author SHA1 Message Date
Armin Ronacher
4d72d05bcd fix(coding-agent): send provider tokens in pi mcp list
Servers with auth.provider, such as Radius, were reported as needing sign-in because the shell command sent no token. pi mcp now reads and refreshes the provider's /login token, and its hints and login/logout point to /login <provider>.
2026-11-04 17:59:41 +01:00
David Brailovsky
d6339fb097 feat(p2): TUI side panel; /artifacts opens artifacts beside the chat
- Runtime: ctx.panel shows one component right of the screen's body (about 40%,
  hidden below 100 columns, dock stays full width). F2 moves the focus into it
  (viewport and select keys scroll it, Escape returns), /close-panel closes it.
  Closes on conversation switch and facet reload, like overlays.
- Artifacts: Enter opens the artifact in the panel when the terminal fits it,
  else inline as before; the panel follows the artifact's newest version.
- Fix: /artifacts rows are keyed by name and version, so every version of an
  artifact can be selected.
2026-11-04 17:51:48 +01:00
Mario Zechner
edc9c69892 feat(chord): the reload suite, kit timeouts and base marks, four reload fixes
An end-to-end reload suite (a kit app, a worker, a real Chromium page; one test per row of design's reload table).
The kit gains timeouts and base (base bundles' inputs and worker entries mark base). Fixes: a call crossing a failed
or a removal gets the cause, not not_ready; a call sent with an old method index is decoded by the names its consumer
had until the new bound is acked; a page retries a failed script fetch; applyChanges runs one at a time.
2026-11-04 16:47:07 +00:00
Mario Zechner
fcebc09956 docs(chord): reload table corrected by the audit and the reload suite 2026-11-04 15:55:13 +00:00
Mario Zechner
d8981c7ba0 feat(p2): sidebar branch rows behind the session's expand control
Branch rows are hidden until the session row's expand control (the
subagents' chevron) opens them, above the subagents; the control also
appears for a session with two or more branches and no subagents, with
the branch count beside the subagent count. Showing a subagent still
expands the list; showing any other non-main conversation no longer
does (right after a /tree switch the page shows the new branch before
the row names it main).
2026-11-04 16:15:33 +01:00
Mario Zechner
6515a79772 feat(chord): calls in flight survive a loading unbind
A hold unbinding with loading (a restart, a parked swap, a live failure retrying) keeps its calls in flight; each
gets one answer from what it waits on (the instance it runs on, or the gate). Calls sent by the old method names
before the consumer learns of the loading wait at the gate too. failed and removal still reject.
2026-11-04 15:12:14 +00:00
Mario Zechner
395f3e0318 docs(chord): what a user sees during a reload 2026-11-04 14:59:02 +00:00
Mario Zechner
0a180b049a feat(p2): keep serving older Sessions, Catalog and SessionTree versions
Pages and TUIs loaded before a deploy hold the contracts they were built
with; without accepts they wait unbound (contract_mismatch) until they
reload. The histories are now written with evolve from the released
versions, and the providers accept them:

- Sessions 3 (rows without main and branches) and 4 (a null branch label
  reads as the session title); v4's treeSelf arguments pass as they are
- Catalog 2 and 3, the same row mappings
- SessionTree 1: the tree without labels (no branches, no node
  conversation); rename is new and needs nothing

test/p2/contract-versions.test.ts binds the released declarations
against the current server.
2026-11-04 15:38:43 +01:00
Mario Zechner
7670c8be43 feat(p2): stored, renamable /tree branch labels
p2.main version 2 stores a label per branch: { conversation, label }.
A new branch is labelled in the commit that forks it, with the start of
the message the owner picked (the answer for "Branch from here", the
edited user message for "Edit"). Conversation 1 keeps null, which every
UI shows as the session title. Version 1 migrates to null labels; at
start the worker fills the other branches once from their fork points.

- SessionTree v2: rename (owner), Tree.branches, TreeNode.conversation
- Sessions v5 / Catalog v4: a branch label may be null
- the worker reports p2.main changes; the derived labels are gone
- web: a pencil beside each sidebar branch row opens a rename sheet
- TUI: /tree shows branch labels; Shift+L renames the selected branch
2026-11-04 15:38:43 +01:00
Mario Zechner
d6bf3549c0 docs(chord): calls in flight survive a loading unbind 2026-11-04 14:36:08 +00:00
David Brailovsky
d3d9c5aeb4 feat(p2): quiet-tools plugin and plugin settings in the TUI
quiet-tools draws every tool call as one line; Ctrl+O, a click or a failure
shows the full content. Its /settings row switches back to the tools' own cards.

TuiSettingFields lets plugins add /settings rows, stored at
settings.plugins.<plugin>.<name> of the visitor's prefs. Tool renderer
resolvers receive the settings; a change redraws every card.
2026-11-04 14:55:06 +01:00
David Brailovsky
e885255061 feat(p2): /tree, branches of the main conversation as Durable forks
Each branch is a top-level conversation forked from another at an entry;
the session-scoped p2.main document lists them and names the current one.
Earlier branches are read-only. The main agent's per-agent state
(subagents, artifacts, voice and chat mode, pending move) stays on
conversation 1 for every branch.

- tree plugin: SessionTree (tree, navigate, select); the worker reports
  main and branches to the catalog (Sessions v4, catalog schema 4)
- TUI: /tree and double Escape (pi's default "tree"), a tree selector
  adapted from coding-agent
- web: Edit on user messages, Branch from here on answers, branch rows
  under the session in the sidebar
- main-agent checks use the branch list instead of conversation 1
- search indexes each branch's own entries

Design: apps/p2/docs/tree.md
2026-11-04 13:40:36 +01:00
Mario Zechner
0a730c8a76 fix(p2): archiving a viewed session shows no error and starts no refused worker
Archiving from the session sheet navigates away first, which unmounted SessionOptions and released its Sessions
reference; Chord cancels a released reference's calls, so the archive call rejected with "p2.sessions: released",
shown as the composer toast over the owner-notice lane. The archive now holds a reference of its own until it is done,
and the server runs its steps in a root of its own.

The archive stopped the worker with Hosts.stop, which restarts it for its holders: the page's lingering
conversation started a worker the archived session refused (resolve_failed, "Worker: Session is archived" notice).
SessionWorkerControl.suspend stops it and leaves its holders suspended until a restore (retry) or their release;
archive, a failed fork's archived copy and an archived session's index rebuild use it.
2026-11-04 13:19:01 +01:00
Mario Zechner
15a5114599 fix(chord): a page cannot claim chord.* attributes on its spans; tutorial layout
The server drops a page's own chord.* attributes and writes chord.host and the principal hook's result itself; tests
for the hook returning nothing, the NOOP adapter and calls over a link.
2026-11-04 12:13:07 +00:00
Mario Zechner
ff33ef42c4 feat(chord): telemetry from pages and workers stored by the server, who a span was for
The four telemetry gaps (rows on commits, chord.host, microsecond times, a read-only store) and: pages record and
send span batches over their link to the server's ingest object (checked, capped, rate-limited, stamped browser and
with the link's principal); workers send theirs too, so the server is the store's only writer; principalAttributes
lets an app choose what a span records about its principal. The tutorial shows a real page's click traced to the
worker, with the trace viewer.
2026-11-04 11:59:14 +00:00
Mario Zechner
80f7a11494 docs(chord): the server is the one place telemetry is stored 2026-11-04 11:01:31 +00:00
Mario Zechner
f666087695 docs(chord): the principal hook caches for itself; a Chord host option 2026-11-04 10:58:46 +00:00
Mario Zechner
72dd26e084 docs(chord): spans record who they were for, through an app hook 2026-11-04 10:57:57 +00:00
Mario Zechner
596523e01e docs(chord): telemetry from browser pages decided 2026-11-04 10:54:06 +00:00
github-actions[bot]
818c42cdeb chore: update Nix model catalog pin 2026-11-04 10:53:01 +00:00
Mario Zechner
0efe50cb6d docs(chord): tutorial chapter on telemetry, with a small trace viewer 2026-11-04 10:40:17 +00:00
Danila Poyarkov
0f4d5e960a
fix(ai): allow prompt cache keys for compatible proxies (#9442) 2026-11-04 11:36:35 +01:00
Mario Zechner
a3a1005053 fix(coding-agent): fix CI type check and image-only steering test
- type-check subagent-extension.test.ts against the extension view like the other subagent test
- update the test to current ToolRenderContext and ExtensionToolContext types
- use a decodable PNG in the image-only steering test; the fake data was omitted by image processing
2026-11-04 10:34:45 +00:00
Dale Lowe
3e4f872654
feat(ai): add Qwen Token Plan Individual CN provider (#7989)
Closes #7847
2026-11-04 11:32:45 +01:00
gaoyk19
14b804d809
feat(ai): add requiresNonNullAssistantContent compat flag (#8118)
Closes #8063
2026-11-04 11:29:54 +01:00
muyiyr
25490ea49b
fix(tui): prevent cursor marker leaks (#9441)
Closes #9257, closes #9332
2026-11-04 11:26:48 +01:00
Michael Renner
aba42ae6b9
fix(coding-agent): make subagent progress and failures reliable (#8250)
The shipped subagent example could say a child was done while it was
still working, lose the useful part of process failures, and return a
normal-looking tool result when the delegated work had actually failed.
Single and chain output could also exceed the normal tool limit.

This also adds a dedicated regression suite covering progress state,
cancellation, process failures, output limits, cwd-based agent
selection, and persisted tool-result error state.

After this change:

- queued, running, completed, failed, and aborted work stay distinct
throughout the run;
- interrupting the parent run preserves partial child results, stops
queued work from starting, and escalates when a child ignores SIGTERM;
- signal exits, spawn failures, provider errors, stderr, and assistant
output remain visible instead of collapsing to `(no output)`;
- failed subagents become failed tool results, so the parent model can
react instead of treating them as successful calls;
- single and chain results stay within the normal 50 KB / 2,000-line
limit;
- project agents are selected and confirmed from each task's actual
working directory rather than the dispatcher's directory.

## Further potential improvements

These are useful follow-ups, but they are separate product and storage
decisions rather than part of the failure-handling fixes in this PR.

<details>
<summary>Potential follow-ups</summary>

### Keep persisted transcripts bounded

Subagent tool results persist the full child message history in
`details`, which can make parent session files large and slow to reload.
A follow-up could compact that history or spill it to disk.

### Define an aggregate parallel-output budget

Parallel mode still gives every task its own 50 KB budget. A follow-up
could keep every task represented while enforcing one overall
tool-result limit.

### Report child usage to the calling session

Per-child token and cost usage is visible in result cards but is not
added to the calling session's totals.

### Isolate malformed agent definitions

One malformed YAML agent can still break discovery for every valid agent
in the same directory.

### Limit subagent recursion

A nesting limit would prevent accidental process and paid-request
multiplication when subagents can invoke the subagent tool again.

* https://github.com/earendil-works/pi/issues/8195

### Add a configurable timeout

Unattended runs would benefit from a generous opt-in timeout for
children that never complete.

</details>

## Related issues

### Spawn failures lose their diagnostics

This directly addresses the reported case where a nonexistent child
working directory produces `(no output)` instead of the actual spawn
error. The regression suite includes that same reproduction.

* https://github.com/earendil-works/pi/issues/8129

### Persisted child history can bloat sessions

This PR intentionally keeps full child history in tool-result details,
so session compaction or spill-to-disk remains follow-up work rather
than something fixed here.

* https://github.com/earendil-works/pi/issues/7452
2026-11-04 11:24:12 +01:00
Mattia Cerutti
dbc61d647b
feat(durable): expose thinking budget and websocket timeout options (#10410) 2026-11-04 11:22:51 +01:00
wren
51313540a4
fix(coding-agent): preserve pasted text during editor restoration (#10146)
Closes #9809
2026-11-04 11:20:58 +01:00
Gangqiang Yao
8a3e163b0d
fix(coding-agent): prevent prompt and tree navigation overlap (#9155)
Closes #9154
2026-11-04 11:18:25 +01:00
jingtao
4b97777378
fix(ai): mark Claude Sonnet 5 as not supporting temperature (#7961) 2026-11-04 11:16:16 +01:00
Julien Chaumond
592667dffd
docs(coding-agent): llama.cpp tweaks (#11624)
use llama.cpp's default port and remove the stale `--jinja` note

same change as #11616 by @pcuenca with the original commit preserved

disclaimer: my julien-cto agent helped me write this

Co-authored-by: Pedro Cuenca <pedro@huggingface.co>
2026-11-04 11:07:48 +01:00
Alexey Zaytsev
aceb1bd128
feat(coding-agent): upgrade Mermaid terminal rendering (#8158)
Replaces grok-mermaid with lovely-mermaid 0.3.3. Closes #8157
2026-11-04 11:06:51 +01:00
Pier Carlo Cadoppi
e6865ec7e1
fix(coding-agent): realpath extension entries before jiti import (#8112)
Closes #8092
2026-11-04 11:04:24 +01:00
Mario Zechner
378d45a05b feat(chord): telemetry, spans across hosts (slice 9)
Chord's telemetry schema; spans for calls, methods, relays, holds, activations, natives, loads, reloads, resolves,
revocations, routines, jobs and HTTP requests; trace carriers on hold and call (bounded, printable ASCII) continued by
relays and workers; events and host-wide metrics; the app kit passes an adapter to its hosts. renderTrace escapes
control characters. Example 62 prints a trace across three hosts.
2026-11-04 10:02:48 +00:00
Giannis Papakostas
ee962fb4db
fix(plan-mode): make progress tracking robust and tolerant (#7950)
## What

Hardens the plan-mode example extension's step-progress tracking.
Previously, completion markers were only recognized in the exact
`[DONE:n]` form inside visible text blocks, so steps frequently never
got checked off during execution.

## Changes

- **`getTextContent`** now reads `thinking` blocks as well as text
blocks, so `[DONE:n]` tags emitted during reasoning are honored.
- **Marker tolerance** (`extractDoneSteps`): accepts `[DONE: 1]`, `[DONE
1]`, `Done: n`, and prose phrasings like "Step n done / is complete /
finished" — all adjacency-anchored so negations ("Step 9 is not done")
never match.
- **Whole-plan fallback** (`allStepsComplete`): if the run's final
message says the plan is done (e.g. "All steps complete"), all remaining
steps are marked at `agent_end`.
- **Plan extraction dedup**: only the last `Plan:` section is extracted,
so a plan drafted in a thinking block no longer duplicates the steps
from the text block.
- **No more truncation**: step text is kept in full (previously cut at
50 chars); the widget truncates for display only. The execution prompt
now shows the complete step text.
- **No more dropped steps**: steps whose text starts with a backtick
(e.g. `1. \`index.ts\` — …`) are no longer skipped.
- **Widget refresh** happens on every turn instead of only when a marker
matched.
- **Plan-complete card** is delivered with `nextTurn` instead of
triggering an extra LLM turn.

## Tests

`packages/coding-agent/test/plan-mode-utils.test.ts` updated for the new
behavior (truncation, backtick steps, marker-count semantics) and
extended with coverage for the new cases (marker variants, prose
phrasings, negation safety, thinking-draft dedup, markdown titles,
whole-plan completion).

All 43 tests in `plan-mode-utils.test.ts` +
`plan-mode-extension.test.ts` pass (`npx vitest run`).

Note: the repo-wide type-check currently fails on `main` with
pre-existing `ModelCatalog`/`never` errors unrelated to this change;
this branch adds none (same count with and without the diff).
2026-11-04 10:53:52 +01:00
robopipi[bot]
c13f6acfbe
Highlight .dart, .nix, and .groovy files in read/write views (#11790)
The `read` and `write` tool views find a file's language from its
extension with `getLanguageFromPath()`. That function's extension table
had no entries for Dart, Nix, or Groovy. Their highlight.js grammars are
loaded at startup, but the lookup returned `undefined`, so these files
were shown as plain uncolored text.

This change adds `dart → dart`, `nix → nix`, `groovy → groovy`, and
`gradle → groovy` to the table in
`packages/coding-agent/src/modes/interactive/theme/theme.ts`.

HTML export keeps its own separate extension map and a vendored
highlight bundle without a Dart grammar. This change does not touch
either.

## Testing

- Added a test to `test/syntax-highlight.test.ts` that checks the new
extension mappings. It fails without the fix and passes with it.
- `node ../../node_modules/vitest/dist/cli.js --run
test/syntax-highlight.test.ts` passes.
- Before, `getLanguageFromPath("a.dart")` (and `.nix`, `.groovy`,
`build.gradle`) returned `undefined` from the built dist. After, they
return `dart`, `nix`, `groovy`, and `groovy`.
- `npm run check` reports 6 type errors in `packages/ai/test` (model ID
types). Main without this change has the same 6 errors.

Fixes #11643

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-04 10:45:55 +01:00
robopipi[bot]
c5d3978350
fix(ai): send session-id instead of session_id on OpenAI Responses (#11793)
The `openai-responses` client sent the session-affinity header as
`session_id`. Proxies such as nginx with `underscores_in_headers off`
silently drop headers with underscores, and Envoy can be configured to
reject them. Codex now sends `session-id`, and the
`openai-codex-responses` path already followed it (#4967).

With the default `openai` session-affinity format, `openai-responses`
now sends `session-id`. `x-client-request-id` and `prompt_cache_key` are
unchanged. The `openai-nosession` and `openrouter` formats behave as
before.

The compat schema description for `sessionAffinityFormat`
(`openai-responses`) and `docs/models-configuration.md` are updated to
match.

Not changed: `openai-completions` still sends `session_id`. I did not
check whether xAI or other backends on the Responses API depend on the
underscored name. Their `prompt_cache_key` is still sent, so prompt
caching should not depend on the header.

Testing:
- `test/openai-responses-compat.test.ts` now captures `session-id`. It
also checks that `session_id` is no longer sent, and the
explicit-override test uses the new name. `test/xai-responses.test.ts`
expects `session-id` and no `session_id`.
- Without the source change, these tests fail (3 failures); with it,
both files pass (50 tests).
- `npm run check` passes.
- The full `packages/ai` suite passes, except
`generate-models-strict.test.ts`. That failure is unrelated: the test
depends on live model catalog data.

Fixes #11639

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-04 10:28:59 +01:00
梧桐雨
a63c4ec5c8
fix(coding-agent): clear delivered image-only queue entries (#8612)
## Summary

- remove delivered queue entries even when the user message has no text
- keep image-only steering and follow-up pending counts in sync with the
agent queue
- cover image-only steering delivery with a regression test

## Testing

- `npm run check`
- `./test.sh`

Fixes #8581
2026-11-04 10:24:43 +01:00
Sai Mallikarjun
f3a64af07b
fix(ai): preserve aborted stop reason during lazy setup (#8635)
Fixes #8409

## Summary
- Pass the request abort signal through lazy stream setup wrappers.
- Report setup failures as aborted when the request signal is already
aborted.
- Added a regression test for aborting during a tool execution before
next auth setup.
2026-11-04 10:23:35 +01:00
Gangqiang Yao
7a53c310a3
fix(coding-agent): settle tool results before disposal (#9126)
Fixes #9124.

Disposing a runtime during tool execution disconnects persistence before
the interrupted turn settles, leaving a stored assistant tool call
without its result. Await `session.abort()` before shutdown and disposal
so the interrupted tool result and final assistant message are
persisted.

This matches `teardownCurrent()`. `AgentSession.dispose()` remains
synchronous. Like the existing replacement path, the wait has no
timeout; handling tools that ignore cancellation is outside this change.

Related: #9986 concerns results for tool calls skipped during a
mid-session abort. This fix preserves the result of an already-running
tool during runtime shutdown.

Validation on main `f1b2e77f5`:

- The regression fails without the fix and passes with it, checking the
durable message order and `Operation aborted` result immediately after
`dispose()` returns, before awaiting the prompt.
- `npm run check`, offline build, and `./test.sh` pass.
2026-11-04 10:21:16 +01:00
Sviatoslav Abakumov
2cfab7c269
fix(coding-agent): render session tree search cursor (#9157)
This PR adds the cursor block to the **Type to search** input in
**Session Tree**. This is similar to the search input in **Resume
Session**.

Before:

<img width="1120" height="778" alt="Screenshot 2026-09-04 at 19 25 30"
src="https://github.com/user-attachments/assets/8f2e0ba9-8476-452b-a6be-1ad9b8d7d6c8"
/>

After:

<img width="1120" height="778" alt="Screenshot 2026-09-04 at 19 25 50"
src="https://github.com/user-attachments/assets/7f6ffcbf-db5a-4732-84fe-6ee0544116fa"
/>
2026-11-04 10:19:38 +01:00
Gangqiang Yao
c3b938e3e9
fix(coding-agent): reject reload while running or compacting (#9222)
Fixes #9221.

An extension tool can lose its context when an RPC command or SDK caller
reloads the session during tool execution: the tool awaits work, reload
invalidates its runner, then `ctx.getSystemPrompt()` throws a
stale-context error. The failed tool result is stored in session
history.

Reject reload when `isStreaming` or `isCompacting` is already set,
before shutdown or runner invalidation. This gives RPC and direct
callers the same entry checks as the TUI. The production change remains
seven lines.

#9548 removed the wrapper access behind the original reproduction. The
regression exercises the tool's own context access after an await,
through both an RPC-style extension command and direct
`AgentSession.reload()`. Rejection preserves the successful result in
session history and the next provider request. Compaction, tree
navigation, and idle reload are also covered.

Scope: this guards operations already marked running or compacting. It
does not serialize reload with prompt preflight or prevent a new prompt
from starting during an ongoing reload.

Related: #5735 proposes a broader deferred-reload API. This patch only
adds the existing TUI entry checks to the core reload method.

Validation on main `f1b2e77f5`:

- Without the guard, four regression cases fail and idle reload passes.
With it, all five pass.
- `npm run check`, offline build, and `./test.sh` pass.
2026-11-04 10:18:59 +01:00
Danila Poyarkov
dca1f55527
fix(coding-agent): preserve indentation in rendered diffs (#9274)
## Problem

The edit tool's intra-line renderer can drop indentation from a removed
line when text is inserted before otherwise unchanged content.

For example, this input:

```diff
-83   await commitRecordingChunk({
+83   const result = await commitRecordingChunk({
```

was rendered as:

```diff
-83 await commitRecordingChunk({
+83   const result = await commitRecordingChunk({
```

`diffWords()` groups the shared leading spaces into the added segment
(`" const result = "`). The existing highlighting logic moved those
spaces outside inverse styling, but only appended them to the added
line.

## Solution

Extract whitespace shared by both lines before calling `diffWords()`,
then initialize both rendered lines with that prefix. This preserves
indentation without changing the existing word-level highlighting
behavior. The existing handling for whitespace that belongs to only one
side remains unchanged.

Added regression coverage for:

- insertion before indented content;
- replacement within indented content;
- keeping shared indentation outside inverse highlighting.

## Validation

- `npm run check`
- `./test.sh`
- targeted `packages/coding-agent/test/diff.test.ts` (3 tests)
2026-11-04 10:17:34 +01:00
Gangqiang Yao
66c6556771
fix(coding-agent): track discarded user bash output (#10165)
Closes #10164.

A user `!` command can discard earlier chunks but report `truncated:
false` when the retained tail fits within the output limits. The model
then receives no truncation notice or full-log path.

Track discarded chunks and include that flag in the executor's result
for both completion and cancellation. This applies to the current shared
finalization path and preserves streaming ANSI handling. Output limits
and message format stay unchanged.

Related: #2852. Here the complete log is already saved; the incorrect
flag hides its path from the model.

Validation on main `f1b2e77f5`:

- Two deterministic 64 KiB + 44 KiB cases fail without the fix. All
three regressions pass with it, checking completion, cancellation, the
complete log, model-facing path, and short-output behavior.
- Regressions are in `test/bash-executor-truncation.test.ts`; that file
and `test/tools.test.ts` pass all 88 tests.
- `npm run check`, offline build, and `./test.sh` pass.
2026-11-04 10:16:53 +01:00
Christian Tietze
3b57ed7e27
fix: limit selector window height (#11072)
With this PR, we stop  rendering every option.
Instead, we render a centered, clamped window of up to 10 options and
show `(selection/total)` when the list overflows.

This keeps the selected option visible in long extension-provided lists
without changing navigation, keybindings, callbacks, countdowns, or
short-list rendering.

Fixes #6688.

## Merge Danger

**Door:** Two-way

The change is localized to `ExtensionSelectorComponent.updateList()` and
can be reverted without migration or persisted-state concerns.

**Blast Radius:** Low

Only extension selectors with more than 10 options change appearance;
they now render a window and position indicator instead of every option.

---------

Co-authored-by: Pi via openai-codex (gpt-5.6-sol) <noreply@openai-codex>
2026-11-04 10:15:27 +01:00
robopipi[bot]
f6278a472e
fix(mcp): read large stdio messages in linear time (#11791)
`JsonRpcLineReader.readStdout` (used by the stdio transport and by
`PipeTransport`) appended every stdout chunk to the buffered bytes with
`Buffer.concat`, then searched the whole buffer from the start for a
newline. A large message arriving in many chunks was therefore copied
and scanned again on every chunk. A 12 MiB tool result in 64 KiB chunks
took about 350 ms just to frame.

Now the reader keeps the bytes of an incomplete message as a list of
chunks, with a running byte count, and searches only the new chunk for a
newline. When the newline arrives, the chunks are joined once. A line
that fits inside a single chunk is sliced directly. The rest is
unchanged: the `maxMessageBytes` limit (now checked against the running
count), CRLF and blank-line handling, errors for invalid JSON and
oversized messages, and the incomplete-message error in `end()`.

Testing:
- New `packages/mcp/test/lines.test.ts`:
- a 1 MiB message fed in 4 KiB chunks calls `Buffer.concat` once (fails
on the old code);
- a mixed input of multi-byte UTF-8, CRLF, blank lines, an invalid line
and several messages gives the same results at every chunk size from 1
byte to the whole input;
  - reading recovers after an oversized message;
  - a message without its newline is reported at the end.
- `test/lines.test.ts`, `test/stdio.test.ts` and `test/pipe.test.ts`
pass. `npm run check` passes.
- Micro-benchmark, one message fed in 64 KiB chunks:
  - before: 1 MiB 5.4 ms, 4 MiB 49.9 ms, 12 MiB 348 ms;
  - after: 2.2 ms, 7.9 ms, 21.6 ms.

Fixes #11761

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-04 10:09:23 +01:00
Mario Zechner
f4427f8a3e docs(chord): the tutorial uses tx.update with its integer key now that int keys type-check 2026-11-04 09:07:34 +00:00
robopipi[bot]
9bca21a0b8
fix(coding-agent): update minimatch to 11.0.3 for GHSA-g3hv-75wg-m249 (#11789)
`@earendil-works/pi-coding-agent` pins `minimatch@10.2.6`.
GHSA-g3hv-75wg-m249 (moderate, a ReDoS via globparts amplification)
covers every minimatch version below 11.0.1, and there is no patched
10.x release. As a result, `npm audit` fails in every project that
installs pi-coding-agent.

This change pins `minimatch` to `11.0.3` and regenerates
`package-lock.json` and `packages/coding-agent/install-lock/` with
`scripts/generate-coding-agent-install-lock.mjs`. minimatch 11 adds one
new transitive dependency, `@isaacs/nonrecursive-glob-match@1.0.3`,
which has no install scripts. It also moves brace-expansion to ^5.0.12.
pi uses minimatch in only two places, `model-resolver.ts` and
`package-manager.ts`. Both use the named `minimatch` export, which 11.x
keeps unchanged.

No new test was added: the change only bumps a dependency, and existing
tests cover both places that use it.

Testing:
- `npm run build` and `npm run check` pass, and the install-lock check
reports it is up to date.
- `test/model-resolver.test.ts`, `test/package-manager.test.ts` and
`test/package-manager-ssh.test.ts` pass (213 tests).
- Before: installing 1.4.0 and running `npm audit` reports `minimatch
<11.0.1 … GHSA-g3hv-75wg-m249`.
- After: `npm audit --package-lock-only` in
`packages/coding-agent/install-lock` reports `found 0 vulnerabilities`.
- At the monorepo root, `npm audit` still flags a `minimatch@3.1.5` that
comes in through the `shx` dev dependency. pi does not ship that copy,
and this change does not touch it.

Fixes #11646

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-04 10:05:21 +01:00
robopipi[bot]
b0c64f33c8
fix(coding-agent): load user keybindings in pi config (#11788)
`pi config` ignored `~/.pi/agent/keybindings.json`. `selectConfig()`
never called `setKeybindings()`, so the config selector's
`getKeybindings()` fell back to the default TUI bindings. Remaps such as
`"tui.select.down": ["down", "ctrl+n"]` did nothing there, although they
work in `/settings` and the `--resume` picker.

`selectConfig()` now installs
`KeybindingsManager.create(options.agentDir)` before it builds the UI,
the same way the session picker and startup UI do.

**Testing**
- Added `test/config-selector-keybindings.test.ts`. It runs
`selectConfig()` with a stubbed terminal and component and an agent dir
whose `keybindings.json` remaps `tui.select.up` and `tui.select.down`,
then checks that the active keybindings include `ctrl+p` and `ctrl+n`.
It fails without the change and passes with it.
- Manual check: ran `pi config` in tmux with that `keybindings.json`.
Before the change, Ctrl+N did nothing. After it, Ctrl+N moves the
selection like Down.
- `npm run check` reports only two errors, both missing modules in
`packages/schema/test/bench/candidates.ts`. They occur on `main` without
this change.

Fixes #11651

Requested by @badlogic

Co-authored-by: triagent <triagent@localhost>
2026-11-04 09:59:49 +01:00