Commit graph

6267 commits

Author SHA1 Message Date
Armin Ronacher
06a4669355 Release v1.2.0 2026-10-13 00:46:55 +02:00
Armin Ronacher
0d79b74db1 docs: audit changelogs and add new features for next release 2026-10-13 00:36:49 +02:00
David Brailovsky
7bd01fb924 fix(ai): retry "The upstream provider returned an error"
closes #10842
2026-10-12 18:24:53 +02:00
David Brailovsky
3e477eab5f feat(coding-agent): add PI_CLIPBOARD_OSC52 to force OSC 52 copies
Sends copied text to the terminal with OSC 52 after the local clipboard
write, as over SSH, for remote clients without SSH variables such as
multiplexer attaches and remote IDE daemons.

closes #10826, closes #10835, closes #10114
2026-10-12 18:07:27 +02:00
Armin Ronacher
b6d505cd1f fix(codemode): reject // @options: lines that are not the first line
A misplaced options line was ignored, so its timeout_ms never applied and
the script ran without a deadline. parseCodemodeSource now throws
CodemodeSourceError before the script runs.

fixes #10631
2026-10-12 17:55:33 +02:00
Mario Zechner
55df12bf62 docs(durable,env): changelog entries for listFiles 2026-10-12 15:22:48 +00:00
Mario Zechner
b6fda579f4 feat(env): listFiles for remote and local environments, nodePlatform
RemoteExecutionEnv.listFiles resolves the path and lists through the
daemon; a daemon without the listFiles feature yields not_supported.
LocalExecutionEnv is NodeExecutionEnv plus listFiles through a daemon on
the same machine, so local and SSH sessions list with the same code.
nodePlatform maps hello's Rust names to Node's (windows -> win32,
macos -> darwin, x86_64 -> x64, aarch64 -> arm64).
2026-10-12 15:22:14 +00:00
Mario Zechner
aa499c2426 feat(env): listFiles operation in the pi-env daemon
Lists a directory tree like fd --hidden --exclude .git without following
symbolic links, with the ignore crate of fd and ripgrep for git's ignore
rules. Records travel as payload (kind byte, relative / path, NUL); the
listing stops at maxEntries, maxBytes or timeoutMs with truncated, and a
cancel stops it between entries. hello reports features: ["listFiles"]
so clients can tell older daemons apart.
2026-10-12 15:22:14 +00:00
Mario Zechner
abfe62928b feat(durable): optional ExecutionEnv.listFiles for tree listings
An environment may list the files, directories and symbolic links below a
directory: relative / paths, depth first in name order, without .git and
what git ignores in work trees, bounded with a truncated flag. Callers fall
back to their own walk when it is missing or fails with not_supported.
The env conformance suite checks it with the new listFiles option.
2026-10-12 15:22:14 +00:00
Armin Ronacher
d4d53612be fix(ai): fall back on Anthropic strict tool limits
Retry known strict tool capacity rejections once with preferred tools using their original non-strict schemas. Preserve required constraints and payload hooks, and never retry a started stream.

closes #10409
2026-10-12 17:20:36 +02:00
David Brailovsky
19cb534c3b fix(chord): add ws and @types/ws dev dependencies for websocket test types 2026-10-12 16:17:25 +02:00
David Brailovsky
e6fb2581df fix(coding-agent): retry transient Windows EPERM when acquiring auth lock
A just-removed auth.json.lock directory can stay delete-pending on Windows
while another process holds a handle, making mkdir fail with EPERM. Retry
this briefly (capped separately from ELOCKED) so real permission errors
still fail fast.

fixes #10801
2026-10-12 16:14:55 +02:00
David Brailovsky
441f5767c4 fix(coding-agent): warn and exit non-zero in print mode on truncated response
Print mode now writes a warning to stderr and exits 1 when the final
response stopped at the model's output token limit. The partial text
is still written to stdout.

fixes #10799
2026-10-12 16:06:42 +02:00
Armin Ronacher
da0eadf043 fix(mcp): do not retry cancelled HTTP requests after an authorization refresh
A tool call that was aborted or timed out while waiting for a shared OAuth
refresh was still retried by StreamableHttpTransport once the refresh
finished, so the server executed a call the caller had already given up on.

McpClient now passes a per-request signal to McpTransport.send(), aborted
once the request settles (cancelled, timed out, answered, or closed).
StreamableHttpTransport checks it before each dispatch. The shared refresh
itself is not aborted, so other requests waiting on it still succeed.

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

closes #10708
2026-10-12 15:29:18 +02:00
David Brailovsky
11f9911450 fix(tui): let unhandled wheel input scroll the transcript under a focused overlay
Wheel events that no component handles now scroll the transcript instead
of reaching the focused overlay's handleInput as raw input. Overlays that
scroll with the wheel must implement handleMouse.

fixes #10769
2026-10-12 14:21:58 +02:00
Armin Ronacher
67996ab75c fix(mcp,coding-agent): accept any JSON value as tool structuredContent
MCP 2026-07-28 allows structuredContent to be any JSON value, including
arrays and null. callTool() rejected non-object values, failing the whole
tool call. CallToolResult.structuredContent is now unknown, and MCP tools
without an output schema declare structuredContent as unknown to codemode.

refs #10416
2026-10-12 14:16:35 +02:00
Armin Ronacher
f4cca2dc22 fix(coding-agent): stop listing MCP resources when connecting
Servers with resources registered no tools until resources/list and
resources/templates/list finished, so a slow listing (a large drive)
kept their tools out of the first prompt. The lists only fed counts in
/mcp and pi mcp list; both now show that a server offers resources.
The resource tools still list and read resources on demand.

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

fixes #10765
2026-10-12 14:03:34 +02:00
Mario Zechner
6db4070f17 docs: describe only the current Chord, Durable, and coding-agent code
Chord: README rewritten for the new runtime; notes.md and requests.md removed, their open limits and
decisions folded into spec.md section 12; guide and spec status lines updated. Durable: drops the
pico-v5 handoff and delta findings, renames the Pico spec, and uses current Chord terms. Delta README,
issue categories, and PI_RADIUS_GATEWAY updated.
2026-10-12 10:47:09 +00:00
Mario Zechner
066ebe4ca1 docs(coding-agent): changelog entries for the chord runtime replacement
Adds packages/chord/CHANGELOG.md (and ships it with the package) and coding-agent's Removed entries.
2026-10-12 10:43:35 +00:00
Mario Zechner
0104fef1e9 feat(chord): replace the facet and service runtime with the chord2 host runtime
Moves chord2 (contracts, objects, cells, roots, plugins, hot reload, memory/IPC/WebSocket links)
from p2 into packages/chord, with its tests (now on vitest) and docs. The root export is the new
runtime plus the shared base (Context, JsonValue, JsonRepresentation, Draft, copyJson, isJsonValue,
replicatedState and its types). ./node now holds the host manager, child control, process locks,
and nodeLoader. ./context and ./delta are unchanged; ./bundler is removed.

replicatedState stays a standalone module with the same exports. It no longer exposes the exact-op
subscription the removed service provider used, so listener failures of mutable states are always
reported asynchronously. The package depends on typebox instead of esbuild. The wire format stays JSON.
2026-10-12 10:41:59 +00:00
Mario Zechner
7472cda0d6 feat(coding-agent): remove the experimental server stack and the client, protocol, and server packages
Deletes packages/client, packages/protocol (CBOR codec), packages/server, coding-agent's
src/experimental (except source-resolver.ts, which source runs still preload), src/cli/experimental,
src/client, the example plugin, and their tests. coding-agent no longer depends on @earendil-works/chord.
The installer lock drops esbuild, so its platform-package sanity check goes too.
2026-10-12 10:41:59 +00:00
Mario Zechner
2ee5c1e56d docs(durable): drop the Chord facet usage guide and its test
The guide and chord-guide.test.ts used Chord's facet and service runtime, which is being replaced.
2026-10-12 10:41:59 +00:00
Armin Ronacher
148622bc6f fix(coding-agent): describe pi mcp login --timeout as a limit on the whole sign-in
closes #10636
2026-10-12 12:16:11 +02:00
Mario Zechner
eb2fce43d2 fix(durable): reject waits that close a wait cycle
A wait is rejected when it closes a cycle in the wait-for graph: waits of
tasks that are not abort-marked, and holds of live ordinary owned work. The
rejection names the cycle. Replaces the owner-chain rule, so a background task
may wait on its conversation's owner. Approach and tests from #10533 by
@odysseus0.

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

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

fixes #10353
2026-10-12 10:17:40 +02:00
Mario Zechner
77332f7a34 feat(durable): deferred tools and the tool_search extension
- ToolRegistration.deferred: not offered to the model until modelTools names it
- modelTools gains { remove?, add? }; control.addTools writes names to add, so
  loaded tools follow the others in load order and a load only appends
- Agent.deferred lists deferred tools not loaded yet
- @earendil-works/pi-durable/tool-search: the coding agent's tool_search (BM25)
- the codemode section leaves deferred tools out
2026-10-12 08:05:26 +00:00
Armin Ronacher
f11e50f72e fix(coding-agent): make codemode output joining linear
joinAdjacentText checked endsWith on the growing joined string, which
makes V8 flatten the concatenation on every item. Collect each text run
in an array, check the separator against the last non-empty part, and
join once. Same fix in the durable code mode copy.

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

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

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

closes #10267, closes #5581, closes #10639, closes #10289
partly fixes #10606
2026-10-12 09:53:24 +02:00
Armin Ronacher
c661bc574f fix(durable): drop shared MCP types from the codemode section
pi-codemode no longer exports MCP_TYPESCRIPT_PREAMBLE or mcpStructuredContentSchema(); output schemas render as their own types, as in the coding agent.
2026-10-12 09:21:04 +02:00
Armin Ronacher
24382bedd0 refactor(codemode,coding-agent): decouple MCP from codemode
Tools that reach other tools declare it with reachesTools(input); MCP uses it to wait for the servers a call needs and to decide which codemode or tool_search tool to activate, instead of parsing codemode scripts and comparing parameter schemas. Namespaces get aliases, replacing codemode's MCP-specific __ suffix rule.

pi-codemode no longer knows MCP: MCP_TYPESCRIPT_PREAMBLE and mcpStructuredContentSchema() are removed, and the MCP result schema describes itself, with the server's local $refs relocated so they resolve as before.
2026-10-12 09:19:30 +02:00
Mario Zechner
4270522473 fix(coding-agent): code mode description says schema tools resolve with their value on failure 2026-10-12 00:23:28 +00:00
Mario Zechner
8bb180bec1 feat(durable): code mode listing, tool search, output, models.*, grammar; tool error rule
- structuredOutput is always the tool's own value; a schema tool's error text
  without one becomes a tool_error diagnostic on nested results
- code mode: a schema tool's error result with its value resolves (bash nonzero
  exit), schema-less tools resolve with their text, as in the coding agent
- codemode prompt section with tool declarations by namespace within an inline
  budget; searchTools/describeTool/describeNamespace from the start data
- result layout of the coding agent: header, text markers, console block,
  truncation within max_output_tokens and the tool's output limits, spill and
  image files in a recorded output directory (a rerun overwrites them)
- models.*: catalog globals; classify/generateImages as hidden internal tools
  (nested calls with usage), at most four at once
- Lark grammar of the script source as constrainedSampling
2026-10-12 00:06:09 +00:00
Mario Zechner
04566631c2 fix(durable): closing the code mode sandbox does not decide the script's outcome
A host that closes its CodemodeSandbox before the Harness, as on a graceful
shutdown, made the running code mode call store "Script error (aborted):
Sandbox closed" as its result, so the script never resumed. A run the sandbox
aborts without the call asking for it now stores nothing and ends with the
call's invocation; the next process replays the script.
2026-10-11 22:04:18 +00:00
Mario Zechner
127b77121e feat(durable): code mode result lists the script's tool calls
details.calls (CodemodeToolDetails): each tool call the script made, in call
order, with status ok/error/cancelled, duration, and cut arguments and error;
calls still running or not yet launched when the script ended are cancelled.
Written once with the result: live progress is pi.live.nestedTools.
bench:codemode compares a code mode script with a plain tool making the same
nested calls: 1,000 parallel calls 0.82 vs 0.78 ms/call, sequential 1.52 vs
1.10 ms/call (two more commits per call: the journal entry and the count).
2026-10-11 22:00:44 +00:00
Mario Zechner
7b597d1481 feat(durable): durable code mode (@earendil-works/pi-durable/codemode)
A codemode tool whose scripts call the conversation's tools as nested calls and
survive a restart. The script runs deterministically (pi-codemode start()); each
result is journaled before it is delivered. A rerun starts the script again from
the stored start data, delivers the journaled results in order (nested calls
reattach by key c<id>), checks every recorded call count, holds calls the
journal lacks until replay ends and checks them against earlier attempts' keys,
then continues live. A call launches only once the count of the drain that made
it is committed, so a rerun can prove which calls are new. Divergence, a changed
module version or sandbox fingerprint, and a passed deadline end the call with a
stored error; a decided outcome is returned without a VM. store()/load() keep
values per conversation, rewindable and forked as of the fork's entry.

pi-codemode: CodemodeRun.finished, set when the outcome is decided.
2026-10-11 21:31:48 +00:00
Armin Ronacher
8a9c051f80 feat(codemode): show schema constraints in tool declarations
Property comments now carry constraints the TypeScript type cannot
express (integer, bounds, lengths, pattern, format, default, ...) as
@ tags, read from nullable anyOf branches too. Types over the size
limit are rendered again without tags before becoming unknown.

closes #10707
2026-10-11 22:19:06 +02:00
Can Celik
78a3c10ba7
fix(tui): enable kitty images in herdr (#10774) 2026-10-11 22:01:02 +02:00
Mario Zechner
0fb1aee749 feat(codemode): deterministic runs that the host answers (sandbox.start)
For durable code mode: a host can rerun a script after a restart and get the
same calls with the same arguments. start() runs the script with a virtual
clock (startedAt, then each result's time, never backwards), randomness from a
seed, UTC, no WeakRef/FinalizationRegistry, and the run's own tools and
globals; every call goes to onCall with the VM's call id, and the host answers
with run.deliver(seq, now, id, outcome). The VM applies results strictly in
seq order whatever the transport does, reports its fingerprint (engine
versions, prelude hash, limits) before the script runs, the calls issued after
each result (onIdle), and the result it was processing when the script ended
(result.during). Rejections can carry properties for the script's Error.
execute() is unchanged.
2026-10-11 19:50:26 +00:00
Jay Madden
dbc7ff87c2
fix(tui): support override colorFn options in CompactionStatusIndicator (#10810) 2026-10-11 21:45:37 +02:00
Armin Ronacher
cd8bdf7518 fix(durable): allow image conversion tests more time on CI 2026-10-11 20:25:00 +02:00
Armin Ronacher
edb2eb966d fix(tui): keep delayed terminal replies out of keyboard input
Preserve terminal reply strings until their terminator while retaining bounded recovery for ambiguous legacy Alt-key input.

Related: #10256, #10657. Missing OSC prefixes and fragmented DA1 replies remain separate issues.
2026-10-11 20:17:00 +02:00
Mario Zechner
091bcc0998 fix(chord): append to arrays without walking or copying them twice
The delta tracker planned every structural array change with a walk over all
base elements; inserts and removals that keep the remaining elements in order
now get the same plan from the changed ranges alone. applyImmutable built an
appended array as a copy that the insert then grew, a second copy; an append now
builds it with one concat. Ops, values, immutability and errors are unchanged.
32,000 one-item appends, one commit each: 11.3 s -> 1.4 s
(npm run bench:document-append in packages/durable).
2026-10-11 18:04:43 +00:00
Mario Zechner
d77d2752e5 feat(durable): a schema tool's error result without structuredOutput gives programs its output
Programs calling a tool with structuredOutputSchema got isError and no text when
its error result omitted structuredOutput; they now get its bounded output,
collapsed like a schema-less tool's, so a code mode script sees the error text.
A thrown error's message stays in its tool_error diagnostic.
2026-10-11 18:04:34 +00:00
Mario Zechner
0f4b1465a4 fix(ai): send the codex request body as a Uint8Array<ArrayBuffer> copy
fetch accepts it as a body under the DOM lib too. From pi-private e97c4a60e.
2026-10-11 16:35:13 +00:00
Mario Zechner
2a33bbee89 test(ai): e2e repro for radius dropping replayed Claude thinking
Radius omits providerThinkingLevel from pi-messages done events, so replayed
signed thinking has no effort marker and Anthropic drops it (block_binding
drop_block). Compares follow-up input tokens with and without the thinking
block against an Anthropic control.
2026-10-11 16:35:08 +00:00
Armin Ronacher
74613de659 feat(env): report a terminal's foreground job with ptyForeground
The process group in front of the pty when it is not the program's own (a job its shell runs), with the leader's name on macOS and Linux. Exposed as RemoteTerminal.foreground().
2026-10-11 16:35:08 +00:00
Armin Ronacher
367b2bcf59 feat(env): run programs in pseudo-terminals for interactive terminals
The daemon gets pty, ptyWrite and ptyResize: raw terminal output as events, input in arrival order, SIGWINCH on resize, and the process group killed on cancel or a lost connection. openTerminal() is the client, localDaemon() the daemon for this machine. Unix only; Windows answers ENOTSUP.
2026-10-11 16:35:08 +00:00
Mario Zechner
89e5c2c075 fix(durable): fail a run whose blocking compaction failed; keep compaction failures visible
A generation waiting on a blocking threshold compaction sent its request
whatever the outcome. When summarization failed, the context was still above
the blocking threshold: a session at 996K of a 1M window got an empty length
answer on every turn and nothing showed the error. The generation now fails
with model_error (Compaction failed: <error>) when its compaction failed,
faulted, was orphaned, or was aborted directly; a compaction that found
nothing to compact or was declined still lets the request go ahead.

pi.live.compactionFailure keeps the newest failure (task, reason, error)
until a compaction produces a summary. The agent events' snapshot carries it,
and compaction_end carries error for a failed compaction.
2026-10-11 15:45:13 +00:00