Skip to content

feat(agent): provider-neutral loop + codex app-server transport - #25

Merged
gustavohariel merged 1 commit into
mainfrom
worktree-codex-app-server-convergence
May 31, 2026
Merged

gustavohariel merged 1 commit into
mainfrom
worktree-codex-app-server-convergence

Conversation

@gustavohariel

Copy link
Copy Markdown
Owner

Why

The vision for funfa is one loop that behaves identically across providers. Before this PR the four SKILL.md files were written against funfa's specific tool harness (all five tool names leaked into the bodies), so the codex path needed a big override preamble — and the two prompts could silently drift. This converges them.

Part A — provider-neutral loop + thin bindings

  • All four SKILL.md files rewritten in capability verbs (no tool names); funfa/SKILL.md's ## Tools section removed.
  • New apps/cli/src/agent/prompt.ts: LOOP_BODY, API_BINDING, CODEX_BINDING, and one buildLoopPrompt({ binding, inlineSkills, skills }) that assembles both providers' prompts.
  • propose.ts rewired to it (deleted buildSystemPrompt/buildSkillsPrompt).
  • prompt.test.ts drift guard: asserts the loop body is byte-identical (and appears exactly once) across the API and codex prompts, so they can't silently diverge.

Editing the loop is now a one-file markdown edit for every provider.

Part B — codex exec → codex app-server

  • Pinned the real v2 protocol from codex app-server generate-json-schema on the local 0.135.0 binary (curated reference under apps/cli/vendor/codex-app-server-schema/). Notable 0.135.0 facts: system prompt rides thread/start.developerInstructions (plain string, not collaborationMode.settings.developer_instructions); sandbox is the bare string "read-only"; the JSON-RPC envelope omits jsonrpc; the final answer streams via item/agentMessage/delta → turn/completed and is constrained by turn/start.outputSchema.
  • New codex-app-server.ts (minimal NDJSON JSON-RPC client with an injectable transport) + codex-protocol.ts (hand-authored minimal types + MIN_CODEX_VERSION).
  • codex-cli.ts drives the app-server flow by default (injecting the same provider-neutral loop via developerInstructions), gated on MIN_CODEX_VERSION. FUNFA_CODEX_TRANSPORT=exec falls back to the legacy codex exec --output-schema path. The consent gate / on-disk write / render (applySubmitPlan) are unchanged.
  • codex-app-server.test.ts (fake-transport unit tests) + updated codex-cli.test.ts.

Honest scope note

app-server's approval callbacks are about codex's own shell/file actions. funfa runs codex read-only (approvalPolicy: never), so there's nothing to approve mid-loop — funfa's whole-plan "Accept this plan?" gate stays post-hoc. The real win from app-server is the clean developerInstructions prompt seam + preserved structured output + streaming. The transport is officially experimental, hence the version gate and the exec escape hatch. (Approach mirrors pingdotgg/t3code, which drives codex app-server the same way.)

Verification

  • bun run check-types: clean · bun run check (Ultracite): clean · bun test: 90 pass, 0 fail (incl. compile smoke + new prompt-drift and app-server-client tests).
  • app-server e2e (FUNFA_FAKE_HARDWARE AMD RX 7800 XT): real structured plan for appid 48190, hardware-filtered, codex-web-sourced, consent gate fired, written to disk (exit 0).
  • exec e2e: same flow over the escape hatch (exit 0).

🤖 Generated with Claude Code

Converge the two AI-provider paths so the funfa loop lives once, in
capability-verb form, with provider differences isolated to thin bindings.

Part A — provider-neutral loop:
- Rewrite all four SKILL.md files in capability verbs (no tool names);
  drop funfa's `## Tools` section.
- Add agent/prompt.ts: LOOP_BODY, API_BINDING, CODEX_BINDING, and one
  buildLoopPrompt({binding, inlineSkills, skills}) assembling both paths.
- Wire propose.ts to it (delete buildSystemPrompt/buildSkillsPrompt).
- prompt.test.ts drift guard: loop body byte-identical across providers.

Part B — codex exec -> codex app-server:
- Pin the v2 protocol from `codex app-server generate-json-schema` on
  0.135.0 (curated under apps/cli/vendor/). System prompt rides
  thread/start.developerInstructions; turn/start.outputSchema constrains
  the answer; envelope omits jsonrpc; output streams via
  item/agentMessage/delta -> turn/completed.
- Add codex-app-server.ts (minimal NDJSON JSON-RPC client, injectable
  transport) + codex-protocol.ts (hand-authored types + MIN_CODEX_VERSION).
- codex-cli.ts drives the app-server flow by default with a
  MIN_CODEX_VERSION gate; FUNFA_CODEX_TRANSPORT=exec falls back to the
  legacy path. Consent gate / write / render unchanged.
- codex-app-server.test.ts (fake transport) + updated codex-cli.test.ts.

Both transports verified e2e (FUNFA_FAKE_HARDWARE). 90 tests +
check-types + ultracite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@gustavohariel
gustavohariel merged commit 137740a into main May 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant