Skip to content

Latest commit

 

History

269 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hpatch

A Codex Responses router that gives agents verified atomic edits and direct script execution, without Code Mode wrapper ceremony.

hpatch-router sits between Codex and the Responses API. The model sees constrained functions.hpatch and free-form functions.shell. Successful calls still return native Codex carriers, so sandbox checks, permissions, command sessions, and the normal diff UI stay intact. The repository also exposes the reusable Go edit engine used by the router.

Goal Start here
Install and route Codex through hpatch Codex router
See what you get Features
Understand verified editing Why hpatch?
Run programs without Code Mode wrappers Why shell?
Inspect live usage Metrics
Use the engine without Codex Go library
Read contracts doc/spec/index.md

Features

User experience

  • Codex still owns sandboxing, permissions, process sessions, and the visible patch diff. The router translates; it does not silently commit workspace edits.
  • A human-readable dashboard lives at the router root, for example http://127.0.0.1:8080/. The same listener serves Responses, models, and /api/metrics.
  • A systemd user unit is the intended long-running setup. Passthrough mode forwards Responses traffic without installing hpatch or plugins.
  • Codex keeps seeing stream activity while a tool call is validated; the router withholds untranslated input until the complete payload is ready.
  • Optional --capture-output appends sanitized JSONL for later inspection. Outcome hooks can record each routed hpatch or recovery result.

Agent experience

  • functions.hpatch is a verified atomic edit language: copy a LINE:HASH row, emit the new text once, and let the router build the patch.
  • functions.shell sends the program body in its native syntax. Compact shebangs select the interpreter; Bash is the default.
  • Private shell commands stay inside Bash and POSIX programs: hread for verified rows, hgrep for text search, hsymbol for language-server lookup, and inspect_file for a structural outline with no source bodies.
  • Eligible programs can be retained, inspected, edited, and rerun through @shell/ references. Wholly stale-target rejections use functions.hpatch_recover instead of rewriting the whole script.
  • A Lark grammar constrains HPATCH syntax as the model writes it. Supported languages are validated before Codex applies the patch. Configured plugins can join the model-visible catalog.
  • HPATCH_DIAGNOSE=1 adds a free-form report_issue tool for agent-experience problems. See REQ-DIAGNOSE-001.
  • Subagent activity shows the requested role, model, reasoning effort, exact spawn and follow-up messages, and exact replies as commentary. These messages are visible in Codex but are removed before later model requests.

Token saving

  • The hpatch family avoids repeating old context. hpatch identifies a verified region and writes the replacement once. hread, hgrep, and hsymbol emit copyable LINE:HASH rows. inspect_file returns structure instead of file bodies.
  • functions.shell drops the JavaScript carrier, JSON argument object, and extra quoting layers that Code Mode exec_command requires.
  • Opt-in CTP/2 is a lossless encoding of eligible model-visible request strings and assistant text between the Hpatch-projected request and the provider. Repeats inside one string can become a local dictionary; tool outputs may instead point at earlier visible output lines in the same request. Newly emitted tool names and payloads stay native. Validated compaction requests stay native and skip CTP/2.
  • A Lark grammar constrains HPATCH generation so the model does not have to retry invalid syntax. A completed invalid script is still rejected atomically.

Performance

  • Opt-in --mentor-handoff temporarily sends eligible spawned gpt-5.6-luna and gpt-5.6-terra children as gpt-5.6-sol with high reasoning, then returns to the Codex-configured model. Only an AgentControl collab_spawn with subagent_kind: thread_spawn activates it; ordinary sessions and forks stay unchanged. See REQ-MENTOR-001.

Why hpatch?

A direct Code Mode edit makes the model repeat patch framing, old context, replacement text, and a JavaScript carrier. The router moves patch reconstruction out of model output:

flowchart LR
    subgraph output["Alternative model-output payloads"]
        H["hpatch path<br/>functions.hpatch + verified targets + replacement"]
        A["apply_patch baseline<br/>functions.exec + JavaScript carrier<br/>+ old context + replacement + patch framing"]
    end

    subgraph router["Router and Codex after model output"]
        B["Router reads the immutable<br/>workspace baseline"]
        C["Router generates the<br/>apply_patch envelope"]
        D["Codex applies the patch<br/>sandbox checks + normal diff"]
    end

    H --> B --> C --> D
    A --> D
Loading

The patch is not eliminated: the router generates it after inference.

For an 11-line function replacement, hpatch asks the model for this:

functions.hpatch
in parser.go
type 42:e217..52:d10b <<PATCH
func parse(input []byte) (Document, error) {
	tokens, err := tokenize(input)
	if err != nil {
		return Document{}, fmt.Errorf("tokenize: %w", err)
	}
	document, err := buildDocument(tokens)
	if err != nil {
		return Document{}, fmt.Errorf("build document: %w", err)
	}
	return document, nil
}
PATCH

Direct apply_patch in Code Mode repeats all 11 old lines, then writes the same 11 new lines plus patch framing and the JavaScript carrier. Hpatch writes the new function once and identifies the old region with two verified rows.

That smaller payload is only one benefit. Editing becomes a verified transaction: targets check an immutable invocation baseline, a bad command rejects the whole script, and supported language validation runs before Codex applies anything. Grammar is syntax only; missing files, stale rows, and conflicting edits still fail atomically.

See REQ-SCRIPT-001, REQ-SELECT-001, and REQ-OUTPUT-001. Authoritative agent workflow: contrib/codex/file-editing-instructions.md.

Why shell?

Native tools.exec_command is Codex's execution backend. Calling it from Code Mode makes the model generate a JavaScript program, a JSON argument object, a quoted command, and an output projection. functions.shell is an adapter to that same executor: the model sends the program body directly.

#!python3
print("hello")
Concern Code Mode tools.exec_command functions.shell
Model output JavaScript wrapper, argument object, quoted command, and output projection Exact script body
Quoting Program text can cross JavaScript, JSON, and shell quoting layers No outer heredoc or command-string wrapper
Interpreter Encoded in the command construction Compact shebang; Bash is the default
Standard input Arranged through the wrapper Remains available to the program
Correction The model must emit the program again Eligible programs can be retained, inspected, edited, and rerun
Execution policy Codex native executor The same Codex native executor, sandbox, permissions, and result

This is better for the harness because it removes syntax that exists only to reach the executor. It is not a claim that the underlying process runs faster.

A one-line Bash program with no shebang or directive and containing one external command is sent directly to the native executor, so a call such as rtk shadowtree test . remains that command. Composed scripts, private commands, and other interpreters use the generated shell <interpreter> <program> carrier.

shell can start PTY-backed, interactive, and long-running programs and forwards the native executor's complete result. If execution yields a session handle, use Codex's native session facilities; each shell call starts a new execution. See OpenAI's Codex prompting guide and REQ-SHELL-001.

Requirements

  • Go 1.26 or newer. Normal go install does not require a checkout.
  • Hpatch router mode requires Codex CLI with ChatGPT auth from codex login so each request carries a Bearer token and ChatGPT account header. Codex normally stores that file auth at ~/.codex/auth.json or $CODEX_HOME/auth.json. At startup, the router reads the adjacent config.toml only to determine whether model_instructions_file is configured.
  • Hpatch router mode resolves Node.js 24 or newer as node; passthrough mode does not load the plugin registry.
  • Private hgrep requires rg on the Codex executor's PATH.
  • Private hsymbol requires the resolver for the queried language on the Codex executor's PATH: gopls for Go, TypeScript 7 as tsc for JavaScript, TypeScript, and JSON, and pyright-langserver for Python .py and .pyi sources.
  • Private hread, hgrep, hsymbol, and inspect_file are evaluated inside Bash or POSIX shell programs. The separately installed, fixed shell helper must be on the Codex executor's trusted PATH.
  • The built-in shell uses the embedded mvdan/sh, including bash and sh shebangs; other selected interpreters must be available through the inherited PATH or a direct path.
  • Source builds that regenerate the embedded plugin with make install or go generate require Bun. make install additionally requires Make.

Install from a checkout

make install regenerates the embedded built-in plugin bundle and installs hpatch-router plus the fixed shell helper through go install:

make install

Installation and uninstallation never create, edit, or remove Codex configuration or instruction files. make uninstall removes only the installed hpatch-router and shell binaries.

Configured plugins

The mandatory builtin.shell implementation comes from plugins/shell.mjs and is embedded during generation; make install does not copy it into user configuration.

Configured plugins are direct regular .js or .mjs files in $XDG_CONFIG_HOME/hpatch/plugins or ~/.config/hpatch/plugins on Linux. The router loads them in lexical order into one immutable process snapshot. It does not discover workspace-local or remote plugins and does not hot-reload files. Restart hpatch-router after any plugin change. Invalid modules, duplicate identities, or an unusable built-in registry fail startup before the router listens. The module contract is REQ-PLUGIN-001.

Shell tool

Everyday functions.shell use is a free-form program. Compact shebang, interpreter selection, and session behavior are in Why shell? and REQ-SHELL-001.

Retain, inspect, and rerun

A retained result includes retained: true and a script_ref such as @shell/<call-id>. The artifact is scoped to the Codex thread, expires after one hour by default, and is not a workspace file. Its thread directory is removed when the router shuts down.

hread @shell/<call-id>

Copy an emitted LINE:HASH row into a complete hpatch script whose paths are all under @shell/. Do not mix retained and workspace paths in one script. Rerun the current retained body with:

#!script=@shell/<call-id>

Retained edits use router-owned storage rather than the workspace apply_patch carrier. See REQ-SHELL-001.

Private shell commands

Hread, hgrep, hsymbol, and inspect_file are recognized only by the Bash and POSIX shell evaluators:

hread parser.go 20:40
hgrep -e 'TranslateForHostAt' .
hsymbol refs internal/router/server.go 42:abcd Run 2
inspect_file internal/router/server.go | jq -c '.data.outline[]'

Use hread before editing; inspect_file lines are not HPATCH targets. Complete inputs and failure behavior: REQ-READ-001, REQ-GREP-001, REQ-SYMBOL-001, and REQ-INSPECT-001.

Codex router

In hpatch mode, the router validates authentication and turn metadata, constructs the plugin registry, and installs standalone functions.hpatch and functions.shell. Configured contributions marked model-visible join that catalog. Hread, hgrep, hsymbol, and inspect_file remain authenticated shell-internal commands.

Defaults:

Setting Default
Mode hpatch (--mode); passthrough forwards Responses traffic without loading the tool registry
Model protocol native (--model-protocol); ctp2 is opt-in and Hpatch-only
Mentor Handoff Disabled (--mentor-handoff); Hpatch-only
Provider base URL https://chatgpt.com/backend-api/codex (--provider-base-url)
Listen 127.0.0.1:8080 (--listen)
Upstream response-start timeout 10m (--timeout)
Upstream stream idle timeout 4m of inactivity between bytes (--stream-idle-timeout)
Auth Codex-managed ChatGPT credentials, typically ~/.codex/auth.json or $CODEX_HOME/auth.json; Codex owns login and refresh
Shell runtime directory $HPATCH_RUNTIME_DIR, or the operating-system temporary directory when unset; router and executor must resolve the same absolute path
Capture output Disabled; --capture-output PATH appends sanitized JSONL
Hooks $XDG_CONFIG_HOME/hpatch or ~/.config/hpatch
Endpoints GET / dashboard, POST /v1/responses, GET /v1/models, and GET /api/metrics, all on one listener

--provider-base-url changes where the router sends Codex-managed credentials and Responses traffic. Use it only with a trusted endpoint.

Use --mode passthrough to forward Responses traffic without installing hpatch, shell, private commands, or rejected-script recovery. Capture remains available because it observes the transport.

Use --model-protocol ctp2 for the compact provider representation. See REQ-CTP-001. Use --mentor-handoff only for the spawned-subagent schedule above. See REQ-MENTOR-001.

In hpatch mode, run the router as the same login user as Codex so it can open the absolute workspace paths Codex sends. Codex attaches its managed credentials to each request. A user systemd unit is the intended long-running setup.

Install the binary

go install github.com/yusing/hpatch/cmd/hpatch-router@latest \
  github.com/yusing/hpatch/cmd/shell@latest

The binaries are installed under $GOBIN, or under $(go env GOPATH)/bin when GOBIN is unset. Ensure that directory is on the router and Codex executor PATH.

Install and start the unit

Install the published user-unit template:

mkdir -p ~/.config/systemd/user
curl -fsSL https://raw.githubusercontent.com/yusing/hpatch/main/contrib/systemd/hpatch-router.service \
  -o ~/.config/systemd/user/hpatch-router.service
systemctl --user daemon-reload
systemctl --user enable --now hpatch-router.service
systemctl --user status hpatch-router.service

Optional: keep the service after logout:

loginctl enable-linger "$USER"

One-shot without the unit (still uses the installed binary):

hpatch-router --listen 127.0.0.1:8080

If auth lives outside ~/.codex, or the binary is not in ~/go/bin, use a drop-in:

systemctl --user edit hpatch-router.service
[Service]
Environment=CODEX_HOME=%h/.codex
ExecStart=
ExecStart=%h/.local/bin/hpatch-router --listen 127.0.0.1:9090

Then systemctl --user daemon-reload && systemctl --user restart hpatch-router.service.

Point Codex at the router

Add a Responses provider in ~/.codex/config.toml:

[model_providers.hpatch]
name = "hpatch"
base_url = "http://127.0.0.1:8080/v1"
wire_api = "responses"
requires_openai_auth = true

Make it the default for the whole config:

model_provider = "hpatch"

Or select it for one invocation with a profile. Put model_provider = "hpatch" in ~/.codex/hpatch.config.toml (the provider block can live in the base config or in that file), then:

codex --profile hpatch

You can also overlay the same setting without a profile file:

codex -c 'model_provider="hpatch"'

Hpatch mode requires valid turn metadata, but its wire workspaces member is optional. No usable directory does not block the turn, never falls back to router cwd, and permits only absolute hpatch operands.

Useful checks:

systemctl --user status hpatch-router.service
journalctl --user -u hpatch-router.service -f
curl -sS http://127.0.0.1:8080/api/metrics
curl -sS http://127.0.0.1:8080/v1/models

Codex model instructions

contrib/codex/file-editing-instructions.md is the single source for CTP/2 representation rules and all durable HPATCH, shell, hread, hgrep, hsymbol, and inspect_file workflow guidance. The router applies it in memory and never reads or writes the configured instruction file.

The carrier is a nonempty top-level instructions string, or the first textual developer message when that field is missing, null, or empty. A recognized stock Codex file-editing section is replaced. A customized prompt without that section receives the hpatch section only when model_instructions_file is set in Codex's config.toml; without that setting, a missing section fails before forwarding. Validated compaction requests skip this rewrite. The router snapshots the setting at startup; restart it after adding or removing the key. See REQ-GUIDE-001.

Go library

The module path is github.com/yusing/hpatch. The root package exposes workspace evaluation, application, reporting, and host translation APIs. Root-scoped application APIs use a caller-authorized *os.Root and root-relative cwd. Host translation uses TranslateForHostAt, retains cleaned host path identities for Codex to authorize, and never uses router cwd as a fallback. See REQ-FILE-001 and CTR-TRANSLATE-001.

Metrics

Open the router root URL, such as http://127.0.0.1:8080/, for the dashboard. GET /api/metrics returns the process-lifetime capturer snapshot. Provider usage is authoritative for model consumption. Metrics are auxiliary and cannot replace a successful edit, command result, or rejection diagnostic.

Use --capture-output PATH when durable evidence is needed. The file contains sanitized JSONL: payload sizes, statuses, provider usage, tool identities, and bounded outcome kinds. Raw prompts, scripts, patches, credentials, and full diagnostics are discarded after measurement.

The executable benchmark requires Docker Compose, Codex authentication, and the task's local source under benchmarks/repos/. The default task, etcd-range-stream, needs a local etcd checkout. Read the benchmark methodology before running bash benchmarks/bench.sh. Capture, snapshot shape, and comparison rules are REQ-METRICS-001 and REQ-BENCH-001.

Documentation

Doc Contents
doc/brief.md Product brief and scope
doc/spec/index.md Specification inventory; each listed file owns one requirement
doc/architecture/index.md Ownership-contract inventory
doc/benchmarks.md Benchmark operation and interpretation
doc/codex-router-e2e.md Codex-facing end-to-end procedure
contrib/systemd/hpatch-router.service User service template
contrib/codex/file-editing-instructions.md Persistent CTP/2, edit, shell, read, search, and inspection guidance
AGENTS.md Agent workflow and repository navigation

Development

go generate ./internal/router/toolplugin
bun test ./internal/router/toolplugin/tests
go test ./...
go vet ./...
make install

Focused checks are go test . for the engine, go test ./internal/router for routing and plugins, and go test ./cmd/hpatch-router ./cmd/shell for the process entry points.

Starting the router in hpatch mode with HPATCH_DIAGNOSE=1 adds the model-visible report_issue tool. Configure hooks.diagnose in $XDG_CONFIG_HOME/hpatch/settings.json or ~/.config/hpatch/settings.json. See REQ-DIAGNOSE-001.

About

A Codex Responses router that gives agents verified atomic edits and direct script execution, without Code Mode wrapper ceremony.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages