Commit graph

1041 commits

Author SHA1 Message Date
Armin Ronacher
8562bcf66a feat(coding-agent): codemode and MCP
This adds codemode and MCP support to pi. Codemode (packages/codemode) runs
model-written JavaScript in a QuickJS wasm VM inside a worker. From there the
script calls pi's tools as async functions, uses a per-session store, and can
read the model catalog and run classifiers. The codemode tool in coding-agent
is compatible with Codex's exec tool, and codemode.mode works like Codex's
tool modes (on or only). Large results are written to a JSON temp file. MCP
(packages/mcp) is a standalone client for stdio and streamable HTTP servers,
with OAuth.

The core only gets general mechanisms; codemode, tool_search and MCP are
built-in extensions that use them. Other extensions can replace these
built-ins. Tools declare an exposure: direct, model-only, codemode, deferred
or hidden. prepareLoadout() lets an orchestrating tool change which tool
declarations the model sees. ctx.executeTool() runs nested calls through
validation and the tool_call/tool_result hooks. Those calls emit events with
parentToolCallId and are recorded as bounded nestedCalls on the parent
result, which compaction and HTML export use. Tools can also return
structuredContent with an outputSchema, and can report errors with isError
instead of throwing. MCP servers come from mcp.json (global, or per project
after the project is trusted) or from pi.registerMcpServer(). They are
managed with /mcp and pi mcp list|login|logout, and their runtimes load on
first use.

Closes #10040
2026-09-29 09:12:50 +02:00
Armin Ronacher
b485fa3128 fix(tui,coding-agent): reduce render cost of theme changes and streaming
Theme previews re-render the whole transcript, and streaming re-renders
every frame, so per-line and per-session work dominated in long sessions.

- visibleWidth: fast path for ANSI-styled ASCII, allocation-free escape scanning
- Box: compare unpadded child lines and skip a second width measurement
- Markdown: reuse parsed tokens across invalidation
- footer: cache usage totals and context usage per session state
- bash results: cache the collapsed preview including the expand hint
- sanitizeBinaryOutput: single regex replace
2026-09-29 08:23:46 +02:00
Armin Ronacher
540e174c72
feat(coding-agent): Virtual models (#10035)
This adds experimental support for virtual models. A virtual model is a catalog entry that an extension registers with pi.registerVirtualModel(). It does not talk to a provider itself; for every request it picks a physical model and thinking level. The motivation is to make routing policies pluggable: plan on a strong model and implement on a cheap one, pick a model with a classifier, or move to a larger window when the context grows. Today this needs model switching by hand or hacks in prepareRequest.

The selection stays virtual: model_change records it, resume restores it, and each response records the physical model and thinking level that produced it. Router state can be stored on the session branch, so it follows /tree and forks and survives compaction. Context limits, compaction and image sizing use the physical model a request was routed to. examples/extensions/jev-router.ts shows a full router that uses the Jev classifier to pick Sol or Terra for planning and hands off to Luna after the first edit. docs/virtual-models.md describes the API.
2026-09-28 23:30:45 +02:00
Christian Klotz
fd889a2741
fix(coding-agent,tui): paste Finder file paths instead of icons (#10136)
Read Finder file URLs before icon image data on macOS. Quote paths in
bash mode and reject terminal control characters before rendering.

Fixes #9999

Co-authored-by: 1dustycy <onedustycy@gmail.com>
2026-09-28 20:59:29 +01:00
Armin Ronacher
04efdfc380
feat(ai): Discount jev (#10119)
Add the llama-cpp-classify classifier API. Each question becomes one chat prompt with single-token answer labels; llama-server's pre-sampling next-token log-probabilities give the softmax over those labels. Missing labels are retried with deeper readouts and then reported as errors.
2026-09-28 14:19:07 +02:00
Armin Ronacher
bf8e4b9531
feat(coding-agent,tui): System theme (#10067)
* feat(coding-agent,tui): add system theme derived from terminal colors

The new default system theme builds pi's colors from the terminal's reported foreground, background, and ANSI palette. Tokens take their hue and relative saturation from a palette slot; lightness is placed by OKLab lightness difference and WCAG 2 contrast minimums. Terminals that report only a background get built-in hues, and terminals that report nothing get ANSI indices with faint secondary text.

The TUI queries OSC 10, 11, and 4 in one burst ended by DA1, so replies complete in one round trip, and reports replies that arrive after the timeout. Startup never waits: the system theme starts in grayscale and updates when colors arrive. Light/dark detection now uses the reported colors instead of the ?996 query; mode 2031 notifications trigger a new query.

* fix(coding-agent): wait for terminal colors before building the startup header

The header and startup notices bake theme colors into their text. When the color replies arrived after they were built, as in Windows Terminal, they stayed grayscale. Startup now waits for the first color query, which ends at the DA1 reply or after 100 ms.

* fix(coding-agent): rebuild header and notices on theme changes

The startup header, loaded resources, and chat notices baked theme colors into their text, so they kept the old colors after a theme switch or when the system theme received the terminal's colors. They now use ThemedText, which rebuilds its text from a function whenever the UI is invalidated.

* fix(tui): keep focus on components that forward mouse events to children

A component that forwards a mouse event to a child it hosts, such as SettingsList with an open submenu, left keyboard focus on the child. When the host removed the child, focus stayed on a detached component and all keys were lost. The forwarding component now keeps focus, like delegating containers do.

* feat(coding-agent): describe the system theme in the theme selector

* feat(coding-agent): list the system theme above automatic in the theme selector

* refactor(coding-agent,tui): remove leftovers of the light/dark startup detection

Theme detection only reports dark or light now; the source and confidence fields only served saving the detected theme. The first-time setup no longer shows the detected appearance. Removes the TUI's unused queryTerminalColorScheme(), queryTerminalBackgroundColor(), queryTerminalForegroundColor(), and parseOsc11BackgroundColor(); queryTerminalColors() covers them.

* feat(coding-agent): match the reviewed system and light/dark theme design

The system theme now follows the reviewed design: color families with OKHSL saturation curves, palette slots per family, contrast rules per token and panel, relaxation for mid-gray backgrounds, and the terminal foreground for body text. Each contrast level is a target-lightness curve fitted to the reviewed output. The built-in dark and light themes use the reviewed Pi dark and light colors.

* feat(coding-agent): show the pi logo in the startup header

A two-line pixel logo replaces the app name. Its first line carries the version, its second line the first line of key hints.

* feat(tui,coding-agent): support OKHSL colors in themes

parseColor() accepts okhsl(H S L), and okhslColor() and colorToOkhsl() construct and read OKHSL colors. OKHSL saturation is relative to the sRGB gamut, so every value is in gamut. Theme files accept okhsl() values; HTML exports convert them to hex. The OKHSL code moves from coding-agent into pi-tui, where colors.ts now shares its Oklab conversion instead of keeping a second copy.

* refactor(coding-agent): write the built-in themes in OKHSL

The built-in dark and light themes use okhsl() values, with variables for colors that several roles share, so they read as color families and serve as editable templates. Near-identical values collapsed into one; no color changes visibly.

* refactor(coding-agent,tui): simplify terminal color and theme code

Share the terminal color query between startup and the theme controller, merge the query's resolve/late-reply state into one callback, keep a single global for terminal colors, use oklab.ts arrays directly in colors.ts, and drop leftover helpers and exports.

* fix(coding-agent): use one terminal light/dark decision everywhere

Theme pairs, the system theme, and Theme.appearance now share getTerminalTheme(): the reported background decides, then the terminal's light/dark report, then COLORFGBG, then dark.

COLORFGBG is classified by palette index like Vim (0-6 and 8 dark) and only reads the last field, so rxvt's "default" no longer picks up the foreground index.

Simplify the tests added on this branch.

* fix(ai): use an OpenRouter model that still exists in beta test

OpenRouter removed anthropic/claude-3-haiku, which broke type checking in CI after model regeneration.
2026-09-26 13:33:47 +02:00
David Brailovsky
ab30693d64
fix(ai): upgrade openai SDK to 7.19.0 (#10044)
* fix(ai): upgrade openai SDK to 7.19.0

Adds the "fast" service tier to the SDK types, needed to price
GPT-6 Fast mode requests correctly. Drops the local
prompt_cache_options type, which the SDK now defines.

* fix(coding-agent): check Cloudflare compat request via fetch instead of SDK mock

pi-ai now resolves its own nested openai 7.x while evals keeps 6.x at the
root, so vi.mock("openai") in coding-agent no longer reached pi-ai's client.
Capture the outgoing request with a fake fetch and assert the URL and headers.

* fix(ai): drop any casts for stream_options and max_tokens in openai-completions

stream_options is typed by the SDK, so assign it directly. max_tokens is
deprecated by OpenAI but still needed for OpenAI-compatible providers
that reject max_completion_tokens; use a narrow cast instead of any to
avoid the deprecation warning.
2026-09-25 17:27:12 +02:00
David Brailovsky
ff72faba28 fix(coding-agent): save new session file at the first user message
A new session was only written to disk once the first assistant message
existed. If pi exited during the first turn (Ctrl+C twice while streaming,
or the process was killed), the user's prompt was lost and the session
never appeared in /resume.

The file is now created as soon as the session has a user or assistant
message. A session with only setup entries (model, thinking level) still
stays in memory, so starting pi and quitting without chatting leaves no
file. Forking uses the same rule through a shared helper, so the two paths
cannot drift apart and write the header twice (#1672).

closes #10000
2026-09-25 16:27:51 +02:00
Christian Klotz
ddba596187
fix(coding-agent): honor truecolor in custom themes (fixes #9973) (#10039) 2026-09-25 14:44:41 +01:00
David Brailovsky
e473b5cd8b feat(coding-agent): report prompt disposition in RPC responses
Successful RPC prompt, steer, and follow_up responses include
data.disposition ("handled", "queued", or "started" for prompt), so clients
know whether to wait for agent_settled.

fixes #9098
2026-09-25 14:53:11 +02:00
Armin Ronacher
ca7460d16b feat: build with TypeScript 7 and run sources with plain node
Replace the tsgo native preview with typescript@7.0.2 and port the check scripts to the TS 7 API. Target ES2024, drop useDefineForClassFields: false and unused decorator options, and enable verbatimModuleSyntax. Replace tsx with node plus the source resolver hook, passed to --import as a file URL.

closes #9965
2026-09-25 13:14:33 +02:00
David Brailovsky
49681e1b71 fix(coding-agent): hide line range for full-file read calls with null offset/limit
Strict tool schemas make models send null for omitted optional fields.
The read renderer only treated undefined as missing, so full-file reads
rendered as "path:1".

fixes #9996
2026-09-25 11:36:43 +02:00
David Brailovsky
f444ea5eaf fix(coding-agent): use per-ref cache folders for pinned temporary git extensions
Temporary git extensions (-e git:...@ref) were cached in a folder keyed
only by host and repo path. After changing the pinned ref, pi reused the
old checkout without refreshing it and kept loading the first downloaded
commit. Include the ref in the temporary folder hash so each ref gets
its own checkout. Unpinned sources keep their existing folder.

closes #9982
2026-09-25 10:53:32 +02:00
Armin Ronacher
5674690966
feat(tui,coding-agent): add color values and theme styling (#8398)
Expose theme colors as values and add theme.style() for combined styling.

- @earendil-works/pi-tui gains a Color type (palette index, sRGB, OKLCH) with parseColor(), mixColors(), colorToHex(), colorToRgb(), styleText() and related helpers.
- Theme JSON accepts #rgb and oklch(...) values and an optional appearance ("dark" or "light"), detected from the theme colors when omitted.
- theme.style() combines tokens or concrete colors with text attributes; theme.colors exposes concrete colors, including the terminal's reported default colors (OSC 10/11) for tokens set to "".
- Terminals with TERM=*-direct are detected as truecolor.
2026-09-24 14:42:22 +02:00
Armin Ronacher
8676a0dcd8 fix(coding-agent): require advertised X11 clipboard images
closes #9786
2026-09-24 09:15:58 +02:00
Armin Ronacher
7fd564cbb7 feat: share model catalog protocol with pi.dev (#9763)
Move catalog layout, index validation, version selection, and request
negotiation into scripts/model-catalog-protocol.ts. pi.dev keeps an
identical copy, so the publisher and a test running the current client
exercise the same selection pi.dev performs. Replaces the cross-repository
compatibility dispatch.

Related to #9099.
2026-09-23 20:08:05 +02:00
Armin Ronacher
a328aa89ad
feat(ai,coding-agent): unify image and classifier model infrastructure (#9948) 2026-09-23 17:41:19 +02:00
David Brailovsky
002fc83852
feat: expose provider stream events to extensions (#9901)
Add onProviderStreamEvent through pi-ai and agent core, and surface
provider_stream_event to coding-agent extensions.

Closes #9784
2026-09-23 16:03:30 +02:00
Armin Ronacher
8d897edaa6 fix(coding-agent): prevent duplicate extension runtimes (closes #9863) 2026-09-23 15:33:56 +02:00
David Brailovsky
d192bd6dca
fix(coding-agent): avoid Fable split-turn summary refusals (#9908)
Separate conversation content from instructions and replace prefix/suffix wording with continuation-oriented summarization guidance.

Hopefully this avoids the refusals.

Fixes #9652
2026-09-22 16:52:07 +02:00
Ramiz Wachtler
e40126f578 fix(coding-agent): reject invalid --mode values
fixes #9045
2026-09-22 08:29:57 +02:00
Armin Ronacher
1a584a7a56 feat(ai,coding-agent): add Grok 4.7 support 2026-09-22 00:07:34 +02:00
Armin Ronacher
eaf72ed4d8 fix(coding-agent): update stale test expectations 2026-09-21 18:26:29 +02:00
Armin Ronacher
aef5fc429b
fix(coding-agent): keep prompt and tool state across context handlers (#9846)
Since 0.86 the prompt and tool declarations live in transcript system
messages, so a context handler that filters or slices messages could drop
them. After extension-driven compaction this sent requests without built-in
tools and made Codex emit raw tool-call text.

context handlers now see the conversation only. An unchanged list keeps the
transcript as is; a changed list gets the replayed prompt sections and tool
declarations as one leading system message. Handler-added system messages
are kept after it.

Add context_with_system, which runs after every context handler on the full
transcript and sends its result verbatim, for extensions that need to edit
system messages per request. Dropping the leading system message is reported
as an extension error.

closes #9789, closes #9822
2026-09-21 17:14:22 +02:00
Mario Zechner
466db0fecd feat: add canonical session context boundaries
Make SessionManager projections authoritative for provider requests, add append-only context edits and actionable turn boundaries, and preserve existing queue scheduling during continuation and recovery.
2026-09-21 16:59:20 +02:00
Mario Zechner
10d1ad621f feat: add transactional replicated state 2026-09-21 16:07:59 +02:00
Christian Klotz
b6419322e6
fix(coding-agent): report invalid prompt frontmatter (#9830)
Fixes #9354

Co-authored-by: Geet Khosla <geetkhosla@gmail.com>
2026-09-21 15:22:17 +02:00
David Brailovsky
47a18e37b1 fix: require complete GIF image signatures
Require GIF87a or GIF89a headers to avoid classifying text files beginning with GIF as images.

Fixes #9755
2026-09-21 10:30:42 +02:00
Armin Ronacher
63787ee6ba feat(coding-agent): identify extensions in crash stacks 2026-09-21 01:00:43 +02:00
Armin Ronacher
f5c946480c feat(ai,coding-agent): add image input limits (closes #9631) 2026-09-20 22:59:16 +02:00
Armin Ronacher
3390bd9363 fix(coding-agent): skip late cache warming refreshes 2026-09-20 15:53:22 +02:00
Armin Ronacher
6dff740fab fix(coding-agent): restore OSC 52 clipboard fallback for headless sessions
3349e1db1 (#9618) limited OSC 52 to SSH/mosh so a desktop terminal that ignores
it no longer reports a false success. That also disabled the only clipboard
route for containers and WSL without WSLg, which have no display to gate on.

Emit OSC 52 again on display-less Linux. On WSL, write the Windows clipboard
through PowerShell (verified, no OSC 52 size cap), preferring OSC 52 in
Windows Terminal where it is known to work. Desktop sessions with a display
still report the failure.

closes #9688
2026-09-20 12:42:15 +02:00
Armin Ronacher
d875512cc4 fix(coding-agent): suppress bug hints for expected failures 2026-09-20 12:06:14 +02:00
Armin Ronacher
d1230ea200 fix(coding-agent): preserve multiline bug descriptions 2026-09-20 01:40:27 +02:00
Armin Ronacher
dfbf793b78 feat(coding-agent): load session picker progressively 2026-09-20 00:48:26 +02:00
梧桐雨
21b8cc1a4b
fix(coding-agent): ignore stale tool image conversions (#8743)
Co-authored-by: Armin Ronacher <armin.ronacher@active-4.com>
2026-09-20 00:38:42 +02:00
Armin Ronacher
40c256cccb feat(coding-agent): defer extension loader dependencies
Load ordinary jiti lazily on Node while retaining jiti/static for Bun and SEA binaries. This closes #9540.
2026-09-19 23:25:47 +02:00
Armin Ronacher
c596d09d9c
feat(coding-agent): add prompt cache warming (#9668) 2026-09-19 21:18:15 +02:00
Armin Ronacher
4d38031fbd feat(ai): ship Radius model catalog 2026-09-19 19:23:31 +02:00
Armin Ronacher
de2de549bc fix(coding-agent): close compaction cancellation races
Closes #9340 and closes #9777.
2026-09-19 19:18:18 +02:00
Armin Ronacher
3c75b27479 feat(coding-agent): add bug reporting 2026-09-19 17:55:49 +02:00
David Brailovsky
1e39862f67 fix(coding-agent): detect llama.cpp chat-template thinking
Inspect loaded models through /props and configure boolean thinking levels for templates supporting enable_thinking.

Fixes #9528
2026-09-18 14:54:17 +02:00
Vegard Stikbakke
8bdcd4498a fix(coding-agent): compact oversized trailing tool results
Fall back to the latest safe assistant boundary when trailing tool results exceed the retained-token budget, allowing older history to compact before the next provider request.

Closes #9740
2026-09-18 13:38:30 +02:00
Mario Zechner
eed5263cdd Merge remote-tracking branch 'origin/main' into HEAD 2026-09-18 12:33:14 +02:00
Mario Zechner
cf8d5fac30 feat(agent): add Pico storage foundation 2026-09-18 12:32:00 +02:00
Ramiz Wachtler
fe219d7f8d
feat(coding-agent): format bash tool durations to support hours, minutes, seconds (#9742) 2026-09-18 12:25:15 +02:00
David Brailovsky
13784598d2 fix(coding-agent): suppress repeated Anthropic thinking drop notices
Fixes #9391
2026-09-18 09:58:42 +02:00
Ramiz Wachtler
46c9de402b
feat(coding-agent): add event handler unsubscribe (#9630) 2026-09-17 21:44:26 +02:00
Armin Ronacher
16292398af fix(coding-agent): send forced system prompts without recording them
Remove SystemMessage.replace. A prompt forced from before_agent_start is a rendering
of the current prompt for the run, not conversation state: persisting it as a
replacing system message wrote the full prompt and tool list on every change,
lost the structured sections while forced, and disabled mid-conversation system
messages for the rest of the session once a replacement existed.

The transcript now always records section patches. AgentSession wraps
transformContext and, when a forced prompt is active, collapses the system
messages into one head carrying the forced text and the current tools. Providers
receive the same request as before; nothing is persisted and the projection ends
with the run.

The evals harness validated the without_docs variant from the transcript, which
relied on the persisted replacement. Its transform extension now records the
prompt it forced and validates that instead.
2026-09-17 20:02:23 +02:00
David Brailovsky
a8b3dd1998 fix(coding-agent): fail signal-terminated shell commands
Map signal termination to conventional nonzero exit codes and reject null exit codes from custom shell operations while preserving partial output.

fixes #9577
2026-09-17 17:01:48 +02:00