Repository navigation
Releases: Particle-Academy/prism-acp-ts
Release list
v0.33.2
Host mode reuses the user's existing subscription; credential privacy hardening
- Documented host mode:
ClaudeDriverwithcredentials: { mode: 'subscription' }andnativeTools: 'none', withoutprocessTree.windowsStdio: 'local-pipes', runs the CLI as the user with its original HOME and configuration, so it uses the account already signed in on the machine. The CLI reads its own login store; prism-acp never reads, copies, hashes or forwards stored credentials, and billing API-key variables are still withheld from the child. Codex restricted mode remains limited to its measured model (gpt-5.3-codex on CLI 0.163.0); an account default such as gpt-6.1-sol is refused. - New opt-in standing test: starts restricted Claude against an existing signed-in subscription with an app-supplied MCP server and verifies model state, MCP connection and a clean stop, without sending a prompt or reading any credential file.
- Privacy hardening: locating a provider's configuration home now looks up only the one environment variable it needs instead of enumerating every environment value, and test probes no longer seed, read or scan provider credential stores. App-supplied credentials (API-key mode, MCP bearer tokens) are unchanged.
No breaking changes.
v0.33.1
Name the cause when the private configuration directory cannot be created
A consumer using nativeTools: 'none' under LOCAL (Windows AppContainer) saw the private configuration helper fail with a bare exit code 133. Measured cause: the private MCP configuration directory is created under the process's effective temp directory (os.tmpdir(), from TEMP/TMP). When HOME, LOCALAPPDATA and USERPROFILE are retargeted into a provider-only home whose effective Temp does not exist yet, CreateDirectoryW fails with Win32 error 3 (path not found).
- Under LOCAL with
nativeTools: 'none', the driver now checks that the effective temp directory exists BEFORE starting the helper or the provider, and refuses with a message saying the host must provision it. - The helper keeps a failure while preparing the native helper distinct from a native directory-creation failure, and reports the native stage and Win32 cause instead of a bare exit code.
- Errors do not include paths. The package still never creates missing parent directories and grants no additional access.
Nothing changes in what is allowed: the same sessions succeed and fail as in 0.33.0, now with an actionable reason. If you retarget the provider's environment, provision the TEMP/TMP it resolves to before starting a session.
Also: the private-directory test's first case uses a measured 10 s budget on hosted Windows runners (cold PowerShell start), with the numbers in the test (G-137).
No breaking changes.
v0.33.0
Claude restricted to the app's own MCP tools, including under Windows AppContainer (LOCAL)
New opt-in nativeTools: 'none' on ClaudeDriver, the Claude counterpart of the Codex restriction. It is host policy; clients and model output cannot set it. The default is unchanged.
- Claude starts with no native tools (
--tools ''), only the suppliedmcpServers(--strict-mcp-config), no inherited settings (--setting-sources '') and--restricted. Inherited MCP servers, settings and hooks are not loaded. - The driver checks what the CLI actually reports, not just the flags it was given. Initialize must report Claude CLI exactly 2.1.311 with a valid model list, and system/init must list exactly the supplied MCP servers and only their tools (plus Claude's built-in EndConversation, which cannot be removed while MCP tools exist). Anything else (another version, an extra or missing server, an unexpected tool, a malformed frame, output before system/init, an invalid session id) ends the session before or during the turn. The first prompt is held until startup is verified.
- Fresh sessions only:
resumeSessionIdis refused, as ispermissionMode: 'bypassPermissions'. - Works under LOCAL (
processTree.windowsStdio: 'local-pipes', an app-provisioned Windows AppContainer): the generated MCP configuration is written to a private directory created atomically with a protected ACL granting only the current user and the actual container SID, by the existing hash-verified supervisor. Permission prompts under LOCAL remain refused. The host must provision the container's effective TEMP; the package never creates parent directories or widens access.
Verified against the real Claude CLI 2.1.311 on Linux and Windows with a loopback fake model and synthetic homes (no real subscription or model): the supplied MCP tool executes; forced Bash, Read, Write, WebFetch and inherited-MCP calls return errors with nothing executed or read; the tool catalog contains only the supplied server. In a real zero-capability AppContainer, Claude, its bridge and the MCP server all carry the container SID inside the owned job, a second container cannot read the private configuration, and stop leaves no descendants or configuration behind.
Limits: Claude CLI 2.1.311 only, fresh sessions only; authenticated real-model acceptance has not been performed by this package. This removes the model's routes to native tools; it is not operating-system isolation beyond the AppContainer the host provides.
No breaking changes.
v0.32.0
Codex nativeTools 'none': no plugin marketplace download at startup
Reported by a consumer: under nativeTools: 'none', Codex 0.163.0 cloned its bundled plugin marketplace into the session's home during app-server startup (measured: 75 files / 3.0 MB grew to 3,097 files / 38.5 MB within ten seconds). The restriction was applied in the thread configuration, which arrives after startup, so it was too late for this.
- Restricted launches now start app-server with
-c features.plugins=false -c features.remote_plugin=false. Before creating a thread the driver reads the effective configuration and refuses unless both arefalse. - Measured with only plugin-source hosts reachable: with the flags the home stayed at 73 files / 3.0 MB with no plugin tree and no outbound attempt.
remote_plugin=falsealone did not stop the attempts. - Unrelated feature settings, the supplied MCP tools and the rest of the restricted tool surface are unchanged. Ordinary (non-restricted) launches are unchanged.
Verified against the real Codex CLI 0.163.0 on Linux and Windows behind a rejecting proxy (no plugin download, no model requests): a ten-second wait shows no plugin tree and no auxiliary connection; an ordinary launch is the control that does attempt the plugin source; removing the flags is refused.
This prevents the measured startup download; it is not filesystem isolation for the CLI.
Windows: parallel first launches no longer refuse to start
On a machine where the process-tree supervisor had not been built yet, several sessions starting at once each compiled it. Compilers produce different bytes, and they all wrote the same executable and hash file, so a launch could find an executable that did not match the recorded hash and refuse with "the supervisor does not match the hash recorded when it was built". Measured with 8 parallel first launches on 20 fresh caches: 80 of 160 launches refused on 0.31.0. This affected every release since 0.21.0, including the Claude approval-hook adapter added in 0.30.0.
- Each build is now published under its own digest (
prism-acp-supervisor-<sha256>.exe) and never replaced; a small pointer naming the current digest is replaced atomically. Every launch verifies the executable's bytes against the digest in its name before running it. - Same measurement after the fix: 0 of 160 refused, including 160 approval adapters. A tampered executable is still refused. Files keep the private cache directory's permissions. Older builds are not deleted, so none is removed while another process runs it.
- CI now runs this parallel cold-install check on Windows.
No breaking changes.
v0.31.0
Codex resume: skip or label the history replay
On a Codex resumeSessionId, the driver replayed the whole saved thread as ordinary updates before the session was ready, with nothing marking it as history. A long thread took more than five minutes and produced thousands of per-item debug notices.
- New
replayHistory: falseonCodexDriver: resume without listing or projecting saved history. Codex still keeps the thread's context: measured on Codex 0.163.0, the saved assistant message is in the next model request either way. - Every replayed update now carries
_meta["particle.academy/replay"] = true, so a client can tell history from live activity. Live notifications that arrive during a replay are not marked. - One bounded summary notice per replay (
_meta["particle.academy/replay_summary"]: turns, items, diagnostic counts by reason, whether it completed) instead of one debug notice per item. Historical payloads are not copied into it. - Replayed reasoning chunks carry the Codex item id as
messageId, and replayed compaction updates carryitemId, so clients can de-duplicate. - New exports
META_REPLAYandMETA_REPLAY_SUMMARY.
Verified against the real Codex CLI 0.163.0 on Linux and Windows: default resume makes 2 history requests and emits 3 replayed updates and 1 summary; replayHistory: false makes none and emits nothing before the first turn.
No breaking changes.
v0.30.0
Claude permission prompts fail closed, including inside Electron hosts
Security fix. The PreToolUse hook installed by permissionPrompts ran "<process.execPath>" approve.cjs. Inside an Electron host (for example Genie) that path is the app, not Node: every gated tool call launched a second copy of the app, the hook gave no answer, and Claude ran the tool anyway. Measured on Claude 2.1.310, Claude also runs the tool when a hook exits 0 with empty output, plain text or malformed JSON, even with onFailure: "block".
- The hook is now launched through a guard that allows a tool call ONLY when the hook script exits with a reserved explicit-allow status after validating the client's answer. Every other outcome (no answer, invalid output, crash, timeout, the host app instead of Node) blocks the call.
- Under Electron the hook runs with
ELECTRON_RUN_AS_NODE=1set for the hook process only; it never reaches Claude, its tools or any grandchild.permissionPrompts.nodePathaccepts a trusted absolute Node executable instead. - Windows: the guard is a mode of the existing hash-checked supervisor, prepared before Claude starts (about 50 ms per gated call). If it cannot be prepared, the session refuses at start. POSIX:
/bin/shwith the paths as arguments, never shell text. - Permission prompts now require Claude CLI 2.1.295 or later, read from the initialize handshake before any prompt is sent; a missing, malformed or older version, a failed initialize, or a later system/init reporting a different version ends the session with no prompt, model request or approval sent.
- New
permissionPrompts.timeoutSeconds(1 to 86400, default 86400); expiry denies. - The POSIX process supervisor also uses the host runtime as Node under Electron; it now restores the provider's original environment before spawning it.
Verified against the real Claude CLI 2.1.310 on Linux and Windows (26 cases each, loopback fake model, no real inference), including a fake Electron executable that exits 0 without running any script.
BREAKING CHANGE: permissionPrompts now refuses at start with a Claude CLI older than 2.1.295 (or one whose version it cannot read); upgrade the CLI. Sessions without permissionPrompts are unaffected.
v0.29.0
Codex without native tools: nativeTools 'none' (opt-in, measured)
A trusted host can now start CodexDriver with nativeTools: 'none' to remove Codex's native execution, file, web, image, browser, agent, goal and human-input tools. The default is unchanged.
- Supported only on the measured matrix exported as
CODEX_NATIVE_TOOLS_NONE: Codex CLI exactly0.163.0(checked at initialize), an explicitgpt-5.3-codexmodel, and fresh sessions. Other CLI versions, other or omitted models,resumeSessionId,setModelto another model and amodel/reroutednotification to another model all refuse. The model picker lists only supported models. - Only explicitly supplied
mcpServersremain; inherited servers are disabled by name. Three read-only resource helpers remain (list_mcp_resources,list_mcp_resource_templates,read_mcp_resource), scoped to the supplied servers. - The effective home/project configuration is checked before the thread starts: known native features are forced off, and any other enabled feature or tool setting that was not measured refuses.
- Verified against the real CLI on Linux and Windows (19 cases each, loopback fake provider, no real inference): hidden shell, file, image, agent and MCP resource routes are refused, and synthetic auth.json/config/session sentinels are never disclosed to the model. Re-enabling only the shell makes that check fail.
Limits: a reroute is refused when Codex announces it, so output or an allowed app tool call before that notification is not prevented, and a serving-model change Codex does not announce cannot be detected. This removes the model's tool routes; it is not operating-system isolation.
No breaking changes.
v0.28.0
Launch providers inside a Windows AppContainer (opt-in local pipes)
Inside a Windows AppContainer, launching a provider with piped stdio hung before spawn returned: Node's libuv creates its stdio pipes with GLOBAL names, the container is denied them, and libuv retries the access-denied error without end. LOCAL pipe names work.
- New opt-in transport:
launchProvider(binary, args, { cwd, env, windowsStdio: 'local-pipes', startupTimeoutMs, ioTimeoutMs }), andprocessTree: { windowsStdio: 'local-pipes' }for both drivers and model listing. Useprovider.iofor the streams and awaitstarted/closed. - The existing source-embedded supervisor (built by the system C# compiler, as before; no new dependency or binary) creates each
LOCALpipe with a protected ACL for exactly the launching user and the container SID, and both sides authenticate every pipe with a per-launch HMAC before any byte is trusted. The provider is created suspended, assigned to a job object, then resumed; stop and host death end the whole tree. The pipe names and secret are removed from the environment before the provider starts. - Startup, connection and stalled I/O are bounded; failures surface as a
ProcessTreeErrorwith a bounded code, never a silent hang and never a fallback to the old path. - Limits in this mode: Claude's generated MCP configuration and permission hooks are refused promptly, and API-key homes must already exist (their private-directory setup shells out with piped stdio, the same hang). Tracked as G-135.
- Verified with 17 cases in a real zero-capability AppContainer on Windows (Node 24.19.0 locally and 24.22.1 on CI), alongside the unchanged ordinary suite.
The default launch path is unchanged on every platform.
No breaking changes.
v0.27.0
Report each MCP server's status and callable tools
A host that gives a session MCP servers can now tell whether their tools actually arrived. Both drivers report the session's servers in one shape, McpServerReport { name, status, toolCount, raw?, error? }, under the mcp_servers meta key, with status normalised to connected, pending, failed, needs-auth or disabled.
- Claude: from
system/init, with each server'stoolCountcounted from the CLI's tool list by themcp__<name>__prefix, plusClaudeDriver.mcpServers(null until init). Measured on Claude Code 2.1.310: a server whosetools/listfails afterinitializeis reportedconnectedwith no tools, so checktoolCountas well asstatus. - Codex: from
mcpServer/startupStatus/updated, emitted as the full current list on every change, plusCodexDriver.mcpServersandCodexDriver.mcpServerStatus(), which asks Codex for the thread's servers with live status and tool counts. The request is scoped to the thread because the unscoped list omits a thread's own servers. - Neither driver offers a reconnect: neither CLI documents one for a client that drives it directly. Restart the session on the same conversation instead.
McpServerReport,McpServerState,META_MCP_SERVERSand the three report builders are exported.
No breaking changes.
v0.26.0
Refresh Codex rate limits on demand
CodexDriver.readRateLimits() asks Codex for the account's rate limits now and emits the reading exactly as the start-up read does: a notice carrying the rate_limit meta. It starts no turn and never rejects; a failed read is reported on the session as an unmapped frame.
Codex volunteers new limits only during a turn, so a host holding a session on a spent window never heard that it cleared and waited for the window's stated reset, which can be days late. A host can now call this periodically on a held session and resume as soon as a reading allows ordinary use.
AgentDriver gains readRateLimits?() as an optional member, so a host can gate on its presence. ClaudeDriver does not implement it; Claude's reported resets are accurate.
No breaking changes.