Repository navigation
feat(agent): embed pi as the engine + add gated plan execution - #27
Merged
Merged
Conversation
Replace the dual Vercel AI SDK ToolLoopAgent (API-key) + codex CLI
subprocess (subscription) engine with a single embedded pi agent
(@earendil-works/pi-coding-agent), and add gated execution so funfa can
apply an accepted plan, not just print it. Effect-TS is kept at the
core/propose boundary.
Engine (Part A):
- provider.ts builds pi AuthStorage + ModelRegistry (~/.pi/agent). No-auth
detector via getAvailable(); FUNFA_PI_MODEL ("<provider>/<model-id>")
override. Unlocks Claude Pro/Max + ChatGPT subscription OAuth.
- propose.ts drives pi headlessly via createAgentSession + session.prompt;
submit_plan captures the outcome through a closure. pi handles transient
retries, so the duplicate Effect retry is dropped.
- prompt.ts collapses to LOOP_BODY + one PI_BINDING (the API/CODEX two-sided
binding + drift guard are gone). FUNFA_FAKE_HARDWARE is now provider-neutral.
- Custom tools are pi defineTools (TypeBox): submit_plan, load_skill (bundled
bodies), fetch_page, web_search. Hardware probes use pi's built-in bash, so
run_shell + sandbox.ts are deleted. web_search is backed by Parallel's
Search API (PARALLEL_API_KEY), degrading to fetch_page when unset; the AI
SDK + AI Gateway deps are removed.
- Delete the codex stack (codex-cli/app-server/protocol) and vendored schema.
Gated execution (Part B):
- permission.ts: a pi tool_call extension (registered in-embed via the
resource loader's extensionFactories, so it survives bun --compile) that
gates every apply_command — consent prompt (default deny), download
allow-list (trusted GitHub release hosts), https-only, --bypass-confirms
auto-allows.
- apply-command.ts: argv-only apply tool (no shell) for PHASE 4 filesystem
steps; runs via Bun.spawn.
- funfa/plan SKILLs gain PHASE 4: apply filesystem high-tier steps via
apply_command; Steam-client state (launch options, Proton version) stays
PRINTED for the user to apply in Steam, since Steam runs continuously and
its config cannot be written safely from the CLI.
Build: compile drops --bytecode (bytecode gen fails on pi's dep tree);
keeps --minify --sourcemap. Binary ~64MB -> ~71MB.
Threat model: funfa runs unsandboxed bash composed by a model that ingests
untrusted forum/wiki text -> a prompt-injection RCE surface. Mitigations
here: the gate, the download allow-list + https-only, argv-only
apply_command. Residual risk: the read-only bash probe tool is allowed
freely; OS-level confinement (bubblewrap) is the next hardening step and is
out of scope. Documented in agent/permission.ts.
Tests: replace propose/provider/prompt tests; add permission.test.ts +
tools.test.ts; compile.test.ts asserts no-bytecode build + the no-auth
message + bundled-skill reachability. Delete codex/sandbox tests.
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates funfa's engine from the dual Vercel AI SDK
ToolLoopAgent(API-key) + codex CLI subprocess (subscription) setup to a single embeddedpiagent (@earendil-works/pi-coding-agent), and adds gated execution so funfa can apply an accepted plan, not just print it. Effect-TS is kept at thecore/*+ propose boundary.Part A — embed pi (engine swap)
provider.ts→ builds piAuthStorage+ModelRegistry(~/.pi/agent). No-auth detector viaModelRegistry.getAvailable();FUNFA_PI_MODEL("<provider>/<model-id>") override; friendly/login-or-provider-key error. Unlocks Claude Pro/Max and ChatGPT subscription OAuth (impossible before).propose.ts→ drives pi headlessly viacreateAgentSession+session.prompt();submit_plancaptures the outcome through a closure. pi retries transients itself, so the duplicate Effect retry is dropped;AgentErrorclassification is kept.prompt.ts→ collapses toLOOP_BODY+ onePI_BINDING+buildBasePrompt(the two-sidedAPI_BINDING/CODEX_BINDING+ drift-guard test are gone).FUNFA_FAKE_HARDWAREis now provider-neutral.defineTool(TypeBox):submit_plan,load_skill(bundled bodies, not disk),fetch_page,web_search. Hardware probes use pi's built-inbash, sorun_shell+sandbox.tsare deleted.web_searchis backed by Parallel's standalone Search API (PARALLEL_API_KEY) via plainfetch; without the key it returns a clear "usefetch_page" message. The Vercel AI SDK + AI Gateway deps are removed (the gateway exposes search only as a generation-time tool, so there was nothing embeddable).codex-cli/codex-app-server/codex-protocol) and the vendoredcodex-app-server-schema/.compiledrops--bytecode(bytecode gen fails on pi's dep tree); keeps--minify --sourcemap. Binary ~64 MB → ~71 MB.Part B — gated execution (new capability)
permission.ts— a pitool_callextension, registered in-embed via the resource loader'sextensionFactories(survivesbun --compile; no-efilesystem discovery). Gates everyapply_command: consent prompt (default deny), pinned trusted-host download allow-list (GitHub release infra for Proton-GE/DXVK/vkd3d), https-only, and--bypass-confirmsauto-allows.apply-command.ts— argv-only apply tool (no shell) for PHASE 4 filesystem steps, run viaBun.spawn.apply_command; Steam-client state (launch options, Proton-version selection) stays PRINTED for the user to apply in Steam — on the maintainer's targets Steam runs continuously and its config files can't be written safely from the CLI. No pretending to apply Steam state.Threat model
funfa runs unsandboxed bash composed by a model that ingests untrusted forum/wiki text → a prompt-injection RCE surface. Mitigations in this PR: the permission gate, the download allow-list + https-only, and argv-only
apply_command. Residual risk: the read-onlybashprobe tool is allowed freely; OS-level confinement (bubblewrap) is the next hardening step and is intentionally out of scope. Documented inagent/permission.ts.Tests
propose.test.ts(was AI-SDKMockLanguageModelV3-specific → nowbuildUserPromptunits + an opt-in live pi-session smoke gated onFUNFA_PI_SMOKE=1+ auth),provider.test.ts(pi auth:parseModelOverride/hasUsableAuth/resolveModelOverride), andprompt.test.ts(buildBasePrompt).permission.test.ts(gate blocks without consent / allows with consent / auto-allows under--bypass-confirms/ blocks off-allow-list + http downloads) andtools.test.ts(each custom tool'sexecute+ the Bun apply runner).compile.test.tsupdated: compiles without--bytecode, asserts the no-auth message + that bundled skill text parsed.Verification
bun run check-types✓,bun run check(ultracite) ✓cd apps/cli && bun test→ 81 pass, 1 skip (opt-in live smoke), 0 failbun run --filter cli compilesucceeds without--bytecode;funfa --helpexits 0; no-auth path prints the/loginerror.Auth-gated manual E2E (can't run in CI):
npx @earendil-works/pi-coding-agent→/login, thenfunfa "Assassin's Creed Brotherhood"(off-Linux: setFUNFA_FAKE_HARDWARE); craft a plan with a Proton-GE install to exercise the gate.Out of scope (unchanged from the plan)
Decky frontend +
SteamClientapply for launch-options/Proton-version; bubblewrap confinement ofapply_command; removing Effect-TS.