6574 Commits
Author SHA1 Message Date
Armin Ronacher f6f3a027e9 feat(coding-agent): decouple Nix model catalog pinning from releases
Revert the release workflow and announcement changes. A Nix workflow builds the flake on relevant pull requests and daily, and after pi.dev publishes a catalog from main it replaces the pin only when the current one no longer hydrates main, then commits it.
2026-09-29 10:52:31 +02:00
Armin Ronacher 288e1357e6 fix(coding-agent): adapt Nix model catalog pinning to typed catalogs
Hydrate, export, and release verification now use the typed models.all.json catalog whose bytes define a revision. Nix fetches the typed representation, packs the new codemode and MCP workspace packages, and pins the current live revision.
2026-09-29 10:19:36 +02:00
Armin Ronacher 9742d080fe Merge remote-tracking branch 'origin/main' into nix-merge 2026-09-29 10:01:13 +02:00
Armin Ronacher f1927c2d56 feat(tui,coding-agent): configurable fullscreen wheel scrolling with auto acceleration
Adds fullscreenWheelScrollLines ("auto" or 1-100). Auto keeps one line
per event in local macOS terminals and scales with wheel velocity
elsewhere, capped at six lines per event.

closes #9758
2026-09-29 09:57:53 +02:00
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 28fd590868 fix(ai,coding-agent): replace removed Together Kimi K2.6 with Kimi K3
Together no longer lists Kimi K2.6, which broke type checks against the hydrated catalog. Tests use Kimi K3 and the Together default model now points at it.
2026-09-29 08:11:56 +02:00
Mario Zechner cb7969d212 feat(durable): add durable task runtime
Adds defineTask, registry-resolved phase handlers, and a scheduler that
decides every task transition in one callback on the Session line:
reservation with migration, dependencies, precedence rules in a step
before each phase, handover to replaced definitions, direct-task abort
(mark, signal, join, fresh abort invocation), orphaning of blocked tasks,
open reconciliation, and close that joins invocations without writing
outcomes. runtime.commit() callbacks return the typed next state; Tx no
longer exposes setTask. Adds Harness.resume, getTask, waitForTask,
abortTask, and task-aware waitForIdle; RegistryReader.subscribe wakes
the scheduler.
2026-09-28 23:55:24 +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
David Brailovsky 1b2aa0ca0e fix(ai): reject unfinished Responses tool calls instead of running them
The agent runs every tool call in the final assistant message, including
calls whose output_item.done never arrived. Those can carry cut-off or
mixed-up arguments. With llama.cpp, which omits output_index, two parallel
calls became three (echo a, echo a, echo b), two of them with the same id.

processResponsesStream now fails the stream when a completed response
still has an unfinished tool call.

fixes #9974
2026-09-28 23:19:38 +02:00
Christian Klotzand1dustycy 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
Mario Zechner 3883fcb1f3 feat(durable): add openable Harness with registry and conversation config
Implements Pico5 Package 13: Harness.open over a Session with an application-owned
registry (tools, tool wrappers, hooks, tasks, system prompt sections; batched
publication, stable keyed order), lazy root creation with init, atomic
create/fork with init, conversation-bound commits, fork-aware entries, model
context derivation, and the built-in ConversationConfig document.

Fixes cached documents skipping migration when accessed with another definition
version. Rewrites the Pico5 spec and handoff to the registry/system-prompt
design and removes the superseded live-registries proposal.
2026-09-28 21:57:41 +02:00
Armin Ronacher c90d9ea586 feat(ai,coding-agent): add Claude Sonnet 5.5 support 2026-09-28 20:46:38 +02:00
Mario Zechner 11894012dd feat(durable): expose deltas since base to checkpoint predicates
Storage reports replayed deltas after the selected base; Session tracks the
count across adopted writes so checkpoint predicates can bound replay.
Also adds internal reserved-root creation and conversation-bound commit
defaults for the upcoming Harness.
2026-09-28 17:21:20 +02:00
Mario Zechner cd5ff11259 docs: reorder durable harness milestones 2026-09-28 16:47:23 +02:00
Mario Zechner 35180b9df8 feat(durable): add document states and buffered watches 2026-09-28 16:36:31 +02:00
David Brailovsky c1449660c8 fix(ai): allow empty thinking signatures for OpenCode qwen3.8-flash
fixes #10047
2026-09-28 14:55:08 +02:00
David Brailovsky dc84c1ac0a fix(ai): send requested thinking level to Mistral reasoning models
Mistral models with effort levels now use reasoning_effort based on
their thinking level map from models.dev instead of a hardcoded id list.
GLM 5.3 no longer gets prompt_mode, GLM 5.2 can use max, and off sends
the model's off value when it has one.

fixes #9678
2026-09-28 14:33:10 +02: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 e1787702d3 fix(coding-agent): default OpenCode Go to Kimi K3 after Kimi K2.6 removal 2026-09-28 12:08:56 +02:00
Armin Ronacher ebced83431 fix(ai): update tests for removed OpenCode Go Kimi K2.6
models.dev dropped Kimi K2.6 from OpenCode Go, which broke type checks
against the regenerated catalog. Use OpenCode Go Kimi K3 for
provider-level tests and OpenCode Zen Kimi K2.6 for thinking format tests.
2026-09-28 12:01:02 +02:00
Armin Ronacher 6f7551516b fix(ai,coding-agent): update Fireworks tests and default for removed models
models.dev dropped Kimi K2.6, GLM 5.2, and the dated DeepSeek V4 Fireworks
models, which broke type checks against the regenerated catalog.
2026-09-28 00:22:39 +02:00
Armin Ronacher 2b0a123de9 feat(coding-agent): remove themes section from startup banner 2026-09-26 14:37:30 +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
Mario Zechner d6af72e185 docs(durable): clarify scratch examples 2026-09-25 18:05:22 +02:00
Mario Zechner 04b5bad666 feat(durable): add typed IDs and explicit ownership 2026-09-25 17:58:29 +02:00
David Brailovsky a6ca861024 fix(ai): price OpenAI Fast mode service tier like priority
GPT-6 models return service_tier "fast" for Fast mode requests (the new
name for priority processing), which fell through to the 1x default.

closes #10034
2026-09-25 17:39:09 +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
Mario Zechner 507d7649e9 feat(durable): add conversation document forks 2026-09-25 16:17:53 +02:00
David Brailovsky 8930b9ec0b fix(ai): ignore empty Mistral content deltas
GLM models on Mistral send empty content deltas at the start of a
response, alongside tool call fragments, and sometimes mid-thinking.
Each one opened an empty text block, and a mid-thinking one split
thinking into two blocks, which Mistral rejects on replay with
"Expected at most one leading ThinkChunk".

closes #9674
2026-09-25 16:01:28 +02:00
David Brailovsky c01f687e5b fix(ai): apply model samplingParams in direct stream()/complete() calls
Model-level samplingParams were only merged by streamSimple(). Direct
stream()/complete() calls, such as extensions using
modelRegistry.complete(), dropped them. The OpenAI-compatible adapters
now merge model defaults with request keys, and request keys take
precedence.

closes #9506
2026-09-25 15:53:08 +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
David Brailovsky b3487650f6 fix(tui): keep cursor visible when overlays close after stop
Closing the last overlay hid the terminal cursor. If an extension closed
an overlay during session shutdown, this ran after stop() had restored
the cursor, leaving the shell with an invisible cursor.

closes #10026
2026-09-25 13:30:50 +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 92e8d4f02a fix(coding-agent): stop RpcClient skipping listeners on unsubscribe
closes #9990
2026-09-25 11:27:10 +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
Mario Zechner 5fd446ca18 feat(durable): optimize document replay 2026-09-25 00:09:50 +02:00
Mario Zechner 9e70c3d505 feat(chord): optimize immutable batch application 2026-09-25 00:09:50 +02:00
Ramiz Wachtler b2bd111f2d feat(coding-agent): add hidden-message toggle to HTML exports (#10020) 2026-09-24 23:18:25 +02:00
Mario Zechner 5d4de953ce feat(durable): add checkpoints and document migrations 2026-09-24 23:09:15 +02:00
Mario Zechner 19a0361be8 feat(durable): add transactional sessions and documents
Add typed document definitions, atomic Session transactions, complete commit publication, task/document lifecycle handling, and paginated Tx scans. Share Chord's strict JSON copier across draft placements and durable ownership boundaries.
2026-09-24 22:24:04 +02:00
Ramiz Wachtler 7cf037c218 fix(tui): choose Kitty image dimensions by aspect distortion (#9957) 2026-09-24 20:51:10 +02:00
Mario Zechner 6966636dbe fix(chord): allow promise probes on settled drafts 2026-09-24 19:33:02 +02:00
Armin Ronacher d5629e2048 fix(tui): autocomplete paths after opening wrappers like ( and backticks 2026-09-24 15:08:54 +02:00
Mario Zechner d5cba1d97c feat(chord): make immutable delta tracker canonical
Replace the alternate tracker implementations with the optimized immutable overlay, validate draft placements as strict JSON, and document the ownership contract. Consolidate tracker tests and benchmarks around the canonical implementation.
2026-09-24 15:07:12 +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
Christian Klotz 3f0573877e feat(durable): export storage benchmark workloads 2026-09-24 14:26:22 +02:00