A manager on the p2 server owns every MCP connection and process. Session
workers hold one view each and lease the locations their conversations
work at; the manager runs the owner's mcp.json servers there (stdio per
session and location, or shared; HTTP shared by default) and offers their
tools as deferred Durable tools found through tool_search.
Stdio servers on an environment start through its pi-env daemon, one
connection per environment shared by its instances, through cmd.exe on
Windows. A project's .pi/mcp.json, read by the worker at each location,
may add stdio servers that run there and switch the owner's servers on or
off; the manager refuses everything else. A lease gets servers only on
environments its session works on.
p2 (Pi Mobile on Chord v2) moves from pi-private into pi as one commit. Root scripts that loop all workspaces
use --if-present; npm test, ./test.sh and the release script cover packages/* only (p2: cd apps/p2 && npm test);
sync-versions also updates apps' workspace pins.
- Web: base bundles built from the app's entry at start (content-hashed, served
forever), plugin scripts that register themselves and one combined file,
WebScripts/TuiScripts showing the current set, startPage with the connection
loop, in-place plugin reloads and a reload on a new base bundle.
- React bindings: HostProvider, useHold, useWatch, useRegistry, useRefState.
- Terminal: startTerminal, a launcher that signs in with a token, a token file or
device pairing and always runs the app's terminal code in a sandboxed child
process (pipes, empty environment, --permission granting nothing, no SQLite,
a builtin allowlist and a frozen process inside); output filter, confirmed
copy/open/attach actions; startTerminalRuntime and TuiScope inside.
- Themes: the Themes registry, ThemeChoice per principal, CSS variables on the
page, terminal colours by role; CSS modules in plugin builds.
- App kit: startServer gains web and tui; startApp renamed startServer,
exportAddress/importAddress renamed exportData/importData and export no longer
stops the worker; ephemeral sessions end with their client's last link.
- Examples 56-web, 57-tui, 58-themes, 59-chat.
Schema helpers are a thin layer over pi-schema's builders with the same JSON,
so fingerprints and stored data are unchanged (proven by schema-switch tests and
a store written by the TypeBox code). Checks through pi-schema; error messages
are pi-schema's; format is an annotation. v1 contracts take any JSON Schema.
map(bytes()) works.
pi-schema: opt-in coercion, compile(schema, { coerce: true }) with coerce() and
parse(), the superset of TypeBox's Value.Convert rules and pi-ai's old coercion;
unresolvedRefs option; url and json-pointer-uri-fragment formats.
pi-ai: Tool takes any JSON Schema; validation through pi-schema; Type, Static
and TSchema are no longer exported (breaking). pi-agent-core and pi-durable:
tools on pi-schema, no TypeBox dependency. Coding agent: extensions keep Type,
Static, TSchema and StringEnum through an extension view of pi-ai; built-in
tools stay on TypeBox.
@earendil-works/pi-schema compiles JSON Schema (drafts 2020-12 and 7) once into
plain closures: no eval or new Function, so it runs under a strict CSP. It
passes 2,222 of the 2,223 core tests of the official JSON-Schema-Test-Suite,
checks Chord-style argument schemas within 1.2x of TypeBox's JIT, and keeps
3,200 compiled schemas in 7.9 MB (TypeBox: 54.7 MB).
- compile(schema, options) returns a Validator: check() is the boolean check
(a type guard for builder schemas), errors() reports each failure with a
JSON Pointer path, missing and unknown fields at the field.
- Builders (str, int, obj, opt, union, ...) return plain JSON Schema typed with
the values it accepts (Static<typeof schema>).
- Formats are an optional entry point (./formats), adapted from TypeBox (MIT).
- Tests run the vendored suite (drafts 2020-12 and 7, with optional and format
tests); npm run bench compares conformance, speed, setup cost, memory and
bundle size with TypeBox, @cfworker/json-schema and omptype.
esbuild is a non-dev package through chord's optional peer dependency, so its optional @esbuild/* platform packages are non-dev as well. npm install regenerates this; the committed lockfile had stale dev flags.
chord/next/http: a Web fetch handler built from the registry Http (routes, wraps,
sockets, hosts, relays, CSP sources) and Authenticators, responses as descriptions,
a security baseline on by default, Chord's own Node listener as a host-lifetime
object, nodeRequest for apps mounting it, links and plugin sockets.
chord/next/workers: a forked worker per address, started on demand, stopped when
idle, never restarted by itself, plugins built by the server and shipped.
Plugins: kinds with platform and host packages, createPluginRuntime, scripts(kind),
rollout results per host, imports of other plugins by name. Kernel: RouteHandle
(retry, suspend), no route backoff, anonymous host.root, origins only from relays.
ws is an optional peer dependency.
Replace build-time shell utilities with Node filesystem operations and retire the native sandbox example in favor of existing isolation options. Upgrade glob matching and worker-test dependencies and refresh the lockfiles.
Plugin discovery, esbuild builds (optional peer dependency), vm evaluation with
pluginFile and mapStack, dependency installs, a watcher with dirty marks and
rollout across hosts; host.load and host.reload per plugin with the closure rule;
examples 35-39 as folders with real plugin directories.
autoevals@0.3.0 declares engines npm: please-use-pnpm, so every npm install printed an EBADENGINE warning. 0.4.1 removes that but adds a pnpm-enforcing preinstall script. The eval only used the Levenshtein scorer, so inline an equivalent normalized similarity helper instead.
Runs the storage conformance suite, the schema migration, an alarm-woken sleep across an eviction, and a
turn recovered through the recovery alarm in workerd via @cloudflare/vitest-plugin (npm run test:workers).
fix(durable): decline background timers in Cloudflare Workers. With nodejs_compat, timers have an unref(),
so the idle-memory timers were armed and a pending one kept a Durable Object from hibernating or being evicted.
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.
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.
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.
Everything outside apps/ is the same in pi and pi-private; apps/ exists only
in pi-private, and these files now tolerate its absence:
- workspaces include apps/*
- biome.json leaves apps/ to its own config
- the pre-commit hook runs the apps/mobile check only when it exists
- the Android APK workflow runs only in earendil-works/pi-private
- scripts/biome-check.mjs runs native Biome, or Biome WASM on Android
(@biomejs/wasm-nodejs 2.3.5), for npm run check
From pi-private bac58de97, c2a8629da, 106714ebc.
read returns an image file as one image block (programs get an ImageContent) plus an info diagnostic,
instead of an unsupported_image error. Limits are the conversation model's inputLimits.images.resize,
by default 2000x2000 pixels and 4.5 MB of base64.
- Without a processor: PNG, JPEG, GIF, and WebP within the limits pass through undecoded; the byte limit
is checked before reading the file, dimensions from the header. Larger images and BMP are error results.
- With createCodingTools({ images }): the processor orients by EXIF, converts BMP to PNG, and shrinks to
fit. ImageProcessor.prepare is async.
- @earendil-works/pi-durable/images: createPhotonImages(wasm) on @silvia-odwyer/photon 0.3.3 (web build);
/images/node and /images/cloudflare load the wasm. The root and ./tools entries never reach it (enforced
in check-entry-graphs).
- An image must be read whole: one that changes while read is read again, unlike a growing text file.
- A model without image input gets a diagnostic that it sees a placeholder.
- RemoteExecutionEnv: a Durable ExecutionEnv on another machine whose
results match NodeExecutionEnv there; Connection to the pi-env daemon
- packages/env/daemon: the Rust daemon for Linux, macOS, Android and
Windows, following Node and libuv semantics
- env.yml runs Durable's env tests, the daemon's checks and pi-env's
conformance and differential tests on Linux, macOS and Windows
Switch native Anthropic tool changes to the inline-tools-2026-09-15 beta. Later tools are defined by value in tool_addition blocks, so the top-level tool list stays fixed and same-name redefinitions no longer fall back to resending the full tool list. The deferred placeholder is kept.
Upgrade @anthropic-ai/sdk to 0.129.0 and deprecate hasToolRedefinitions().
minimatch 10.2.6 (latest) still allows brace-expansion ^5.0.8, so pin the
patched version directly to keep the shrinkwrap off vulnerable releases.
closes#10288
pi-agent-core now contains only Agent, the agent loop, the proxy stream, and
their types. Removed the harness, sessions and session storage, pico3, harness
tools, compaction, skills, prompt templates, telemetry schemas, search types,
and the uuidv7 and pi-telemetry re-exports, plus the ./node, ./harness/* and
./experimental/pico3 subpath exports. Durable sessions live in
@earendil-works/pi-durable.
Also removed packages/session-backends and the experimental mini and micro
coding-agent frontends, with their scripts and build configuration.
Generation offers active registered tools through positional system entries and
hands a tool-calling answer to parallel or sequential pi.tool tasks and a
pi.post-tools task that applies addTools/terminate and continues the run. Tool
tasks validate, run beforeTool/afterTool, record intent with the replay policy,
publish bounded, sanitized, adaptively throttled progress in pi.live.tools, and
write results with a rendered diagnostics block. Hook dispatch with conversation
and subtree scopes, prepareArguments, HarnessOptions.env, and read/bash/edit/write
tools in pi-durable/tools. The env shell now streams raw output and spills past
thresholds; its bounded capture and publisher are removed.
Addresses GHSA-82fw-gwwq-j7x9 (vitest mocker path traversal) and
GHSA-3wwx-pv8p-q78v (undici 6.x permessage-deflate DoS). Both are
dev/example-only; the shipped coding-agent shrinkwrap is unchanged.
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
* 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.